“模仿”的C策略是什么?一个 C++模板 ?
在阅读了 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)
,以便生成整数、双精度等列表背后的代码。
通过遵循此策略,我必须将所有“模板”声明保留在两个文件中,同时,每当需要数据结构时,我都需要包含 templates.h
。这是一个非常“集中”的解决方案。
您是否知道其他策略来“模仿”(在某些时候)C++ 中的模板?您是否知道改进此策略的方法,以便以更加分散的方式保持事物,这样我就不需要这两个文件:templates.c
和templates.h?
After reading some examples on stackoverflow, and following some of the answers for my previous questions (1), I've eventually come with a "strategy" for this.
I've come to this:
1) Have a declare section in the .h
file. Here I will define the data-structure, and the accesing interface. Eg.:
/**
* 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) Wrap the functions in the interface inside 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) Implement the functions:
/**
* 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...)
In order to use those constructs, I have to create two files (let's call them templates.c
and templates.h
) .
In templates.h
I will have to NM_TEMPLATE_DECLARE_LIST(int)
, NM_TEMPLATE_DECLARE_LIST(double)
, while in templates.c
I will need to NM_TEMPLATE_DEFINE_LIST(int)
, NM_TEMPLATE_DEFINE_LIST(double)
, in order to have the code behind a list of ints, doubles and so on, generated.
By following this strategy I will have to keep all my "template" declarations in two files, and in the same time, I will need to include templates.h
whenever I need the data structures. It's a very "centralized" solution.
Do you know other strategy in order to "imitate" (at some point) templates in C++ ? Do you know a way to improve this strategy, in order to keep things in more decentralized manner, so that I won't need the two files: templates.c
and templates.h
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我可能不应该承认这样做,但是当我过去在 C 领域需要“模板化”容器时,我将“模板化队列类”编写为一对特殊文件,如下所示:
File MyQueue.include_h:
File MyQueue .include_c:
然后,每当我想“实例化”我的队列“模板”以使用特定的项目类型时,我都会将类似的内容放入实际的 .c 和 .h 文件中:
在我的真实 .h 文件之一中:
在我的一个真实的 .c 文件中(无论是哪一个):
.... 很快,C 预处理器充当了穷人的模板扩展器,而不需要制定整个“模板定义”一个巨大的#define 语句。
I probably shouldn't admit to doing this, but when I needed "templatized" containers in C land in the past, I wrote the 'templatized queue class' as a pair of special files, like this:
File MyQueue.include_h:
File MyQueue.include_c:
Then, whenever I wanted to "instantiate" my Queue "template" to use a particular item-type, I'd put something like this into an actual .c and .h file:
In one of my real .h files:
In one of my real .c files (it doesn't matter which one):
.... and presto, the C preprocessor acts as a poor man's template-expander, without requiring the entire "template definition" to be made out of a giant #define statement.
您的示例只是模板的许多可能用途之一 - 生成通用数据结构。这个例子不需要任何使模板变得强大的推理;要求能够创建通用数据结构的东西与要求相当于 C++ 模板的东西实际上并不是同一个问题。
使用的一些实现技术可能会提供一些类型推断功能,但它们仍然比 C++ 模板要弱得多且可移植性较差。对于容器的具体示例,我不会打扰 - 只需创建一个包含 void* 数据的列表,然后使用 malloc 和 free 来创建数据,或者为列表提供一对函数指针来创建和销毁值。您还可以仅依靠客户端来管理数据,而不是将值作为列表的成员。如果要保存间接寻址,请使用可变长度数组作为数据成员。由于 C 不像 C++ 那样类型安全,因此拥有 void* 数据不是问题。
您可以使用宏进行一些复杂的代码生成,但还有其他工具可以生成代码。就我个人而言,我喜欢使用 XSLT 来生成代码,但是这样您的构建过程就会有一个完全不类似于 C 的部分。
Your example is only one of the many possible uses of templates - generating a generic data structure. This example doesn't need any of the inference which makes templates powerful; asking for something which lets you create generic data structures is not really the same question as asking for something equivalent to C++ templates.
Some of the implementation techniques used for
<tgmath.h>
might give some type inference capabilities, but they is still much weaker and less portable than C++ templates.For the specific example of containers, I wouldn't bother - just create a list with void* data in it, and either use malloc and free to create the data, or give the list have a pair of function pointers to create and destroy values. You can also just rely on the client to manage the data, rather than having the value as a member of the list. If you want to save the indirection, use a variable length array as the data member. As C isn't as type-safe as C++, having void* data isn't an issue.
You can do some sophisticated code generation with macros, but there are also other tools to generate code. Personally I like using XSLT for code generation, but then you have a completely non-C-like part of your build process.
省去一些麻烦并使用现有的
queue(3)
一组宏 - 经过尝试和测试,用于内核源代码等。Save yourself some trouble and use existing
queue(3)
set of macros - tried and tested, used in kernel sources, etc.