C++ C语言中的名称空间

C++ C语言中的名称空间,c++,c,namespaces,c-preprocessor,C++,C,Namespaces,C Preprocessor,有没有办法(ab)使用C预处理器来模拟C中的名称空间 我的想法是这样的: #define NAMESPACE name_of_ns some_function() { some_other_function(); } 这将转化为: name_of_ns_some_function() { name_of_ns_some_other_function(); } 您可以使用##运算符: #define FUN_NAME(namespace,name) namespace ## n

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

我的想法是这样的:

#define NAMESPACE name_of_ns
some_function() {
    some_other_function();
}
这将转化为:

name_of_ns_some_function() {
    name_of_ns_some_other_function();
}
您可以使用##运算符:

#define FUN_NAME(namespace,name) namespace ## name
并声明函数为:

void FUN_NAME(MyNamespace,HelloWorld)()

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

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

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

我提出了以下方案:

(标题)

(实施)


与公认答案类似的方法如下:

// 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();

下面是一个基于上述方法的示例,它将函数和结构的方法结合起来,创建伪名称空间NAMESPACE1和NAMESPACE2。与拥有一个包含函数的结构相比,这种方法的好处在于,包含函数的结构方法需要跨多个伪名称空间的标准化结构,而这并不总是可能的(或者根本不可能,或者如果没有大量的工作,可能无法改进代码),或者是可取的

不确定宏扩展顺序是否会成为一个问题,但这在GCC上起作用,似乎可以最大限度地减少所需的代码更改量,同时保持良好的可读性(尽管远不理想)


应用程序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);

}


header2.h和api2.c中的其他代码与header1.h和header2.h相同,为名称空间“NAMESPACE2”进行了修改。

我使用基于结构的方法,有两个改进:我添加子结构来创建分层名称空间,当我想简化名称空间的路径时,我定义了一些简单的宏

让我们以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__
#ifndef __FOOBAR_TEXT_H__
#define __FOOBAR_TEXT_H__

void foobar_text__print(char *s);

#endif // __FOOBAR_TEXT_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 <stdio.h>
#include "foobar_text.h"

void foobar_text__print(char *s) {
    printf("%s\n", s);
}
然后,可以使用名称空间:

#include "foobar.h"

void main() {
    foobar.text.print("it works");
}
但是
foobar\u text\u 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\u text.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__
#ifndef __FOOBAR_TEXT_H__
#define __FOOBAR_TEXT_H__

void foobar_text__print(char *s);

#endif // __FOOBAR_TEXT_H__
foobar\u text.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 <stdio.h>
#include "foobar_text.h"

void foobar_text__print(char *s) {
    printf("%s\n", s);
}
#包括
#包括“foobar_text.h”
void foobar_text____打印(字符*s){
printf(“%s\n”,s);
}

我写了一篇教程,介绍了如何使用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, ... );
使用名称空间和模板概念的第二种方法是使用宏连接和include。例如,我可以创建一个

template<T> T multiply<T>( T x, T y ) { return x*y }
乘法模板

_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
  return x*y;
}
我们现在可以定义int_乘法如下。在本例中,我将创建一个int_multiply.h/.c文件

整数乘

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

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

我意识到这是一个老问题(11岁了),但我试图基本上实现我认为您最初想要的,正如您上面列出的那样

我希望在我的函数前面有一个名称空间。但是我想要改变名称空间的功能。默认情况下,我希望本例没有名称空间,但如果发生命名冲突,那么我希望能够为库中的所有函数预先设置名称空间。(与C++相比,在默认情况下,这里有一个命名空间稍微落后,并且使用<代码>使用名称空间,无论是< /Cord>),每次都需要指定命名空间。但是,就像C++一样,如果使用命名空间< /COS>语句和别名代码,请在<代码>中删除,则需要更新调用代码。您也可以编写一些其他宏序列来自动重命名调用,但这超出了我认为您要查找的范围

#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");
}
#包括
#定义名称空间(…)测试//将其用作我的前置程序
//在这里,所有可以包含在头文件中的魔法都发生了。
#ifndef名称空间
//默认情况下没有命名空间
#定义命名空间(…)
#恩迪夫
//实际替换
#定义NSPREPENDER(…)NSPROCESSING(命名空间(),_VA_ARGS__;)
#定义NSPROCESSING(…)NSFINALIZE(_VA_ARGS__)
#定义(a,b)a##b
//开始实际程序
//原型
无效的打印机(myprint)();
int main()
{
test_myprint();//如果名称空间(…)定义为其他任何内容,则此代码必须更改。
返回0;
}
//实施
无效NSPREPENDER(myprint)()
{
看跌期权(“测试”);
}
此代码将仅在C99及以上版本上编译,因为它使用可变宏。这些宏执行一种递归形式,这样我们就可以从顶部定义的宏中获取值

所有it工程的明细:

  • 我们定义了我们想要的名称空间
  • 如果未定义任何内容,则设置默认值
  • 做一系列的调用来绕过和(ab)使用prep
    int int_multiply( int x, int y ) { return x * y }
    
    #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");
    }
    
    #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;
    }