Existe-t-il un moyen (ab) d'utiliser le préprocesseurCpour émuler les espaces de noms dansC?
Je pense à quelque chose dans ce sens:
#define NAMESPACE name_of_ns
some_function() {
some_other_function();
}
Cela serait traduit en:
name_of_ns_some_function() {
name_of_ns_some_other_function();
}
Lorsque j'utilise des préfixes d'espaces de noms, j'ajoute normalement des macros pour les noms abrégés pouvant être activées via #define NAMESPACE_SHORT_NAMES
avant l'inclusion de l'en-tête. Un en-tête foobar.h pourrait ressembler à ceci:
// 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 je veux utiliser des noms abrégés dans un fichier inclus, je ferai
#define FOOBAR_SHORT_NAMES
#include "foobar.h"
Je trouve cette solution plus propre et plus utile que d’utiliser des macros d’espace de nommage comme décrit par Vinko Vrsalovic (dans les commentaires).
Une autre alternative serait de déclarer une structure contenant toutes vos fonctions, puis de définir vos fonctions de manière statique. Dans ce cas, vous ne devez vous soucier que des conflits de noms pour la structure de nom globale.
// 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;
}
Dans l'exemple ci-dessus, my_bar
et my_baz
ne peuvent pas être appelés directement à partir de main.c, uniquement via foo
.
Si vous avez plusieurs espaces de noms qui déclarent des fonctions avec les mêmes signatures, vous pouvez alors normaliser votre structure d'espaces de noms pour cet ensemble et choisir quel espace de noms utiliser au moment de l'exécution.
// 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;
}
Les définitions multiples de my_bar
et my_baz
ne sont pas en conflit, car elles sont définies de manière statique, mais les fonctions sous-jacentes sont toujours accessibles via la structure d'espace de nom appropriée.
Vous pouvez utiliser l'opérateur ##:
#define FUN_NAME(namespace,name) namespace ## name
et déclarer les fonctions en tant que:
void FUN_NAME(MyNamespace,HelloWorld)()
Cela semble plutôt bizarre.
Je suis venu avec le schéma suivant:
(entête)
// 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
(la mise en oeuvre)
#define _IMPL
#include "header.h"
#undef __IMPL
J'utilise l'approche basée sur la structure, avec deux améliorations: j'ajoute des sous-structures pour créer des espaces de noms hiérarchiques et je définit des macros simples lorsque je souhaite simplifier le chemin d'accès aux espaces de noms.
Prenons par exemple une bibliothèque 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
}
};
Ensuite, il est possible d'utiliser l'espace de noms:
#include "foobar.h"
void main() {
foobar.text.print("it works");
}
Mais il n'y a pas tellement de différence entre foobar_text__print()
et foobar.text.print()
. Je pense que le second est plus lisible, mais c'est discutable. Il devient donc très utile de définir des macros pour simplifier ces espaces de noms:
#include "foobar.h"
#define txt foobar.text
#define date foobar.date
void main() {
char *today = date.getDateString();
txt.print(today);
}
Ce type d'espaces de noms hiérarchiques est rapide à définir, facile à comprendre et à réduire la verbosité du code.
Juste pour le plaisir, voici les fichiers pour le code 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);
}
J'ai écrit un tutoriel sur la façon de tirer parti des espaces de noms et/ou des modèles en utilisant C.
Espaces de noms et modèles en C
Espaces de noms et modèles en C (à l'aide de listes chaînées)
Pour l'espace de nom de base, on peut simplement préfixer le nom de l'espace de nom en tant que convention.
namespace MY_OBJECT {
struct HANDLE;
HANDLE *init();
void destroy(HANDLE * & h);
void do_something(HANDLE *h, ... );
}
peut être écrit comme
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, ... );
Une deuxième approche dont j'ai eu besoin et qui utilise le concept d'espacement de noms et de modèles consiste à utiliser la concaténation de macros et l'inclure. Par exemple, je peux créer un
template<T> T multiply<T>( T x, T y ) { return x*y }
en utilisant les fichiers modèles comme suit
multiplier-template.h
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);
multiplier-template.c
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
return x*y;
}
Nous pouvons maintenant définir int_multiply comme suit. Dans cet exemple, je vais créer un fichier 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"
À la fin de tout cela, vous aurez un fichier de fonction et d’en-tête pour.
int int_multiply( int x, int y ) { return x * y }
J'ai créé un tutoriel beaucoup plus détaillé sur les liens fournis. Espérons que cela aide quelqu'un!
Une approche similaire à la réponse acceptée est la suivante:
// 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
ce fichier d'en-tête doit être accompagné d'un fichier .c:
#include "foobar.h"
struct _foobar foobar = {
foobar_some_func;
foobar_other_func;
};
en utilisant les fonctions,
foobar.some_func(10);
foobar.other_func();
voici un exemple qui construit les approches ci-dessus et les combine pour les fonctions et les structures afin de créer des pseudo-espaces-noms NAMESPACE1 et NAMESPACE2. L'avantage de ceci par rapport à une structure qui maintient des fonctions est que l'approche des fonctions de maintien de structure nécessite une structure normalisée sur plusieurs pseudo-espaces de noms, ce qui n'est pas toujours possible pas améliorer le code) ou souhaitable.
Je ne sais pas si l'ordre d'extension des macros pourrait poser problème, mais cela fonctionne sur GCC et semble minimiser le nombre de modifications de code nécessaires, tout en maintenant une lisibilité décente (bien que loin d'être idéale).
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
L'autre code dans header2.h et api2.c est identique à header1.h et header2.h, modifié pour l'espace de noms "NAMESPACE2".