在阅读了有关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.c
和templates.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.c
和templates.h
?
答案 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的部分。