Przestrzenie nazw w C

Czy istnieje sposób, aby (ab)użyć preprocesora C do emulowania przestrzeni nazw w C ?

Myślę o czymś w tym stylu:

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

To zostałoby przetłumaczone na:

name_of_ns_some_function() {
    name_of_ns_some_other_function();
}
Author: Brian Tompsett - 汤莱恩, 2008-12-23

10 answers

Używając prefiksów przestrzeni nazw, Zwykle dodaję makra dla skróconych nazw, które można aktywować za pomocą #define NAMESPACE_SHORT_NAMES przed dodaniem nagłówka. Foobar nagłówka.h może wyglądać tak:

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

Jeśli chcę użyć krótkich nazw w pliku zawierającym, zrobię

#define FOOBAR_SHORT_NAMES
#include "foobar.h"

Uważam, że jest to czystsze i bardziej użyteczne rozwiązanie niż używanie makr przestrzeni nazw opisanych przez Vinko Vrsalovica (w komentarzach).

 54
Author: Christoph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-23 10:28:46

Inną alternatywą byłoby zadeklarowanie struktury przechowującej wszystkie funkcje, a następnie zdefiniowanie funkcji statycznie. Wtedy będziesz musiał martwić się tylko o konflikty nazw dla globalnej struktury nazw.

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

W powyższym przykładzie, my_bar i my_baz nie mogą być wywołane bezpośrednio z main.c, tylko przez foo.

Jeśli masz kilka przestrzeni nazw, które deklarują funkcje z tymi samymi podpisami, możesz standaryzować twoja struktura przestrzeni nazw dla tego zestawu i wybierz, która przestrzeń nazw do użycia w czasie wykonywania.

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

Wielokrotne definicje my_bar i my_baz nie są ze sobą sprzeczne, ponieważ są zdefiniowane statycznie, ale podstawowe funkcje są nadal dostępne poprzez odpowiednią strukturę przestrzeni nazw.

 87
Author: rampion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-23 20:21:54

Możesz użyć operatora##:

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

I zadeklarować funkcje jako:

void FUN_NAME(MyNamespace,HelloWorld)()
Wygląda dość niezręcznie.
 13
Author: mmx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-23 19:32:23

Wymyśliłem następujący schemat:

(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

(realizacja)

#define  _IMPL 
#include "header.h"
#undef   __IMPL
 8
Author: Norswap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-10 22:30:15

Używam podejścia opartego na strukturach, z dwoma udoskonaleniami: dodaję podstruktury do tworzenia hierarchicznych przestrzeni nazw i definiuję kilka prostych makr, gdy chcę uprościć ścieżkę do przestrzeni nazw.

Weźmy jako przykład bibliotekę 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
    }
};

Wtedy możliwe jest użycie przestrzeni nazw:

#include "foobar.h"

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

Ale nie ma tak dużej różnicy między foobar_text__print() i foobar.text.print(). Myślę, że druga jest bardziej czytelne, ale wątpliwe. Tak więc stało się naprawdę przydatne, definiując niektóre makra, aby uprościć te przestrzenie nazw:

#include "foobar.h"

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

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

Tego rodzaju hierarchiczne przestrzenie nazw są szybkie do zdefiniowania, łatwe do zrozumienia i zmniejszają szczegółowość kodu.


Dla Zabawy, oto pliki dla foobar.text kod:

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
Author: Amaury Bouchard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-25 20:28:22

Napisałem tutorial jak wykorzystać przestrzenie nazw i / lub szablony używając C.

Przestrzenie nazw i szablony w C

Przestrzenie nazw i szablony w C (używając list linkowanych)

Dla podstawowej przestrzeni nazw, można po prostu przedrostek nazwy przestrzeni nazw jako konwencję.

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

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

Można zapisać jako

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

Drugim podejściem, którego potrzebowałem, wykorzystującym koncepcję przestrzeni nazw i szablonów, jest użycie konkatenacji makr i include. Na przykład, mogę utworzyć

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

Używanie plików szablonów w następujący sposób

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

Możemy teraz zdefiniować int_multiply w następujący sposób. W tym przykładzie utworzę int_multiply.h/.plik 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"

Na końcu tego wszystkiego, będziesz miał funkcję i plik nagłówka dla.

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

Stworzyłem o wiele bardziej szczegółowy tutorial na podane linki. Mam nadzieję, że to komuś pomoże!

 4
Author: Andy Curtis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-29 22:43:55

Podejście podobne do zaakceptowanej odpowiedzi jest następujące:

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

Ten plik nagłówkowy powinien zawierać .plik c:

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

Podczas korzystania z funkcji,

foobar.some_func(10);
foobar.other_func();
 3
Author: Earth Engine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-14 04:00:18

Możesz użyć helpera #define makro:

#include <stdio.h>

#define ns(x) gargantua_ ## x

struct ns(stats) {
    int size;
};

int ns(get_size)(struct ns(stats) *st) {
    return st->size;
}

void ns(set_size)(struct ns(stats) *st, int sz) {
    st->size = sz;
}

int main(void) {
    struct ns(stats) stats = {0};

    ns(set_size)(&stats, 3);
    printf("size=%d\n", ns(get_size)(&stats));
    return 0;
}

Uruchomienie go przez preprocesor daje:

struct gargantua_stats {
    int size;
};

int gargantua_get_size(struct gargantua_stats *st) {
    return st->size;
}

void gargantua_set_size(struct gargantua_stats *st, int sz) {
    st->size = sz;
}

int main(void) {
    struct gargantua_stats stats = {0};

    gargantua_set_size(&stats, 3);
    printf("size=%d\n", gargantua_get_size(&stats));
    return 0;
}
 1
Author: Gargantua,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-02 22:19:04

Oto przykład, który opiera się na powyższych podejściach i łączy je zarówno dla funkcji, jak i struktur, aby utworzyć pseudo-przestrzenie nazw NAMESPACE1 i NAMESPACE2. korzyść z tego, że struktura przechowuje funkcje, polega na tym, że podejście structure-holding-functions wymaga znormalizowanej struktury w wielu pseudo-przestrzeniach nazw, a nie zawsze jest to możliwe (albo w ogóle, albo bez dużo pracy, która prawdopodobnie nie poprawia kodu) lub pożądane.

Nie jestem pewien, czy kolejność rozszerzania makr może być problemem, ale działa to na GCC i wydaje się minimalizować ilość wymaganych zmian kodu, przy zachowaniu przyzwoitej (choć dalekiej od idealnej) czytelności.


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

}

Nagłówek 1.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

Inny kod w nagłówku2.h i api2.c jest tym samym co nagłówek 1.h i header2.h, modified for namespace"NAMESPACE2"

 0
Author: mwag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-04 21:04:25

Zdaję sobie sprawę, że jest to stare pytanie (11 lat), ale starałem się osiągnąć zasadniczo to, co myślę, że chciałeś pierwotnie, jak wymienione powyżej.

Chciałem, aby przestrzeń nazw była poprzedzona moimi funkcjami. Ale chciałem mieć możliwość zmiany tej przestrzeni nazw. Domyślnie chciałem, aby ten przykład nie miał przestrzeni nazw, ale jeśli doszło do kolizji nazw, chciałem możliwość rozszerzenia przestrzeni nazw do wszystkich funkcji w mojej bibliotece. (To nieco wstecz w porównaniu do C++, gdzie domyślnie istnieje przestrzeń nazw i używasz using namespace whatever, Aby usunąć konieczność każdorazowego określania przestrzeni nazw.) Jednak, podobnie jak w C++, jeśli upuścisz using namespace oświadczenie i alias kodu, będziesz musiał zaktualizować swój kod wywołujący. Możesz też napisać inną sekwencję makr do automatycznej zmiany nazw połączeń, ale jest to poza zakresem tego, czego myślę, że szukałeś.

#include <stdio.h>

#define NAMESPACE(...) test_ //Use this as my prepender

//Where all the magic happens which could be included in a header file.
#ifndef NAMESPACE
//No Namespace by default
#define NAMESPACE(...)
#endif

//Actual replacements
#define NSPREPENDER(...) NSPROCESSING(NAMESPACE(), __VA_ARGS__)
#define NSPROCESSING(...) NSFINALIZE(__VA_ARGS__)
#define NSFINALIZE(a,b) a ## b


//BEGIN ACTUAL PROGRAM
//Prototype
void NSPREPENDER(myprint)();

int main()
{
    test_myprint(); //If NAMESPACE(...) is defined to anything else, this code must change.

    return 0;
}

//Implementation
void NSPREPENDER(myprint)()
{
    puts("Testing");
}

Ten kod będzie kompilowany tylko na C99 i w górę, ponieważ używa zmienne makra. Makra te wykonują formę rekurencji, która jest wykonywana tak, że możemy pobrać wartość z makra zdefiniowanego na górze.

Podział wszystkiego, co działa:

  • definiujemy, że chcemy, aby nasza przestrzeń nazw była.
  • Jeśli nic nie jest zdefiniowane ustaw wartość domyślną
  • wykonaj kilka wywołań by obejść i (ab)użyj funkcji preprocesora.
  • Dodaj funkcję makra nsprepender do każdej funkcji c, aby można ją było nazwać zniekształconą.
  • napisz kod używając zniekształcone nazwy, ponieważ nazwa będzie poprawnie zniekształcona do czasu, gdy kompilator ją zobaczy.

Ten kod był testowany z clang.

 0
Author: Questionable,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-12 15:49:31