什么是“模仿”C ++模板的C策略?

时间:2010-06-12 21:04:25

标签: c++ c templates macros c-preprocessor

在阅读了有关stackoverflow的一些示例之后,并根据我之前的问题(1)的一些答案,我最终为此提出了“策略”。

我来到这里:

1).h文件中设置声明部分。在这里,我将定义数据结构和访问接口。例如:

/**
 * LIST DECLARATION. (DOUBLE LINKED LIST)
 */
#define NM_TEMPLATE_DECLARE_LIST(type) \
typedef struct nm_list_elem_##type##_s { \
    type data; \
    struct nm_list_elem_##type##_s *next; \
    struct nm_list_elem_##type##_s *prev; \
} nm_list_elem_##type ; \
typedef struct nm_list_##type##_s { \
    unsigned int size; \
    nm_list_elem_##type *head; \
    nm_list_elem_##type *tail; \
    int (*cmp)(const type e1, const type e2); \
} nm_list_##type ; \
\
nm_list_##type *nm_list_new_##type##_(int (*cmp)(const type e1, \
    const type e2)); \
\
(...other functions ...)

2)将功能包装在MACROS内的界面中:

/**
 * LIST INTERFACE
 */
#define nm_list(type) \
    nm_list_##type

#define nm_list_elem(type) \
    nm_list_elem_##type

#define nm_list_new(type,cmp) \
    nm_list_new_##type##_(cmp)

#define nm_list_delete(type, list, dst) \
    nm_list_delete_##type##_(list, dst)

#define nm_list_ins_next(type,list, elem, data) \
    nm_list_ins_next_##type##_(list, elem, data)

(...others...)

3)实施功能:

/**
 * LIST FUNCTION DEFINITIONS
 */
#define NM_TEMPLATE_DEFINE_LIST(type) \
nm_list_##type *nm_list_new_##type##_(int (*cmp)(const type e1, \
    const type e2)) \
{\
    nm_list_##type *list = NULL; \
    list = nm_alloc(sizeof(*list)); \
    list->size = 0; \
    list->head = NULL; \
    list->tail = NULL; \
    list->cmp = cmp; \
}\
void nm_list_delete_##type##_(nm_list_##type *list, \
    void (*destructor)(nm_list_elem_##type elem)) \
{ \
    type data; \
    while(nm_list_size(list)){ \
        data = nm_list_rem_##type(list, tail); \
        if(destructor){ \
            destructor(data); \
        } \
    } \
    nm_free(list); \
} \
(...others...)

为了使用这些结构,我必须创建两个文件(让我们称之为templates.ctemplates.h)。

templates.h我必须NM_TEMPLATE_DECLARE_LIST(int)NM_TEMPLATE_DECLARE_LIST(double),而在templates.c我将需要NM_TEMPLATE_DEFINE_LIST(int)NM_TEMPLATE_DEFINE_LIST(double),按顺序生成一个int,double等列表后面的代码。

通过遵循这个策略,我必须将所有“模板”声明保存在两个文件中,同时,每当我需要数据结构时,我都需要包含templates.h。这是一个非常“集中”的解决方案。

你知道其他策略是为了“模仿”(在某些时候)C ++中的模板吗?您是否知道改进此策略的方法,以便以更分散的方式保持事物,这样我就不需要这两个文件:templates.ctemplates.h

3 个答案:

答案 0 :(得分:2)

我可能不应该承认这样做,但是当我过去在C盘中需要“模板化”容器时,我将“模板化队列类”写成一对特殊文件,如下所示:

文件MyQueue.include_h:

/** NOTE: THIS IS NOT a REAL .h FILE, it only looks like one!  Don't #include it! */
struct MyQueueClass
{
   void init_queue(MyQueueClass * q);
   void push_back(MyQueueClass * q, MyQueueClassItem * item);

   [....All the other standard queue header declarations would go here....]

   MyQueueClassItem * _head;
   MyQueueClassItem * _tail;
   int _size;
};

文件MyQueue.include_c:

/** NOTE: THIS IS NOT A REAL .c FILE, it only looks like one! Don't compile directly! */
void init_queue(MyQueueClass * q)
{
   q->_size = 0;
   q->_head = q->_tail = NULL;
}

void push_back(MyQueueClass * q, MyQueueClassItem * item)
{
   if (q->_head == NULL) q->_head = q->_tail = item;
   else
   {
      q->_tail->_next = item;
      item->_prev = q->_tail;
      q->_tail = item;
   }
   q->_size++;
}

[....All the other standard queue function bodies would go here....]

然后,每当我想“实例化”我的队列“模板”以使用特定的项目类型时,我就会把这样的东西放到实际的.c和.h文件中:

在我的一个真正的.h文件中:

#define MyQueueClass struct SomeSpecificQueueType
#define MyQueueClassItem struct SomeSpecificQueueTypeItem
# include "MyQueue.include_h"
#undef MyQueueClassItem
#undef MyQueueClass

在我的一个真正的.c文件中(哪个无关紧要):

#define MyQueueClass struct SomeSpecificQueueType
#define MyQueueClassItem struct SomeSpecificQueueTypeItem
# include "MyQueue.include_c"
#undef MyQueueClass
#undef MyQueueClassItem

....而且,预处理器可以充当穷人的模板扩展器,而不需要从一个巨大的#define语句中创建整个“模板定义”。

答案 1 :(得分:1)

为自己省去一些麻烦并使用现有的queue(3)组宏 - 经过试验和测试,在内核源代码中使用等。

答案 2 :(得分:1)

您的示例只是模板的许多可能用途之一 - 生成通用数据结构。这个例子不需要任何使模板功能强大的推理;要求创建通用数据结构的东西与要求等同于C ++模板的东西并不是同一个问题。

用于<tgmath.h>的一些实现技术可能会提供一些类型推断功能,但它们仍然比C ++模板更弱,更不便携。

对于容器的具体示例,我不打扰 - 只需在其中创建一个包含void *数据的列表,并使用malloc和free来创建数据,或者给列表提供一对函数指针来创建并摧毁价值观。您也可以依靠客户端来管理数据,而不是将值作为列表的成员。如果要保存间接,请使用可变长度数组作为数据成员。由于C不像C ++那样类型安全,因此拥有void *数据不是问题。

您可以使用宏进行复杂的代码生成,但也有其他工具可以生成代码。我个人喜欢使用XSLT代码生成,但是你的构建过程中有一个完全不像C的部分。