webentwicklung-frage-antwort-db.com.de

Namensräume in C

Gibt es eine Möglichkeit (/ ab) denCPräprozessor zum Emulieren von Namespaces inCzu verwenden?

Ich denke etwas in diese Richtung:

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

Dies würde übersetzt werden in:

name_of_ns_some_function() {
    name_of_ns_some_other_function();
}
48
Kim Stebel

Bei der Verwendung von Namespace-Präfixen füge ich normalerweise Makros für die verkürzten Namen hinzu, die über #define NAMESPACE_SHORT_NAMES aktiviert werden können, bevor der Header eingefügt wird. Ein Header foobar.h könnte so aussehen:

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

Wenn ich kurze Namen in einer einschließenden Datei verwenden möchte, mache ich das

#define FOOBAR_SHORT_NAMES
#include "foobar.h"

Ich finde, dass dies eine sauberere und nützlichere Lösung ist, als die von Vinko Vrsalovic beschriebenen Namespace-Makros (in den Kommentaren).

47
Christoph

Eine andere Alternative wäre, eine Struktur zu deklarieren, die alle Ihre Funktionen enthält, und dann Ihre Funktionen statisch zu definieren. Dann müssen Sie sich nur um Namenskonflikte für die globale Namensstruktur sorgen.

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

Im obigen Beispiel können my_bar und my_baz nicht direkt von main.c aus aufgerufen werden, sondern nur über foo.

Wenn Sie über eine Reihe von Namespaces verfügen, die Funktionen mit denselben Signaturen deklarieren, können Sie die Namespace-Struktur für diese Gruppe Standardisieren und den zur Laufzeit zu verwendenden Namespace auswählen.

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

Die mehrfachen Definitionen von my_bar und my_baz stehen nicht miteinander in Konflikt, da sie statisch definiert sind. Die zugrunde liegenden Funktionen sind jedoch weiterhin über die entsprechende Namespace-Struktur zugänglich.

79
rampion

Sie können den Operator ## verwenden:

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

und Funktionen deklarieren als:

void FUN_NAME(MyNamespace,HelloWorld)()

Sieht ziemlich unbeholfen aus.

12
Mehrdad Afshari

Ich habe folgendes Schema entworfen:

(Header)

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

(Implementierung)

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

Ich verwende den strukturbasierten Ansatz mit zwei Verfeinerungen: Ich füge Substrukturen hinzu, um hierarchische Namespaces zu erstellen, und ich definiere einige einfache Makros, wenn ich den Pfad von Namespaces vereinfachen möchte.

Nehmen wir als Beispiel eine Foobar -Bibliothek.

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

Dann ist es möglich, den Namespace zu verwenden:

#include "foobar.h"

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

Es besteht jedoch kein so großer Unterschied zwischen foobar_text__print() und foobar.text.print(). Ich denke, der zweite ist lesbarer, aber fragwürdig. Daher ist es sehr nützlich, einige Makros zu definieren, um diese Namespaces zu vereinfachen:

#include "foobar.h"

#define txt    foobar.text
#define date   foobar.date

void main() {
    char *today = date.getDateString();
    txt.print(today);
}

Diese Art von hierarchischen Namespaces lässt sich schnell definieren, ist leicht verständlich und verringert die Ausführlichkeit von Code.


Nur zum Spaß sind hier die Dateien für foobar.text-Code:

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);
}
4
Amaury Bouchard

Ich habe ein Tutorial geschrieben, wie man Namespaces und/oder Templates mit C nutzen kann. 

Namensräume und Vorlagen in C

Namespaces und Vorlagen in C (mit verknüpften Listen)

Für den Basisnamensraum kann der Namensraumname einfach als Konvention vorangestellt werden.

namespace MY_OBJECT {
  struct HANDLE;
  HANDLE *init();
  void destroy(HANDLE * & h);

  void do_something(HANDLE *h, ... );
}

kann als geschrieben werden 

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

Eine zweite Herangehensweise, die das Konzept des Namensraums und der Vorlagen verwendet, ist die Verwendung der Makrokettenfolge und des Include. Zum Beispiel kann ich ein erstellen 

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

schablonendateien wie folgt verwenden

multiplizieren-template.h

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

multiplizieren-template.c

_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
  return x*y;
}

Wir können nun int_multiply wie folgt definieren. In diesem Beispiel werde ich eine int_multiply.h/.c-Datei erstellen.

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"

Am Ende haben Sie eine Funktions- und Header-Datei für.

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

Ich habe ein ausführlicheres Tutorial zu den Links erstellt. Hoffentlich hilft das jemandem! 

4
Andy Curtis

Eine der akzeptierten Antwort ähnliche Vorgehensweise ist die folgende:

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

diese Header-Datei wird mit einer .c-Datei geliefert:

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

bei Verwendung der Funktionen

foobar.some_func(10);
foobar.other_func();
3
Earth Engine

hier ist ein Beispiel, das sich aus den oben genannten Ansätzen zusammensetzt und diese für Funktionen und Strukturen kombiniert, um die Pseudo-Namespaces NAMESPACE1 und NAMESPACE2 zu erstellen. Der Vorteil gegenüber einer Struktur, die Funktionen enthält, besteht darin, dass der Structure-Holding-Functions-Ansatz eine standardisierte Struktur über mehrere Pseudo-Namespaces hinweg erfordert, und dies ist nicht immer möglich (entweder überhaupt nicht oder ohne viel Arbeit, die möglicherweise funktioniert Code nicht verbessern) oder wünschenswert. 

Nicht sicher, ob die Reihenfolge der Makroerweiterung ein Problem darstellen kann, dies funktioniert jedoch bei GCC und scheint die Anzahl der erforderlichen Codeänderungen zu minimieren, während gleichzeitig eine anständige (wenn auch alles andere als ideale) Lesbarkeit erhalten bleibt. 


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

Anderer Code in header2.h und api2.c ist identisch mit header1.h und header2.h, geändert für den Namespace "NAMESPACE2".

0
mwag