Подтвердить что ты не робот

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

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

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

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

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

name_of_ns_some_function() {
    name_of_ns_some_other_function();
}
4b9b3361

Ответ 1

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

Ответ 2

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

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

Ответ 3

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

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

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

void FUN_NAME(MyNamespace,HelloWorld)()

Выглядит довольно неудобно.

Ответ 4

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

(заголовок)

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

Ответ 5

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

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

Ответ 6

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

В качестве примера возьмем библиотеку Foobar.

foobar.h

#ifndef __FOOBAR_H__
#define __FOOBAR_H__

// definition of the namespace 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);
}

Ответ 7

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

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

Ответ 8

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