C 中的命名空间

发布于 2024-07-10 14:27:41 字数 303 浏览 6 评论 0原文

有没有办法(ab)使用C预处理器来模拟C中的命名空间?

我正在思考这样的事情:

#define NAMESPACE name_of_ns
some_function() {
    some_other_function();
}

这将被翻译为:

name_of_ns_some_function() {
    name_of_ns_some_other_function();
}

Is there a way to (ab)use the C preprocessor to emulate namespaces in 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();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

终遇你 2024-07-17 14:27:42

您可以使用 ## 运算符:

#define FUN_NAME(namespace,name) namespace ## name

并将函数声明为:

void FUN_NAME(MyNamespace,HelloWorld)()

不过看起来很尴尬。

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.

风追烟花雨 2024-07-17 14:27:42

我使用基于结构的方法,并进行了两项改进:添加子结构来创建分层命名空间,并在想要简化命名空间的路径时定义一些简单的宏。

让我们以 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 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 Foobar library as an example.

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
    }
};

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:

#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.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);
}
記柔刀 2024-07-17 14:27:42

我想出了以下方案:(

标题)

// 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

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
十级心震 2024-07-17 14:27:42

我写了一篇关于如何使用 C 来利用命名空间和/或模板的教程。

C 中的命名空间和模板

C 中的命名空间和模板(使用链接列表)

对于基本命名空间,可以简单地为命名空间名称添加前缀作为约定。

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-template.h、

_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;
}

我们现在可以如下定义 int_multiply。 在此示例中,我将创建一个 int_multiply.h/.c 文件。

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

#include "int_multiply.h"
#include "multiply-template.c"

在这一切结束时,您将拥有一个函数和头文件。

int int_multiply( int x, int y ) { return x * y }

我根据提供的链接创建了一个更详细的教程。 希望这对某人有帮助!

I wrote up a tutorial on how to get the advantage of namespaces and/or templates using C.

Namespaces and templates in 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

_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;
}

We can now define int_multiply as follows. In this example, I'll create a int_multiply.h/.c file.

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

#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!

唠甜嗑 2024-07-17 14:27:42

与接受的答案类似的方法如下:

// 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;
};

使用函数时,

foobar.some_func(10);
foobar.other_func();

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:

#include "foobar.h"
struct _foobar foobar = {
    foobar_some_func;
    foobar_other_func;
};

when using the functions,

foobar.some_func(10);
foobar.other_func();
柠檬色的秋千 2024-07-17 14:27:42

您可以使用辅助 #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;
}

通过预处理器运行它可以得到:

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;
}

You can use a helper #define macro:

#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;
}
晚雾 2024-07-17 14:27:42

可以使用文件函数名称的前缀,例如 stb 用于 C/C++ 的单文件公共域库 :“作为命名文件名和源函数名称的一种适度合理的方式”。

示例:

  • stb_image_write.h ( 文件名 )
  • STBI_THREAD_LOCAL ( 名称 )
  • static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)

One can use prefixes for file function names, like in stb single-file public domain libraries for C/C++: "as a moderately sane way of namespacing the filenames and source function name".

Examples:

  • stb_image_write.h ( file name )
  • STBI_THREAD_LOCAL ( name )
  • static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
醉城メ夜风 2024-07-17 14:27:42

这是一个基于上述方法并将它们组合起来的函数和结构的示例,以创建伪命名空间 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

header2.h和api2.c中的其他代码与header1.h和header2.h相同,针对命名空间“NAMESPACE2”进行修改

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.

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.


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

Other code in header2.h and api2.c is the same as header1.h and header2.h, modified for namespace "NAMESPACE2"

伏妖词 2024-07-17 14:27:42

我意识到这是一个老问题(11年前),但我试图基本上完成我认为你最初想要的事情,正如你上面列出的那样。

我希望在我的函数前面有一个名称空间。 但我希望能够更改该命名空间。 默认情况下,我希望此示例没有名称空间,但如果发生命名冲突,那么我希望能够在库中的所有函数前面添加名称空间。 (与 C++ 相比,这稍微落后了一点,C++ 默认有一个命名空间,并且您可以使用 using namespace another 来消除每次都指定命名空间的需要。)但是,就像 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");
}

此代码只能在 C99 及更高版本上编译,因为它使用可变参数宏。 这些宏执行一种递归形式,这一切都是为了我们可以从顶部定义的宏中获取值。

其工作原理分解:

  • 我们定义我们想要的命名空间。
  • 如果未定义任何内容,则设置默认值
  • 执行一系列调用以绕过和(ab)使用预处理器功能。
  • 将 NSPREPENDER 宏函数添加到每个 c 函数中,以便可以对其进行名称修改。
  • 使用损坏的名称编写代码,因为当编译器看到它时,名称将被正确损坏。

这段代码是用 clang 测试的。

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.

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.

#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.

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.

This code was tested with clang.

上课铃就是安魂曲 2024-07-17 14:27:41

另一种选择是声明一个结构体来保存所有函数,然后静态定义函数。 那么您只需要担心全局名称结构的名称冲突。

// 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_barmy_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_barmy_baz 的多个定义不会冲突,因为它们是静态定义的,但仍然可以通过适当的命名空间结构访问底层函数。

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.

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.

妳是的陽光 2024-07-17 14:27:41

使用命名空间前缀时,我通常会为缩短的名称添加宏,这些宏可以在包含标头之前通过#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(在评论中)描述的命名空间宏更干净、更有用的解决方案。

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:

// 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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文