我在C++之前解决了这个问题,然后C++获得了模板,我仍然有 有代码。
您无法用宏 完全限定为头文件的方式定义真正通用的类型安全容器模板。标准预处理器 不提供“推送”和“弹出”您将要求的宏分配的方式,以便通过嵌套和顺序的 扩展的上下文保持其完整性。一旦你试图通过定义T的容器来吃自己的狗粮,你就会遇到嵌套的上下文。
的事情可以做,因为我们将看到,但作为@immortal表明,它需要 产生不同.h
和.c
文件,你需要的T每个值。 可以,例如,定义一个宏的完全通用的列表的-T在 内嵌文件,比方说,list_type.inl
,然后包括在 list_type.inl
每对小集合式包装的 - list_float.h
和list_float.c
- 这 将分别定义和实现浮动列表容器。对于int列表,浮点列表列表,双向列表列表, 等,也是如此, 等等。
一个示意性的例子将使所有清楚。但首先要获得完整的 这个吃你自己的狗食的挑战。
将这样的二阶容器当作一个列表的东西。我们希望 能够通过设置T = list-of-thingummy为我们的宏列表 T解决方案进行实例化。但是,绝不会将其列为POD 数据类型。无论是我们自己的dogfood还是其他人的东西,它的 将成为一个抽象数据类型,它位于堆上,并通过typedef-ed指针类型表示为 其用户。或者至少在 的堆上有动态组件。无论如何,不是POD。
这意味着我们的T清单解决方案仅仅被告知 T = list-of-thingummy是不够的。还必须告知T是否需要非POD 复制构建和销毁,如果是的话如何复制构建和销毁 。在C而言,这意味着:
我们可以不先了解有关默认施工或施工从 非T的参数,我们可以合理地限制我们的列表的-T解决方案, 从用户提供的原件复制的对象的遏制。但我们 必须复制它们,我们必须处置我们的副本。除了T列表之外,假设我们希望为T集或T 1到T 2的映射 提供模板。这些按键排序的数据类型添加了另一个参数 我们必须插入T或T1的任何非POD值,即如何订购 密钥类型的任何两个对象。实际上,我们将需要 的任何关键数据类型的参数memcmp()
不会做。
注意到之后,我们将坚持使用 原理图示例中更简单的T列表问题;为了进一步简化,我会忘记任何const
API的合意 。
对于这一点,我们会想其他任何模板容器类型的一些令牌粘贴 宏,让我们方便组装的功能和类型, 加上可能其他工具宏标识符。这些都可以去一个头,说macro_kit.h
, 如:
#ifndef MACRO_KIT_H
#define MACRO_KIT_H
/* macro_kit.h */
#define _CAT2(x,y) x##y
// Concatenate 2 tokens x and y
#define CAT2(x,y) _CAT2(x,y)
// Concatenate 3 tokens x, y and z
#define CAT3(x,y,z) CAT2(x,CAT2(y,z))
// Join 2 tokens x and y with '_' = x_y
#define JOIN2(x,y) CAT3(x,_,y)
// Join 3 tokens x, y and z with '_' = x_y_z
#define JOIN3(x,y,z) JOIN2(x,JOIN2(y,z))
// Compute the memory footprint of n T's
#define SPAN(n,T) ((n) * sizeof(T))
#endif
我们的list_type.inl
示意结构:
//! There is intentionally no idempotence guard on this file
#include "macro_kit.h"
#include <stddef.h>
#ifndef INCLUDE_LIST_TYPE_INL
#error This file should only be included from headers \
that define INCLUDE_LIST_TYPE_INL
#endif
#ifndef LIST_ELEMENT_TYPE
#error Need a definition for LIST_ELEMENT_TYPE
#endif
/* list_type.inl
Defines and implements a generic list-of-T container
for T the current values of the macros:
- LIST_ELEMENT_TYPE:
- must have a definition = the datatype (or typedef alias) for \
which a list container is required.
- LIST_ELEMENT_COPY_INITOR:
- If undefined, then LIST_ELEMENT_TYPE is assumed to be copy-
initializable by the assignment operator. Otherwise must be defined
as the name of a copy initialization function having a prototype of
the form:
LIST_ELEMENT_TYPE * copy_initor_name(LIST_ELEMENT_TYPE *pdest,
LIST_ELEMENT_TYPE *psrc);
that will attempt to copy the LIST_ELEMENT_TYPE at `psrc` into the
uncommitted memory at `pdest`, returning `pdest` on success and NULL
on failure.
N.B. This file itself defines the copy initializor for the list-type
that it generates.
- LIST_ELEMENT_DISPOSE
If undefined, then LIST_ELEMENT_TYPE is assumed to need no
destruction. Otherwise the name of a destructor function having a
protoype of the form:
void dtor_name(LIST_ELEMENT_TYPE pt*);
that appropriately destroys the LIST_ELEMENT_TYPE at `pt`.
N.B. This file itself defines the destructor for the list-type that
it generates.
*/
/* Define the names of the list-type to generate,
e.g. list_int, list_float
*/
#define LIST_TYPE JOIN2(list,LIST_ELEMENT_TYPE)
/* Define the function-names of the LIST_TYPE API.
Each of the API macros LIST_XXXX generates a function name in
which LIST becomes the value of LIST_TYPE and XXXX becomes lowercase,
e.g list_int_new
*/
#define LIST_NEW JOIN2(LIST_TYPE,new)
#define LIST_NODE JOIN2(LIST_TYPE,node)
#define LIST_DISPOSE JOIN2(LIST_TYPE,dispose)
#define LIST_COPY_INIT JOIN2(LIST_TYPE,copy_init)
#define LIST_COPY JOIN2(LIST_TYPE,copy)
#define LIST_BEGIN JOIN2(LIST_TYPE,begin)
#define LIST_END JOIN2(LIST_TYPE,end)
#define LIST_SIZE JOIN2(LIST_TYPE,size)
#define LIST_INSERT_BEFORE JOIN3(LIST_TYPE,insert,before)
#define LIST_DELETE_BEFORE JOIN3(LIST_TYPE,delete,before)
#define LIST_PUSH_BACK JOIN3(LIST_TYPE,push,back)
#define LIST_PUSH_FRONT JOIN3(LIST_TYPE,push,front)
#define LIST_POP_BACK JOIN3(LIST_TYPE,pop,back)
#define LIST_POP_FRONT JOIN3(LIST_TYPE,pop,front)
#define LIST_NODE_GET JOIN2(LIST_NODE,get)
#define LIST_NODE_NEXT JOIN2(LIST_NODE,next)
#define LIST_NODE_PREV JOIN2(LIST_NODE,prev)
/* Define the name of the structure used to implement a LIST_TYPE.
This structure is not exposed to user code.
*/
#define LIST_STRUCT JOIN2(LIST_TYPE,struct)
/* Define the name of the structure used to implement a node of a LIST_TYPE.
This structure is not exposed to user code.
*/
#define LIST_NODE_STRUCT JOIN2(LIST_NODE,struct)
/* The LIST_TYPE API... */
// Define the abstract list type
typedef struct LIST_STRUCT * LIST_TYPE;
// Define the abstract list node type
typedef struct LIST_NODE_STRUCT * LIST_NODE;
/* Return a pointer to the LIST_ELEMENT_TYPE in a LIST_NODE `node`,
or NULL if `node` is null
*/
extern LIST_ELEMENT_TYPE * LIST_NODE_GET(LIST_NODE node);
/* Return the LIST_NODE successor of a LIST_NODE `node`,
or NULL if `node` is null.
*/
extern LIST_NODE LIST_NODE_NEXT(LIST_NODE node);
/* Return the LIST_NODE predecessor of a LIST_NODE `node`,
or NULL if `node` is null.
*/
extern LIST_NODE LIST_NODE_PREV(LIST_NODE node);
/* Create a new LIST_TYPE optionally initialized with elements copied from
`start` and until `end`.
If `end` is null it is assumed == `start` + 1.
If `start` is not NULL then elements will be appended to the
LIST_TYPE until `end` or until an element cannot be successfully copied.
The size of the LIST_TYPE will be the number of successfully copied
elements.
*/
extern LIST_TYPE LIST_NEW(LIST_ELEMENT_TYPE *start, LIST_ELEMENT_TYPE *end);
/* Dispose of a LIST_TYPE
If the pointer to LIST_TYPE `plist` is not null and addresses
a non-null LIST_TYPE then the LIST_TYPE it addresses is
destroyed and set NULL.
*/
extern void LIST_DISPOSE(LIST_TYPE * plist);
/* Copy the LIST_TYPE at `psrc` into the LIST_TYPE-sized region at `pdest`,
returning `pdest` on success, else NULL.
If copying is unsuccessful the LIST_TYPE-sized region at `pdest is
unchanged.
*/
extern LIST_TYPE * LIST_COPY_INIT(LIST_TYPE *pdest, LIST_TYPE *psrc);
/* Return a copy of the LIST_TYPE `src`, or NULL if `src` cannot be
successfully copied.
*/
extern LIST_TYPE LIST_COPY(LIST_TYPE src);
/* Return a LIST_NODE referring to the start of the
LIST_TYPE `list`, or NULL if `list` is null.
*/
extern LIST_NODE LIST_BEGIN(LIST_TYPE list);
/* Return a LIST_NODE referring to the end of the
LIST_TYPE `list`, or NULL if `list` is null.
*/
extern LIST_NODE LIST_END(LIST_TYPE list);
/* Return the number of LIST_ELEMENT_TYPEs in the LIST_TYPE `list`
or 0 if `list` is null.
*/
extern size_t LIST_SIZE(LIST_TYPE list);
/* Etc. etc. - extern prototypes for all API functions.
...
...
*/
/* If LIST_IMPLEMENT is defined then the implementation of LIST_TYPE is
compiled, otherwise skipped. #define LIST_IMPLEMENT to include this
file in the .c file that implements LIST_TYPE. Leave it undefined
to include this file in the .h file that defines the LIST_TYPE API.
*/
#ifdef LIST_IMPLEMENT
// Implementation code now included.
// Standard library #includes...?
// The heap structure of a list node
struct LIST_NODE_STRUCT {
struct LIST_NODE_STRUCT * _next;
struct LIST_NODE_STRUCT * _prev;
LIST_ELEMENT_TYPE _data[1];
};
// The heap structure of a LIST_TYPE
struct LIST_STRUCT {
size_t _size;
struct LIST_NODE_STRUCT * _anchor;
};
/* Etc. etc. - implementations for all API functions
...
...
*/
/* Undefine LIST_IMPLEMENT whenever it was defined.
Should never fall through.
*/
#undef LIST_IMPLEMENT
#endif // LIST_IMPLEMENT
/* Always undefine all the LIST_TYPE parameters.
Should never fall through.
*/
#undef LIST_ELEMENT_TYPE
#undef LIST_ELEMENT_COPY_INITOR
#undef LIST_ELEMENT_DISPOSE
/* Also undefine the "I really meant to include this" flag. */
#undef INCLUDE_LIST_TYPE_INL
注意list_type.inl
具有对复式没有纳入宏观后卫。您至少需要一些 - 至少是模板API - 每次查看时都会包含 。
如果您阅读文件顶部的注释,您可以猜到如何编码 包装页眉以导入int列表容器类型。
#ifndef LIST_INT_H
#define LIST_INT_H
/* list_int.h*/
#define LIST_ELEMENT_TYPE int
#define INCLUDE_LIST_TYPE_INL
#include "list_type.inl"
#endif
,同样你会如何编写包裹头部导入列表的一览的-INT 容器类型:
#ifndef LIST_LIST_INT_H
#define LIST_LIST_INT_H
/* list_list_int.h*/
#define LIST_ELEMENT_TYPE list_int
#define LIST_ELEMENT_COPY_INIT list_int_copy_init
#define LIST_ELEMENT_DISPOSE list_int_dispose
#define INCLUDE_LIST_TYPE_INL
#include "list_type.inl"
#endif
您的应用程序可以安全地包括这样的包装,例如
#include "list_int.h"
#include "list_list_int.h"
尽管在他们冲突的方式定义LIST_ELEMENT_TYPE
因为 list_type.inl
总是#undefs
都当它与他们做过参数列表型 宏:查看文件的最后几行。
还要注意使用宏LIST_IMPLEMENT
。如果在解析list_type.inl
时未定义,则只显示模板API;跳过模板实现 。如果定义了LIST_IMPLEMENT
,则编译整个文件。因此,我们的 封装标题(通过未定义LIST_IMPLEMENT
)仅导入列表类型 API。
相反,对于我们的包装源文件list_int.c
,list_list_int.c
,我们将 定义为LIST_IMPLEMENT
。在那之后,没有什么可以做,但包括 相应标题:
/* list_int.c */
#define LIST_IMPLEMENT
#include "list_int.h"
和:
/* list_list_int.c*/
#include "list_int.h"
#define LIST_IMPLEMENT
#include "list_list_int.h"
在你的应用
现在,没有列表模板的宏出现。你的包裹 头解析出“真正的代码”:(!)
#include "list_int.h"
#include "list_list_int.h"
// etc.
int main(void)
{
int idata[10] = {1,2,3,4,5,6,7,8,9,10};
//...
list_int lint = list_int_new(idata,idata + 10);
//...
list_list_int llint = list_list_int_new(&lint,0);
//...
list_int_dispose(&lint);
//...
list_list_int_dispose(&llint);
//...
exit(0);
}
要使用“C++模板库”这种方式,只有努力工作 是编写.inl
文件每个集装箱式装备自己,你想要并非常彻底地测试它 。然后,您可能会生成一个对象文件 和标头,用于每个本地数据类型和容器类型的组合,用于 现成的链接,并根据需要为其他类型的文件打包.h
和.c
。不用说,只要C++发布了模板,我就热衷于出汗 他们这样蒸发。但它可以这样完成,一般来说,如果由于某种原因,C是唯一的选择,则通常是 。
语法看起来很酷:) – 2012-02-22 20:05:50
[OpenGC3](https://github.com/kevin-dong-nai-jia/OpenGC3)就是你要找的东西。 ['ccxll(T)'](https://github.com/kevin-dong-nai-jia/OpenGC3/blob/master/doc/ccxll-list.pdf)甚至可以[嵌套](https:// gist.github.com/kevin-dong-nai-jia/af150182091f2871a92176b15965f814)! – 2017-04-05 06:27:07