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

发布于 2024-09-06 02:54:12 字数 2686 浏览 3 评论 0原文

在阅读了 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) ,以便生成整数、双精度等列表背后的代码。

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

您是否知道其他策略来“模仿”(在某些时候)C++ 中的模板?您是否知道改进此策略的方法,以便以更加分散的方式保持事物,这样我就不需要这两个文件:templates.ctemplates.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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

听风念你 2024-09-13 02:54:12

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

File 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;
};

File 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

.... 很快,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:

/** 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;
};

File 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....]

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:

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

In one of my real .c files (it doesn't matter which one):

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

.... 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.

情深已缘浅 2024-09-13 02:54:12

您的示例只是模板的许多可能用途之一 - 生成通用数据结构。这个例子不需要任何使模板变得强大的推理;要求能够创建通用数据结构的东西与要求相当于 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.

热风软妹 2024-09-13 02:54:12

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

Save yourself some trouble and use existing queue(3) set of macros - tried and tested, used in kernel sources, etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文