有没有办法(ab)使用C预处理器来模拟C中的命名空间?
我正在考虑以下几点:
#define NAMESPACE name_of_ns some_function() { some_other_function(); }
这将被翻译为:
name_of_ns_some_function() { name_of_ns_some_other_function(); }
rampion.. 82
另一种方法是声明一个struct来保存你的所有函数,然后静态地定义你的函数.然后,您只需要担心全局名称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#include "foo.h" int main(void) { foo.baz(); printf("%d", foo.bar(3, "hello")); return 0; }
在上面的例子中,my_bar
并my_baz
不能直接从main.c调用,只能通过foo
.
如果您有一堆声明具有相同签名的函数的命名空间,那么您可以标准化该集合的命名空间结构,并选择在运行时使用哪个命名空间.
// 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#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
不冲突,因为它们是静态定义的,但是底层函数仍然可以通过适当的命名空间结构访问.
另一种方法是声明一个struct来保存你的所有函数,然后静态地定义你的函数.然后,您只需要担心全局名称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#include "foo.h" int main(void) { foo.baz(); printf("%d", foo.bar(3, "hello")); return 0; }
在上面的例子中,my_bar
并my_baz
不能直接从main.c调用,只能通过foo
.
如果您有一堆声明具有相同签名的函数的命名空间,那么您可以标准化该集合的命名空间结构,并选择在运行时使用哪个命名空间.
// 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#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 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所描述的命名空间宏(在评论中)更清晰,更有用.
你可以使用##运算符:
#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
我使用基于结构的方法,并进行了两个改进:添加子结构以创建分层名称空间,并且当我想简化名称空间的路径时定义一些简单的宏。
让我们以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()
。我认为第二个更具可读性,但是值得怀疑。因此,通过定义一些宏来简化这些名称空间将变得非常有用:
#include "foobar.h" #define txt foobar.text #define date foobar.date void main() { char *today = date.getDateString(); txt.print(today); }
这种分层的名称空间可以快速定义,易于理解并减少代码的冗长性。
只是为了好玩,这里是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#include "foobar_text.h" void foobar_text__print(char *s) { printf("%s\n", s); }