3

私はこのようなコードを持っています:

template<> const string &Wrapper<Foo>::s_Name = "Foo";
template<> const Binding Wrapper<Foo>::s_Bindings[] = {
    Binding("m1", &caller<&Foo::f1>),
    Binding("m2", &caller<&Foo::f2>),
    Binding("m3", &caller<&Foo::f1>),
    Binding("outer", &caller<&outer>),
};
template<> const int Wrapper<Foo>::s_BindingsLength =
    (sizeof(ArraySizeHelper(s_Bindings))); 

ArraySizeHelperは、コンパイル時に配列サイズを計算します。非メンバー関数とメンバー関数をバインドできます。これらのバインディングを作成する時間を節約するために、いくつかのマクロを作成しました。

#define BIND_START(Class) \
    namespace _Bind##Class##Namespace { \
        typedef Class _BindClass; \
        template<> const string &Wrapper<_BindClass>::s_Name = #Class; \
        template<> const Binding Wrapper<_BindClass>::s_Bindings[] = {

#define BIND(FunctionName, Function) \
            Binding(FunctionName, &caller<Function>),

#define BIND_END \
        };\
        template<> const int Wrapper<_BindClass>::s_BindingsLength = \
            (sizeof(ArraySizeHelper(s_Bindings))); \
    }

上記のコードは次のように記述できます。

BIND_START(Foo)
    BIND("m1", &Foo::f1)
    BIND("m2", &Foo::f2)
    BIND("m3", &Foo::f1)
    BIND("outer", &outer)
BIND_END

入力と読み取りがはるかに簡単です。なぜ名前空間に入れる必要があるのですか?クラス名を一度だけ(メンバー関数ポインターを除いて)記述し、それを複数のクラスで使用する必要があるような方法でこれらのマクロを記述する別の方法を見つけることができないためです。そして今、私は疑問に思っています、それをしても大丈夫ですか?そうでない場合、私が望む機能を実装する他の方法はありますか?

Pastebinでのこの例の完全なコード


GCC 4.7.2のバグのようです(他のバージョンについてはわかりません)

このコードは9.4.2/2に違反しています。「静的データメンバーの定義は、メンバーのクラス定義を囲む名前空間スコープに表示されます。」
バグレポートは次のとおりです。GCCBugzilla–バグ56119

4

1 に答える 1

2

あなたのソリューションはC++11標準によって禁止されているようです。これは9.4.2/2が言うことです:

「クラス定義での静的データメンバーの宣言は定義ではなく、cv-qualified void以外の不完全なタイプである可能性があります。静的データメンバーの定義は、メンバーのクラス定義を囲む名前空間スコープに表示されるものとします。 」

これで、静的メンバーデータを定義する名前空間(その名前は_Bind##Class##Namespaceプリプロセッサ式の評価の結果です)には、クラスが定義される名前空間(この場合はグローバル名前空間)が含まれません。したがって、静的データメンバーの定義は不正です。

使用しているコンパイラはわかりませんが、それをコンパイルする場合はバグです(たとえば、GCC 4.7.2の場合はそうです)。Clang 3.2はそれを正しくコンパイルすることを拒否し、正しい出力メッセージを生成します。

source.cpp:106:1: error: cannot define or redeclare 's_Name' here because namespace '_BindFooNamespace' does not enclose namespace 'Wrapper<Foo>' BIND_START(Foo)

代替ソリューションについては、の展開でクラス名が必要である限り、存在しないと思いますBIND_END(これは、のコンパイル時の初期化に必要なものですs_BindingsLength)。マクロが引数としてクラス名を取得しない場合、実際には、その名前はとして使用可能である必要があります(これは、マクロがディレクティブtypedefに展開できる場合に当てはまりますが、そうではありません)。#defineクラスの名前を形成する方法についてのヒントがないため、常に同じBIND_END名前を探す必要があります。また、名前が常に同じである必要がある場合は、名前の衝突を避けるために、別の名前空間に配置する必要があります。しかし、それは上記の基準の規則によって禁じられています。

つまり、マクロに1つの引数を追加する必要があるのではないかと思います。BIND_END結局のところ、これはそれほど悪くはありません。

于 2013-01-26T15:25:41.420 に答える