如何命名一个 good/meaningful 类型?
How to name a good/meaningful type?
Device_Manager.h
typedef enum
{
DNM = 0x2A,
}TYPE_e;
typedef struct DEVICE_s* p_DEVICE;
typedef p_DEVICE(*FUNC)(char* name, TYPE_e type, uint32_t ip, uint16_t method, uint16_t groupRule);
p_DEVICE DeviceManager_New(void);
p_DEVICE DeviceManager_Ctor(char* name, TYPE_e type, uint32_t ip, uint16_t method, uint16_t groupRule);
p_DEVICE DeviceManager_Dtor(p_DEVICE element);
Device_Manager.c
struct DEVICE_s
{
uint32_t IP;
TYPE_e Type;
uint16_t Method;
uint16_t GroupRule;
char Name[40];
FUNC fp_Ctor, fp_Dtor; //this line needs modification
}DeviceSet[32],DeviceTemp;
p_DEVICE DeviceManager_InitObject(p_DEVICE* self)
{
(*self) = DeviceManager_New();
(*self)->IP = 0;
(*self)->Type = 0;
(*self)->Method = 0;
(*self)->GroupRule = 0;
memset((*self)->Name, 0, NAME_SIZE);
(*self)->fp_Ctor = DeviceManager_Ctor;
(*self)->fp_Dtor = DeviceManager_Dtor; // warning: assign to wrong type
return (*self);
}
p_DEVICE DeviceManager_New(void)
{
return &DeviceTemp;
}
p_DEVICE DeviceManager_Ctor(char* name, TYPE_e type, uint32_t ip, uint16_t method, uint16_t groupRule)
{
memcpy(DeviceTemp.Name, name, sizeof(name));
DeviceTemp.Type = type;
DeviceTemp.IP = ip;
DeviceTemp.Method = method;
DeviceTemp.GroupRule = groupRule;
return &DeviceTemp;
}
p_DEVICE DeviceManager_Dtor(p_DEVICE element)
{
element->IP = 0;
element->Type = 0;
element->Method = 0;
element->GroupRule = 0;
memset(element->Name, 0, NAME_SIZE);
return element;
}
这是我第一次实现封装概念,遇到了一些问题。
在头文件中,我使用 typedef 将类型 "FUNC" 定义为函数指针。
我觉得这个名字"FUNC"不够明确,因为这种命名时尚会导致:
struct DEVICE_s
{
uint32_t IP;
TYPE_e Type;
uint16_t Method;
uint16_t GroupRule;
char Name[40];
FUNC1 fp_Ctor; //not clear
FUNC2 fp_Dtor; //not clear
}DeviceSet[32],DeviceTemp;
fp_Ctor 和 fp_Dtor 都是相同的类型(函数指针)并且参数的数量不同。
我总是在命名类型上挣扎。能否提供一些命名类型的建议?
这有点主观,但我会从放弃将指针隐藏在 typedef 后面的风格开始,并在其上涂抹某种 "Hungarian notation" 的方式。很多 C 程序员都会同意这一点。
所以第一个建议是
typedef struct DEVICE_s DEVICE_s;
然后根据 DEVICE_s*
定义您的不透明界面。它不仅更易于阅读,而且可以过滤掉混淆,例如调用者试图将 p_DEVICE*
传递给用户定义的函数等,因为它们没有意识到它们已经有一个指针。 (Win32 API 严重受此问题困扰。)
那么你的构造函数就变成了
DEVICE_s* DeviceManager_Ctor ( ...
并且所有成员函数都将采用 DEVICE_s*
参数而不是 p_DEVICE
值。调用者必须声明指针而不是对象,让他们清楚他们有一个指向不完整类型的指针,他们 can/should 没有玩什么。
接下来你也可以删除隐藏在函数指针中的指针。这不是什么大问题,但保持一致很好:
typedef DEVICE_s* DeviceManager_Ctor_t ( ...
您的函数指针定义将变为:
DeviceManager_Ctor_t* Ctor;
您可以删除 "fp" 命名,因为该类型已经很明显是函数指针。
作为旁注,我建议避免使用 obj.member
表示法模仿 C++ 成员函数。因为在 C 中,缺少 this
指针,您最终会得到 obj.member(&obj, ...)
,这有点多余。
宁愿接受 C 就是它的样子,并将成员函数调用为 DeviceManager_Ctor(obj);
,其中 obj
声明为 DEVICE_s* obj;
。可读 OO 代码的关键是对属于 "class" 的所有函数使用一致的源代码前缀,就像您已经这样做的那样:DeviceManager_
.
Linux kernel coding style 建议谨慎使用 typedef。 "Methods" 或函数指针通常分组到一个操作结构中。例如:
// DeviceManager.h
//
struct device_operations;
struct device {
const char *name;
const struct device_operations *ops;
};
struct device_operations {
void (*ctor)(struct device *d, const char *name);
void (*dtor)(struct device *d);
};
struct device *DeviceManager_new(const char *name);
// DeviceManager.c
//
#include <stdlib.h>
static void DeviceManager_ctor(struct device *d, const char *name);
static void DeviceManager_dtor(struct device *d);
static struct device_operations DeviceManager_ops = {
.ctor = DeviceManager_ctor,
.dtor = DeviceManager_dtor,
};
struct device *DeviceManager_new(const char *name)
{
struct device *d = malloc(sizeof(*d));
if (!d)
return NULL;
d->ops = &DeviceManager_ops;
d->ops->ctor(d, name);
return d;
}
static void DeviceManager_ctor(struct device *d, const char *name) { /* ... */ }
static void DeviceManager_dtor(struct device *d) { /* ... */ }
Device_Manager.h
typedef enum
{
DNM = 0x2A,
}TYPE_e;
typedef struct DEVICE_s* p_DEVICE;
typedef p_DEVICE(*FUNC)(char* name, TYPE_e type, uint32_t ip, uint16_t method, uint16_t groupRule);
p_DEVICE DeviceManager_New(void);
p_DEVICE DeviceManager_Ctor(char* name, TYPE_e type, uint32_t ip, uint16_t method, uint16_t groupRule);
p_DEVICE DeviceManager_Dtor(p_DEVICE element);
Device_Manager.c
struct DEVICE_s
{
uint32_t IP;
TYPE_e Type;
uint16_t Method;
uint16_t GroupRule;
char Name[40];
FUNC fp_Ctor, fp_Dtor; //this line needs modification
}DeviceSet[32],DeviceTemp;
p_DEVICE DeviceManager_InitObject(p_DEVICE* self)
{
(*self) = DeviceManager_New();
(*self)->IP = 0;
(*self)->Type = 0;
(*self)->Method = 0;
(*self)->GroupRule = 0;
memset((*self)->Name, 0, NAME_SIZE);
(*self)->fp_Ctor = DeviceManager_Ctor;
(*self)->fp_Dtor = DeviceManager_Dtor; // warning: assign to wrong type
return (*self);
}
p_DEVICE DeviceManager_New(void)
{
return &DeviceTemp;
}
p_DEVICE DeviceManager_Ctor(char* name, TYPE_e type, uint32_t ip, uint16_t method, uint16_t groupRule)
{
memcpy(DeviceTemp.Name, name, sizeof(name));
DeviceTemp.Type = type;
DeviceTemp.IP = ip;
DeviceTemp.Method = method;
DeviceTemp.GroupRule = groupRule;
return &DeviceTemp;
}
p_DEVICE DeviceManager_Dtor(p_DEVICE element)
{
element->IP = 0;
element->Type = 0;
element->Method = 0;
element->GroupRule = 0;
memset(element->Name, 0, NAME_SIZE);
return element;
}
这是我第一次实现封装概念,遇到了一些问题。
在头文件中,我使用 typedef 将类型 "FUNC" 定义为函数指针。
我觉得这个名字"FUNC"不够明确,因为这种命名时尚会导致:
struct DEVICE_s
{
uint32_t IP;
TYPE_e Type;
uint16_t Method;
uint16_t GroupRule;
char Name[40];
FUNC1 fp_Ctor; //not clear
FUNC2 fp_Dtor; //not clear
}DeviceSet[32],DeviceTemp;
fp_Ctor 和 fp_Dtor 都是相同的类型(函数指针)并且参数的数量不同。
我总是在命名类型上挣扎。能否提供一些命名类型的建议?
这有点主观,但我会从放弃将指针隐藏在 typedef 后面的风格开始,并在其上涂抹某种 "Hungarian notation" 的方式。很多 C 程序员都会同意这一点。
所以第一个建议是
typedef struct DEVICE_s DEVICE_s;
然后根据 DEVICE_s*
定义您的不透明界面。它不仅更易于阅读,而且可以过滤掉混淆,例如调用者试图将 p_DEVICE*
传递给用户定义的函数等,因为它们没有意识到它们已经有一个指针。 (Win32 API 严重受此问题困扰。)
那么你的构造函数就变成了
DEVICE_s* DeviceManager_Ctor ( ...
并且所有成员函数都将采用 DEVICE_s*
参数而不是 p_DEVICE
值。调用者必须声明指针而不是对象,让他们清楚他们有一个指向不完整类型的指针,他们 can/should 没有玩什么。
接下来你也可以删除隐藏在函数指针中的指针。这不是什么大问题,但保持一致很好:
typedef DEVICE_s* DeviceManager_Ctor_t ( ...
您的函数指针定义将变为:
DeviceManager_Ctor_t* Ctor;
您可以删除 "fp" 命名,因为该类型已经很明显是函数指针。
作为旁注,我建议避免使用 obj.member
表示法模仿 C++ 成员函数。因为在 C 中,缺少 this
指针,您最终会得到 obj.member(&obj, ...)
,这有点多余。
宁愿接受 C 就是它的样子,并将成员函数调用为 DeviceManager_Ctor(obj);
,其中 obj
声明为 DEVICE_s* obj;
。可读 OO 代码的关键是对属于 "class" 的所有函数使用一致的源代码前缀,就像您已经这样做的那样:DeviceManager_
.
Linux kernel coding style 建议谨慎使用 typedef。 "Methods" 或函数指针通常分组到一个操作结构中。例如:
// DeviceManager.h
//
struct device_operations;
struct device {
const char *name;
const struct device_operations *ops;
};
struct device_operations {
void (*ctor)(struct device *d, const char *name);
void (*dtor)(struct device *d);
};
struct device *DeviceManager_new(const char *name);
// DeviceManager.c
//
#include <stdlib.h>
static void DeviceManager_ctor(struct device *d, const char *name);
static void DeviceManager_dtor(struct device *d);
static struct device_operations DeviceManager_ops = {
.ctor = DeviceManager_ctor,
.dtor = DeviceManager_dtor,
};
struct device *DeviceManager_new(const char *name)
{
struct device *d = malloc(sizeof(*d));
if (!d)
return NULL;
d->ops = &DeviceManager_ops;
d->ops->ctor(d, name);
return d;
}
static void DeviceManager_ctor(struct device *d, const char *name) { /* ... */ }
static void DeviceManager_dtor(struct device *d) { /* ... */ }