有没有一种方法可以(滥用)C预处理器来在C中模拟命名空间?
我想的是这样的:
#define NAMESPACE name_of_ns
some_function() {
some_other_function();
}
这将被翻译为:
name_of_ns_some_function() {
name_of_ns_some_other_function();
}
有没有一种方法可以(滥用)C预处理器来在C中模拟命名空间?
我想的是这样的:
#define NAMESPACE name_of_ns
some_function() {
some_other_function();
}
这将被翻译为:
name_of_ns_some_function() {
name_of_ns_some_other_function();
}
另一种选择是声明一个结构体来保存所有函数,然后静态地定义函数。这样,您只需要担心全局名称结构体的名称冲突。
// 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
间接调用。// 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 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 some_func foobar_some_func
。这样可以使用短名称来初始化函数指针。 - tstanisl您可以使用 ## 运算符:
#define FUN_NAME(namespace,name) namespace ## name
并将函数声明为:
void FUN_NAME(MyNamespace,HelloWorld)()
看起来相当尴尬。
我采用基于结构体的方法,进行了两次精简:我添加了子结构以创建分层命名空间,并在需要简化命名空间路径时定义了一些简单的宏。
我们以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 <stdio.h>
#include "foobar_text.h"
void foobar_text__print(char *s) {
printf("%s\n", s);
}
I came up with the following scheme :
(header)
// 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
namespace MY_OBJECT {
struct HANDLE;
HANDLE *init();
void destroy(HANDLE * & h);
void do_something(HANDLE *h, ... );
}
struct MY_OBJECT_HANDLE;
struct MY_OBJECT_HANDLE *my_object_init();
void my_object_destroy( MY_OBJECT_HANDLE * & h );
void my_object_do_something(MY_OBJECT_HANDLE *h, ... );
template<T> T multiply<T>( T x, T y ) { return x*y }
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);
multiply-template.c
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
return x*y;
}
#ifndef _INT_MULTIPLY_H
#define _INT_MULTIPLY_H
#ifdef _multiply_
#undef _multiply_
#endif
#define _multiply_(NAME) int ## _ ## NAME
#ifdef _multiply_type_
#undef _multiply_type_
#endif
#define _multiply_type_ int
#include "multiply-template.h"
#endif
int_multiply.c
#include "int_multiply.h"
#include "multiply-template.c"
int int_multiply( int x, int y ) { return x * y }
我已经为提供的链接创建了一个更详细的教程。希望这对某些人有所帮助!
一个类似于被接受答案的方法如下:
// 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
#include "foobar.h"
struct _foobar foobar = {
foobar_some_func;
foobar_other_func;
};
使用这些函数时,
foobar.some_func(10);
foobar.other_func();
可以使用前缀来命名文件函数,就像在C / C ++的stb单文件公共域库中一样:“作为一种相对合理的方式来对文件名和源函数名进行命名空间管理”。
例如:
.
而不是 _
,而避免使用这个简单的解决方案。 - SO_fix_the_vote_sorting_bug#define
宏:#include <stdio.h>
#define ns(x) gargantua_ ## x
struct ns(stats) {
int size;
};
int ns(get_size)(struct ns(stats) *st) {
return st->size;
}
void ns(set_size)(struct ns(stats) *st, int sz) {
st->size = sz;
}
int main(void) {
struct ns(stats) stats = {0};
ns(set_size)(&stats, 3);
printf("size=%d\n", ns(get_size)(&stats));
return 0;
}
Running it through the preprocessor gives you:
struct gargantua_stats {
int size;
};
int gargantua_get_size(struct gargantua_stats *st) {
return st->size;
}
void gargantua_set_size(struct gargantua_stats *st, int sz) {
st->size = sz;
}
int main(void) {
struct gargantua_stats stats = {0};
gargantua_set_size(&stats, 3);
printf("size=%d\n", gargantua_get_size(&stats));
return 0;
}
以下是一个示例,基于以上方法结合了函数和结构体,以创建伪命名空间NAMESPACE1和NAMESPACE2。与持有函数的结构体相比,这种方法的好处在于需要在多个伪命名空间中使用标准化结构的情况并不总是可能(要么根本不可能,要么需要大量工作,这显然不能改进代码)。
不确定宏扩展顺序是否会成为问题,但这在GCC上可以工作,并且似乎最小化了所需的代码更改,同时保持了相当不错的可读性(尽管远非理想)。
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