如何命名好/有意义的类型?

时间:2019-01-23 10:06:41

标签: c encapsulation naming

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都是相同的类型(函数指针),并且参数的编号不同。

我总是在命名类型上挣扎。可以提供一些有关命名类型的建议吗?

2 个答案:

答案 0 :(得分:4)

这有点主观,但我首先将隐藏指针的样式放在typedef后面,并在其上涂抹某种“匈牙利符号”。很多C程序员都会同意。

所以第一个建议是与

一起使用
typedef struct DEVICE_s DEVICE_s;

然后根据DEVICE_s*定义不透明的界面。不仅易于阅读,而且可以过滤掉混淆,就像调用者试图将p_DEVICE*传递给用户定义的函数等一样,因为它们没有意识到它们已经具有指针了。 (Win32 API严重受此问题困扰。)

然后您的构造函数变为

DEVICE_s* DeviceManager_Ctor ( ...

所有成员函数将采用DEVICE_s*参数,而不是按值p_DEVICE。调用者将不得不声明指针而不是对象,从而使他们清楚它们具有指向不完整类型的指针,并且它们无法/不应使用。

接下来,您也可以将指针隐藏在函数指针中。这不是什么大问题,但是保持一致很高兴:

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代码的关键是对属于“类”的所有功能使用一致的源代码前缀,就像您已经做的那样:DeviceManager_

答案 1 :(得分:-1)

Linux kernel coding style建议谨慎使用typedef。通常将“方法”或函数指针分组为一个操作结构。例如:

// 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) { /* ... */ }