C中的命名空间
有没有办法(AB)使用C预处理器来模拟C中的命名空间?
我正在考虑这方面的一些事情:
#define NAMESPACE name_of_ns some_function() { some_other_function(); }
这将转化为:
name_of_ns_some_function() { name_of_ns_some_other_function(); }
当使用名称空间前缀时,我通常为缩写名称添加macros,可以在包含头之前通过#define NAMESPACE_SHORT_NAMES
激活缩写名称。 头文件foobar.h可能看起来像这样:
// inclusion guard #ifndef FOOBAR_H_ #define FOOBAR_H_ // long names void foobar_some_func(int); void foobar_other_func(); // short names #ifdef FOOBAR_SHORT_NAMES #define some_func(...) foobar_some_func(__VA_ARGS__) #define other_func(...) foobar_other_func(__VA_ARGS__) #endif #endif
如果我想在包含文件中使用短名称,我会这样做
#define FOOBAR_SHORT_NAMES #include "foobar.h"
我发现这比使用Vinko Vrsalovic(在注释中)描述的名称空间macros更清洁和更有用。
另一种select是声明一个结构来保存所有的函数,然后静态地定义你的函数。 那么你只需要担心全局名称struct的名称冲突。
// foo.h #ifndef FOO_H #define FOO_H typedef struct { int (* const bar)(int, char *); void (* const baz)(void); } namespace_struct; extern namespace_struct const foo; #endif // FOO_H // foo.c #include "foo.h" static int my_bar(int a, char * s) { /* ... */ } static void my_baz(void) { /* ... */ } namespace_struct const foo = { my_bar, my_baz } // main.c #include <stdio.h> #include "foo.h" int main(void) { foo.baz(); printf("%d", foo.bar(3, "hello")); return 0; }
在上面的例子中, my_bar
和my_baz
不能直接从main.c调用,只能通过foo
调用。
如果您有一大堆声明具有相同签名的函数的名称空间,则可以标准化该集合的名称空间结构,并select在运行时使用哪个名称空间。
// goo.h #ifndef GOO_H #define GOO_H #include "foo.h" extern namespace_struct const goo; #endif // GOO_H // goo.c #include "goo.h" static int my_bar(int a, char * s) { /* ... */ } static void my_baz(void) { /* ... */ } namespace_struct const goo = { my_bar, my_baz }; // other_main.c #include <stdio.h> #include "foo.h" #include "goo.h" int main(int argc, char** argv) { namespace_struct const * const xoo = (argc > 1 ? foo : goo); xoo->baz(); printf("%d", xoo->bar(3, "hello")); return 0; }
my_bar
和my_baz
的多重定义不会发生冲突,因为它们是静态定义的,但底层函数仍然可以通过适当的命名空间结构来访问。
你可以使用##运算符:
#define FUN_NAME(namespace,name) namespace ## name
并将函数声明为:
void FUN_NAME(MyNamespace,HelloWorld)()
看起来很尴尬。
我提出了以下计划:
(标题)
// NS_PREFIX controls the prefix of each type and function declared in this // header, in order to avoid name collision. #define NS_PREFIX myprefix_ // Makes a string from argument (argument is not macro-expanded). #define stringify(arg) #arg // Concatenation that macro-expands its arguments. #define concat(p1, p2) _concat(p1, p2) // Macro expands the arguments. #define _concat(p1, p2) p1 ## p2 // Do the actual concatenation. // Append the namespace prefix to the identifier. #define ns(iden) concat(NS_PREFIX, iden) // header content, for instance : void ns(my_function)(int arg1, ns(t) arg2, int arg3); // Allow implementation files to use namespacing features, else // hide them from the including files. #ifndef _IMPL #undef NS_PREFIX #undef ns #undef stringify #undef concat #undef _concat #endif // _IMPL
(实现)
#define _IMPL #include "header.h" #undef __IMPL
类似于接受的答案的方法如下:
// inclusion guard #ifndef FOOBAR_H_ #define FOOBAR_H_ // long names void foobar_some_func(int); void foobar_other_func(); // qualified names #ifdef FOOBAR_SHORT_NAMES extern struct _foobar { void (*some_func)(int); void (*other_func)(); } foobar; #endif #endif
这个头文件应该带有一个.c文件:
#include "foobar.h" struct _foobar foobar = { foobar_some_func; foobar_other_func; };
当使用这些function时,
foobar.some_func(10); foobar.other_func();
我使用基于结构的方法,有两个改进:我添加子结构来创build分层名称空间,当我想简化名称空间的path时,我定义了一些简单的macros。
以Foobar图书馆为例。
foobar.h中
#ifndef __FOOBAR_H__ #define __FOOBAR_H__ // definition of the namespace's hierarchical structure struct _foobar_namespace { struct { void (*print)(char *s); } text; struct { char *(*getDateString)(void); } date; }; // see the foobar.c file // it must be the only one defining the FOOBAR macro # ifndef FOOBAR // definition of the namespace global variable extern struct _foobar_namespace foobar; # endif // FOOBAR #endif // __FOOBAR_H__
foobar.c但是
// the FOOBAR macro is needed to avoid the // extern foobar variable declaration #define FOOBAR #include "foobar.h" #include "foobar_text.h" #include "foobar_date.h" // creation of the namespace global variable struct _foobar_namespace foobar = { .text = { .print = foobar_text__print }, .date = { .getDateString = foobar_date__getDateString } };
然后,可以使用命名空间:
#include "foobar.h" void main() { foobar.text.print("it works"); }
但foobar_text__print()
和foobar.text.print()
之间没有太大的区别。 我认为第二个更具可读性,但是值得怀疑。 所以通过定义一些macros来简化这些命名空间变得非常有用:
#include "foobar.h" #define txt foobar.text #define date foobar.date void main() { char *today = date.getDateString(); txt.print(today); }
这种层次化的命名空间是快速定义的,易于理解的,并减less代码冗长。
只是为了好玩,这里是foobar.text
代码的文件:
foobar_text.h
#ifndef __FOOBAR_TEXT_H__ #define __FOOBAR_TEXT_H__ void foobar_text__print(char *s); #endif // __FOOBAR_TEXT_H__
foobar_text.c
#include <stdio.h> #include "foobar_text.h" void foobar_text__print(char *s) { printf("%s\n", s); }
这里是一个构build上面的方法的例子,并且将funcs和structure结合起来创build伪命名空间NAMESPACE1和NAMESPACE2。 这样做的好处在于拥有一个保存函数的结构,这个结构保持函数方法需要跨越多个伪名称空间的标准化结构,这并不总是可能的(根本不可能,或者没有很多可以certificate的工作没有改善代码)或可取的。
不确定macros扩展顺序是否可能是一个问题,但是这对GCC起作用,并似乎尽量减less所需的代码更改量,同时保持可观的(尽pipe很不理想)可读性。
application.c:
#include <stdio.h> #include "header1.h" #include "header2.h" /* use NAMESPACE1 and NAMESPACE2 macros to choose namespace */ int main() { NAMESPACE1(mystruct) data1; // structure specific to this namespace NAMESPACE2(mystruct) data2; data1.n1 = '1'; data1.c = 'a'; data2.n2 = '2'; data2.c = 'a'; NAMESPACE1(print_struct)(&data1); // function specific to this namespace NAMESPACE2(print_struct)(&data2); }
那么header1.h
/* the below block is unnecessary, but gets rid of some compiler warnings */ #ifdef NAMESPACE_REAL #undef NAMESPACE_REAL #endif /* edit the below lines to change the three occurrences of NAMESPACE1 to the desired namespace */ #define NAMESPACE1(name) NAMESPACE1 ## _ ## name #define NAMESPACE_REAL(name) NAMESPACE1(name) /* don't edit the next block */ #define TYPEDEF(name, ...) typedef struct NAMESPACE_REAL(name) { __VA_ARGS__ } NAMESPACE_REAL(name) #define STRUCT(name) struct NAMESPACE_REAL(name) #define FUNC(name) NAMESPACE_REAL(name) /* normal header code, using FUNC and STRUCT macros */ #include <stdio.h> TYPEDEF(mystruct, char n1; char c; ); void FUNC(print_struct)(STRUCT(mystruct) *data); /* don't edit the rest */ #undef TYPEDEF
api1.c:
#include "header1.h" /* normal code, using FUNC and STRUCT macros */ void FUNC(print_struct)(STRUCT(mystruct) *data) { printf("this is the struct from namespace1: %c %c\n", data->n1, data->c); } /* don't edit the rest */ #undef STRUCT #undef FUNC #undef NAMESPACE #undef NAMESPACE_REAL
header2.h和api2.c中的其他代码与header1.h和header2.h相同,对名称空间“NAMESPACE2”进行了修改