Espacios de nombres en C

¿Hay alguna manera de (ab) usar el preprocesador C para emular espacios de nombres en C ?

Estoy pensando algo así:

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

Esto se traduciría a:

 name_of_ns_some_function() { name_of_ns_some_other_function(); } 

Cuando uso prefijos de espacio de nombres, normalmente agrego macros para los nombres abreviados que se pueden activar a través de #define NAMESPACE_SHORT_NAMES antes de incluir el encabezado. Un encabezado foobar.h podría verse así:

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

Si quiero usar nombres cortos en un archivo incluido, lo haré

 #define FOOBAR_SHORT_NAMES #include "foobar.h" 

Considero que esta es una solución más limpia y más útil que usar macros de espacio de nombres como lo describe Vinko Vrsalovic (en los comentarios).

Otra alternativa sería declarar una estructura para mantener todas sus funciones, y luego definir sus funciones estáticamente. Entonces solo tendrías que preocuparte por los conflictos de nombres para la estructura de nombre global.

 // 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  #include "foo.h" int main(void) { foo.baz(); printf("%d", foo.bar(3, "hello")); return 0; } 

En el ejemplo anterior, my_bar y my_baz no pueden my_baz directamente desde main.c, solo a través de foo .

Si tiene un montón de espacios de nombres que declaran funciones con las mismas firmas, puede estandarizar su estructura de espacio de nombres para ese conjunto y elegir qué espacio de nombres usar en tiempo de ejecución.

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

Las múltiples definiciones de my_bar y my_baz no entran en conflicto, ya que están definidas estáticamente, pero las funciones subyacentes todavía son accesibles a través de la estructura apropiada del espacio de nombres.

Podría usar el operador ##:

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

y declarar funciones como:

 void FUN_NAME(MyNamespace,HelloWorld)() 

Aunque parece bastante incómodo.

Se me ocurrió el siguiente esquema:

(encabezamiento)

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

(implementación)

 #define _IMPL #include "header.h" #undef __IMPL 

Utilizo el enfoque basado en estructuras, con dos refinamientos: agrego subestructuras para crear espacios de nombres jerárquicos, y defino algunas macros simples cuando quiero simplificar la ruta de los espacios de nombres.

Tomemos una biblioteca de Foobar como ejemplo.

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

Entonces, es posible usar el espacio de nombres:

 #include "foobar.h" void main() { foobar.text.print("it works"); } 

Pero no hay mucha diferencia entre foobar_text__print() y foobar.text.print() . Creo que el segundo es más legible, pero es cuestionable. Por lo tanto, se vuelve realmente útil al definir algunas macros para simplificar estos espacios de nombres:

 #include "foobar.h" #define txt foobar.text #define date foobar.date void main() { char *today = date.getDateString(); txt.print(today); } 

Este tipo de espacios de nombres jerárquicos es rápido de definir, fácil de entender y disminuir la verbosidad del código.


Solo por diversión, aquí están los archivos para el código 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  #include "foobar_text.h" void foobar_text__print(char *s) { printf("%s\n", s); } 

Un enfoque similar a la respuesta aceptada es el siguiente:

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

este archivo de encabezado vendrá con un archivo .c:

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

cuando se usan las funciones,

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

Escribí un tutorial sobre cómo aprovechar los espacios de nombres y / o plantillas usando C.

Espacios de nombres y plantillas en C

Espacios de nombres y plantillas en C (usando listas enlazadas)

Para el espacio de nombres básico, uno simplemente puede prefijar el nombre del espacio de nombres como una convención.

 namespace MY_OBJECT { struct HANDLE; HANDLE *init(); void destroy(HANDLE * & h); void do_something(HANDLE *h, ... ); } 

Se puede escribir como

 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, ... ); 

Un segundo enfoque que he necesitado que utiliza el concepto de espacios de nombres y plantillas es usar la macroconcatenación e incluir. Por ejemplo, puedo crear un

 template T multiply( T x, T y ) { return x*y } 

usando archivos de plantilla de la siguiente manera

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

Ahora podemos definir int_multiply de la siguiente manera. En este ejemplo, crearé un archivo 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" 

Al final de todo esto, tendrá una función y un archivo de encabezado para.

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

Creé un tutorial mucho más detallado sobre los enlaces proporcionados. ¡Espero que esto ayude a alguien!

Aquí hay un ejemplo que se basa en los enfoques anteriores y los combina tanto para los funcs como para las estructuras para crear pseudo-namespaces NAMESPACE1 y NAMESPACE2. el beneficio de esto sobre tener una estructura que tiene funciones es que el enfoque de estructura-funciones-retención requiere una estructura estandarizada a través de múltiples pseudo-espacios de nombres, y esto no siempre es posible (en absoluto, o sin mucho trabajo que podría decirse que no mejora el código) o deseable.

No estoy seguro de si el orden de expansión de macros podría ser un problema, pero esto funciona en GCC y parece minimizar la cantidad de cambios de código necesarios, al tiempo que mantiene una legibilidad decente (aunque no ideal).


application.c:

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

Otro código en header2.h y api2.c es lo mismo que header1.h y header2.h, modificado para el espacio de nombres “NAMESPACE2”