Cの名前空間をエミュレートするためにCプリプロセッサを (ab) 使用する方法はありますか?
私はこれらの線に沿って何かを考えています:
#define NAMESPACE name_of_ns
some_function() {
some_other_function();
}
これは次のように変換されます。
name_of_ns_some_function() {
name_of_ns_some_other_function();
}
Cの名前空間をエミュレートするためにCプリプロセッサを (ab) 使用する方法はありますか?
私はこれらの線に沿って何かを考えています:
#define NAMESPACE name_of_ns
some_function() {
some_other_function();
}
これは次のように変換されます。
name_of_ns_some_function() {
name_of_ns_some_other_function();
}
もう 1 つの方法は、すべての関数を保持する構造体を宣言してから、関数を静的に定義することです。そうすれば、グローバル名構造体の名前の競合についてのみ心配する必要があります。
// 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
andmy_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
ませんが、基になる関数には適切な名前空間構造体を介してアクセスできます。
#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 (コメント内) によって説明されているように、名前空間マクロを使用するよりもクリーンで便利なソリューションだと思います。
## 演算子を使用できます。
#define FUN_NAME(namespace,name) namespace ## name
関数を次のように宣言します。
void FUN_NAME(MyNamespace,HelloWorld)()
かなり厄介に見えますが。
私は構造体ベースのアプローチを使用しますが、2 つの改良点があります。下位構造を追加して階層的な名前空間を作成し、名前空間のパスを単純化したい場合はいくつかの単純なマクロを定義します。
例として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
}
};
次に、名前空間を使用できます。
#include "foobar.h"
void main() {
foobar.text.print("it works");
}
しかし、 と の間にはそれほど大きな違いはありませfoobar_text__print()
んfoobar.text.print()
。2 番目の方が読みやすいと思いますが、疑問があります。したがって、いくつかのマクロを定義してこれらの名前空間を簡素化することで、非常に便利になります。
#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);
}
私は次のスキームを思いつきました:
(ヘッダ)
// 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
受け入れられた回答に似たアプローチは次のとおりです。
// 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();