[〜#〜] c [〜#〜]プリプロセッサを使用して[〜#〜]の名前空間をエミュレートする方法はありますかc [〜#〜]?
私はこれらの線に沿って何かを考えています:
#define NAMESPACE name_of_ns
some_function() {
some_other_function();
}
これは次のように翻訳されます。
name_of_ns_some_function() {
name_of_ns_some_other_function();
}
名前空間プレフィックスを使用する場合、ヘッダーを含める前に#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(コメント)で説明されている名前空間マクロを使用するよりもクリーンで便利なソリューションです。
別の方法として、すべての関数を保持する構造体を宣言し、関数を静的に定義する方法があります。そうすれば、グローバル名構造体の名前の競合だけを心配する必要があります。
// 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
とmy_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 FUN_NAME(namespace,name) namespace ## name
関数を次のように宣言します。
void FUN_NAME(MyNamespace,HelloWorld)()
しかし、かなり厄介に見えます。
私は次のスキームを思いついた:
(ヘッダ)
// 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
構造ベースのアプローチを使用しますが、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);
}
_
Cを使用して名前空間やテンプレートを活用する方法に関するチュートリアルを作成しました。
基本的な名前空間の場合、慣例として名前空間名の前に付けることができます。
namespace MY_OBJECT {
struct HANDLE;
HANDLE *init();
void destroy(HANDLE * & h);
void do_something(HANDLE *h, ... );
}
として書くことができます
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, ... );
名前空間とテンプレートの概念を使用するために必要な2番目のアプローチは、マクロ連結とインクルードを使用することです。たとえば、私は作成することができます
template<T> T multiply<T>( T x, T y ) { return x*y }
次のようにテンプレートファイルを使用する
マルチプルテンプレート.h
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);
マルチプルテンプレート.c
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
return x*y;
}
これで、int_multiplyを次のように定義できます。この例では、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"
このすべての最後に、関数とヘッダーファイルがあります。
int int_multiply( int x, int y ) { return x * y }
提供されているリンクについて、より詳細なチュートリアルを作成しました。これが誰かの助けになることを願っています!
受け入れられている答えに似たアプローチは次のとおりです。
// 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();
上記のアプローチに基づいて構築され、ファンクと構造の両方でそれらを組み合わせて、擬似ネームスペースNAMESPACE1およびNAMESPACE2を作成する例を次に示します。関数を保持する構造を持つことに対するこの利点は、構造保持関数のアプローチが複数の疑似名前空間にまたがる標準化された構造を必要とすることであり、これは常に可能とは限りません(おそらく、またはおそらく間違いなく多くの作業なしで)コードを改善しない)または望ましい。
マクロの展開順序が問題になるかどうかはわかりませんが、これはGCCで機能し、適切な(理想からはほど遠い)可読性を維持しながら、必要なコード変更の量を最小限に抑えるようです。
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
Header2.hおよびapi2.cの他のコードは、header1.hおよびheader2.hと同じで、ネームスペース「NAMESPACE2」用に変更されています