C 中的命名空间
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/389827/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Namespaces in C
提问by Kim Stebel
Is there a way to (ab)use the Cpreprocessor to emulate namespaces in C?
有没有办法(ab)使用C预处理器来模拟C 中的命名空间?
I'm thinking something along these lines:
我在想这些事情:
#define NAMESPACE name_of_ns
some_function() {
some_other_function();
}
This would get translated to:
这将被翻译成:
name_of_ns_some_function() {
name_of_ns_some_other_function();
}
采纳答案by Christoph
When using namespace prefixes, I normally add macros for the shortened names which can be activated via #define NAMESPACE_SHORT_NAMES
before inclusion of the header. A header foobar.h might look like this:
使用命名空间前缀时,我通常为缩短的名称添加宏,这些宏可以#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
If I want to use short names in an including file, I'll do
如果我想在包含文件中使用短名称,我会这样做
#define FOOBAR_SHORT_NAMES
#include "foobar.h"
I find this a cleaner and more useful solution than using namespace macros as described by Vinko Vrsalovic (in the comments).
我发现这是一个比 Vinko Vrsalovic(在评论中)描述的使用命名空间宏更干净、更有用的解决方案。
回答by rampion
Another alternative would be to declare a struct to hold all your functions, and then define your functions statically. Then you'd only have to worry about name conflicts for the global name 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;
}
In the above example, my_bar
and my_baz
can't be called directly from main.c, only through foo
.
在上述例子中,my_bar
和my_baz
不能直接从main.c中只有通过调用foo
。
If you have a bunch of namespaces that declare functions with the same signatures, then you can standardize your namespace struct for that set, and choose which namespace to use at runtime.
如果您有一堆名称空间声明具有相同签名的函数,那么您可以为该集合标准化您的名称空间结构,并选择在运行时使用哪个名称空间。
// 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;
}
The multiple definitions of my_bar
and my_baz
don't conflict, since they're defined statically, but the underlying functions are still accessible through the appropriate namespace struct.
的多重定义my_bar
和my_baz
不冲突,因为他们是静态定义,但潜在的功能仍然通过适当的命名空间结构进行访问。
回答by Mehrdad Afshari
You could use the ## operator:
您可以使用 ## 运算符:
#define FUN_NAME(namespace,name) namespace ## name
and declare functions as:
并将函数声明为:
void FUN_NAME(MyNamespace,HelloWorld)()
Looks pretty awkward though.
不过看着还挺别扭的。
回答by Norswap
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
(implementation)
(执行)
#define _IMPL
#include "header.h"
#undef __IMPL
回答by Amaury Bouchard
I use the struct-based approach, with two refinements: I add substructures to create hierarchical namespaces, and I define some simple macros when I want to simplify namespaces' path.
我使用基于结构的方法,有两个改进:我添加子结构来创建分层命名空间,当我想简化命名空间的路径时,我定义一些简单的宏。
Let's take a Foobarlibrary as an example.
我们以Foobar库为例。
foobar.h
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
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
}
};
Then, it's possible to use the namespace:
然后,可以使用命名空间:
#include "foobar.h"
void main() {
foobar.text.print("it works");
}
But there is not so much difference between foobar_text__print()
and foobar.text.print()
. I think the second one is more readable, but it's questionable. So it become really useful by defining some macros to simplify these namespaces:
但是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);
}
This kind of hierarchical namespaces is fast to define, easy to understand, and decrease code verbosity.
这种分层命名空间可以快速定义、易于理解并减少代码冗长。
Just for fun, here are the files for foobar.text
code:
只是为了好玩,这里是foobar.text
代码文件:
foobar_text.h
foobar_text.h
#ifndef __FOOBAR_TEXT_H__
#define __FOOBAR_TEXT_H__
void foobar_text__print(char *s);
#endif // __FOOBAR_TEXT_H__
foobar_text.c
foobar_text.c
#include <stdio.h>
#include "foobar_text.h"
void foobar_text__print(char *s) {
printf("%s\n", s);
}
回答by Andy Curtis
I wrote up a tutorial on how to get the advantage of namespaces and/or templates using C.
我写了一篇关于如何使用 C 获得命名空间和/或模板优势的教程。
Namespaces and templates in C (using Linked Lists)
For the basic namespace, one can simply prefix the namespace name as a convention.
对于基本命名空间,可以简单地作为命名空间名称的前缀作为约定。
namespace MY_OBJECT {
struct HANDLE;
HANDLE *init();
void destroy(HANDLE * & h);
void do_something(HANDLE *h, ... );
}
can be written as
可以写成
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, ... );
A second approach that I have needed that uses the concept of namespacing and templates is to use the macro concatenation and include. For example, I can create a
我需要的第二种使用命名空间和模板概念的方法是使用宏连接和包含。例如,我可以创建一个
template<T> T multiply<T>( T x, T y ) { return x*y }
using template files as follows
使用模板文件如下
multiply-template.h
乘法模板.h
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);
multiply-template.c
乘法模板.c
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
return x*y;
}
We can now define int_multiply as follows. In this example, I'll create a int_multiply.h/.c file.
我们现在可以定义 int_multiply 如下。在本例中,我将创建一个 int_multiply.h/.c 文件。
int_multiply.h
int_multiply.h
#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
int_multiply.c
#include "int_multiply.h"
#include "multiply-template.c"
At the end of all of this, you will have a function and header file for.
在所有这一切结束时,您将拥有一个函数和头文件。
int int_multiply( int x, int y ) { return x * y }
I created a much more detailed tutorial on the links provided. Hopefully this helps someone!
我在提供的链接上创建了一个更详细的教程。希望这对某人有所帮助!
回答by Earth Engine
An approach similar to the accepted answer is the following:
类似于接受的答案的方法如下:
// 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
this header file shall come with a .c file:
这个头文件应该带有一个 .c 文件:
#include "foobar.h"
struct _foobar foobar = {
foobar_some_func;
foobar_other_func;
};
when using the functions,
使用功能时,
foobar.some_func(10);
foobar.other_func();
回答by mwag
here is an example that builds off above approaches and combines them for both funcs and structures to create pseudo-namespaces NAMESPACE1 and NAMESPACE2. the benefit of this over having a structure that holds functions is that the structure-holding-functions approach requires a standardized structure across multiple pseudo-namespaces, and this is not always possible (either at all, or without a lot of work that arguably does not improve the code) or desirable.
这是一个示例,它建立在上述方法的基础上,并将它们结合起来用于函数和结构,以创建伪命名空间 NAMESPACE1 和 NAMESPACE2。与拥有保存函数的结构相比,这种结构的好处是结构保持函数方法需要跨多个伪命名空间的标准化结构,这并不总是可能的(要么根本不可能,要么没有很多工作可以说不改进代码)或可取的。
Not sure if the macro expansion order could be an issue but this works on GCC and seems to minimize the amount of code changes required, while maintaining decent (though far from ideal) readability.
不确定宏扩展顺序是否可能是一个问题,但这适用于 GCC,并且似乎最大限度地减少了所需的代码更改量,同时保持了不错的(尽管远非理想)可读性。
application.c:
应用程序.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
头文件1.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:
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
Other code in header2.h and api2.c is the same as header1.h and header2.h, modified for namespace "NAMESPACE2"
header2.h 和 api2.c 中的其他代码与 header1.h 和 header2.h 相同,针对命名空间“NAMESPACE2”进行了修改
回答by Questionable
I realize that this is an old question (11 years old), but I was trying to accomplish essentially what I think you wanted originally as you have listed above.
我意识到这是一个老问题(11 岁),但我试图基本上完成我认为你最初想要的事情,正如你上面列出的那样。
I wanted there to be a namespace prepended to my functions. But I wanted the ability to change what that namespace would be. By default I wanted for this example to not have a namespace, but if a naming collision occurred then I wanted the ability to prepend a namespace to all of the functions in my library. (This is slightly backwards compared to C++ where there is a namespace by default and you use using namespace whatever
to remove the need to specify the namespace every time.) However, just like C++ if you drop in a using namespace
statement and alias your code, you will need to update your calling code. You could write some other macro sequence to auto rename your calls as well, but that is outside the scope of what I think you were looking for.
我希望在我的函数之前有一个命名空间。但我希望能够改变命名空间的内容。默认情况下,我希望此示例没有命名空间,但如果发生命名冲突,那么我希望能够为库中的所有函数添加命名空间。(这是稍微向后相比,C ++那里是默认命名空间,并使用using namespace whatever
删除需要每次都指定命名空间)。但是,就像C ++,如果你在一个下降using namespace
的语句和别名你的代码,你将需要更新您的调用代码。您也可以编写一些其他宏序列来自动重命名您的调用,但这超出了我认为您正在寻找的范围。
#include <stdio.h>
#define NAMESPACE(...) test_ //Use this as my prepender
//Where all the magic happens which could be included in a header file.
#ifndef NAMESPACE
//No Namespace by default
#define NAMESPACE(...)
#endif
//Actual replacements
#define NSPREPENDER(...) NSPROCESSING(NAMESPACE(), __VA_ARGS__)
#define NSPROCESSING(...) NSFINALIZE(__VA_ARGS__)
#define NSFINALIZE(a,b) a ## b
//BEGIN ACTUAL PROGRAM
//Prototype
void NSPREPENDER(myprint)();
int main()
{
test_myprint(); //If NAMESPACE(...) is defined to anything else, this code must change.
return 0;
}
//Implementation
void NSPREPENDER(myprint)()
{
puts("Testing");
}
This code will compile only on C99 and up since it is using variadic macros. These macros do a form of recursion which is all done so that we can grab the value out of a macro defined at the top.
这段代码只能在 C99 及更高版本上编译,因为它使用的是可变参数宏。这些宏执行了一种递归形式,所有这些都完成了,以便我们可以从顶部定义的宏中获取值。
Breakdown of all it works:
所有它的工作分解:
- We define that we want our namespace to be.
- If nothing is defined set a default
- Do a bunch of calls to bypass and (ab)use preprocessor functionality.
- Add the NSPREPENDER macro function to each c function so that it can be name mangled.
- Write code using mangled names since the name will be properly mangled by the time the compiler see it.
- 我们定义了我们想要的命名空间。
- 如果未定义任何内容,则设置默认值
- 进行大量调用以绕过和(ab)使用预处理器功能。
- 将 NSPREPENDER 宏函数添加到每个 c 函数中,以便可以对其进行名称修改。
- 使用经过修改的名称编写代码,因为在编译器看到时名称将被正确修改。
This code was tested with clang.
这段代码是用 clang 测试的。