Пространства имен в C - PullRequest
       25

Пространства имен в C

49 голосов
/ 23 декабря 2008

Есть ли способ (ab) использовать препроцессор C для эмуляции пространств имен в C ?

Я думаю о чем-то вроде:

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

Это будет переведено на:

name_of_ns_some_function() {
    name_of_ns_some_other_function();
}

Ответы [ 8 ]

80 голосов
/ 23 декабря 2008

Другой альтернативой может быть объявление структуры, которая будет содержать все ваши функции, а затем статическое определение ваших функций. Тогда вам придется беспокоиться только о конфликтах имен для глобальной структуры имен.

// 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 не конфликтуют, поскольку они определены статически, но базовые функции по-прежнему доступны через соответствующую структуру пространства имен.

47 голосов
/ 24 декабря 2008

При использовании префиксов пространства имен я обычно добавляю макросы для сокращенных имен, которые можно активировать с помощью #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"

Я считаю, что это более чистое и полезное решение, чем использование макросов пространства имен, как описано Винко Врсаловичем (в комментариях).

12 голосов
/ 23 декабря 2008

Вы можете использовать оператор ##:

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

и объявить функции как:

void FUN_NAME(MyNamespace,HelloWorld)()

Хотя выглядит довольно неловко.

7 голосов
/ 11 марта 2011

Я придумал следующую схему:

(заголовок)

// 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
4 голосов
/ 30 декабря 2017

Я написал учебник о том, как использовать преимущества пространств имен и / или шаблонов, используя 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 }

используя файлы шаблонов следующим образом

множественно-template.h

_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);

множественно-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 }

Я создал намного более подробное руководство по предоставленным ссылкам. Надеюсь, это кому-нибудь поможет!

4 голосов
/ 26 января 2014

Я использую структурный подход с двумя уточнениями: я добавляю подструктуры для создания иерархических пространств имен и определяю несколько простых макросов, когда хочу упростить путь к пространствам имен.

Давайте возьмем библиотеку 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);
}
3 голосов
/ 30 апреля 2013

Подход, аналогичный принятому ответу, следующий:

// 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();
0 голосов
/ 05 января 2014

здесь приведен пример, который строит вышеприведенные подходы и объединяет их как для функций, так и для структур, чтобы создать псевдо-пространства имен 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"

...