2

クラス コンストラクターのパラメーターの数と型を特定するにはどうすればよいでしょうか。メンバー関数に対してこれを行うのは簡単なことです。

template <class T, typename P0, typename P1, typename P2, typename P3>
void BindNativeMethod( void (T::*MethodPtr)(P0, P1, P2, P3) )
{
   // we've got 4 params
   // use them this way:
   std::vector<int> Params;
   Params.push_back( TypeToInt<P0>() );
   Params.push_back( TypeToInt<P1>() );
   Params.push_back( TypeToInt<P2>() );
   Params.push_back( TypeToInt<P3>() );
}

template <class T, typename P0, typename P1, typename P2, typename P3, typename P4>
void BindNativeMethod( void (T::*MethodPtr)(P0, P1, P2, P3, P4) )
{
   // we've got 5 params
   // use them this way:
   std::vector<int> Params;
   Params.push_back( TypeToInt<P0>() );
   Params.push_back( TypeToInt<P1>() );
   Params.push_back( TypeToInt<P2>() );
   Params.push_back( TypeToInt<P3>() );
   Params.push_back( TypeToInt<P4>() );
}

他のメンバーについても同様です。

しかし、クラス コンストラクターをどうするのでしょうか。引数の型を調べる方法はありますか? コンストラクターのアドレスを取得することさえ不可能であるため、これを解決するための根本的に異なるアプローチがあるのではないでしょうか?

編集:すべてのソースファイルをスキャンし、すべてのクラス、メソッド、ctor、およびそれらの正確なプロトタイプのデータベースを持つ C++ プリプロセッサがあります。これに基づいていくつかのスタブを生成する必要があります。

4

2 に答える 2

5

要件を正しく理解している場合は、1つまたは複数のコンストラクターにパラメーターのタイプを通知するアドレスを取得できる関数が必要です。

#include <string>
#include <new>

template <typename T, typename P0, typename P1>
T * fake_ctor (T *mem, P0 p0, P1 p1) {
    return mem ? new (mem) T(p0, p1) : 0;
}

struct Foo {
    Foo (int, int) {}
    Foo (int, std::string) {}
};

template <class T, typename P0, typename P1>
void BindClassCtor(T *(*FakeCtor)(T *, P0, P1)) {
    std::vector<int> Params;
    Params.push_back( TypeToInt<P0>() );
    Params.push_back( TypeToInt<P1>() );
}

// PARSER GENERATED CALLS
BindClassCtor<Foo, int, int>(&fake_ctor);
BindClassCtor<Foo, int, std::string>(&fake_ctor);

fake_ctorを実際に呼び出すように実装するとctorfake_ctorそれ自体が呼び出されることはありませんが)、コンパイル時の健全性のレベルが提供されます。Foo呼び出しを再生成せずにコンストラクターの1つを変更するとBindClassCtor、コンパイル時エラーが発生する可能性があります。

編集:ボーナスとして、可変個引数を持つテンプレートを使用してパラメーターのバインドを簡略化しました。まず、BindParamsテンプレート:

template <typename... T> struct BindParams;

template <typename T, typename P0, typename... PN>
struct BindParams<T, P0, PN...> {
    void operator () (std::vector<int> &Params) {
        Params.push_back( TypeToInt<P0>() );
        BindParams<T, PN...>()(Params);
    }
};

template <typename T>
struct BindParams<T> {
    void operator () (std::vector<int> &Params) {}
};

これで、fake_ctorクラスのコレクションになりました。これにより、各クラスを可変個引数パラメーターリストでインスタンス化できます。

template <typename... T> struct fake_ctor;

template <typename T>
class fake_ctor<T> {
    static T * x (T *mem) {
        return mem ? new (mem) T() : 0;
    }
    decltype(&x) y;
public:
    fake_ctor () : y(x) {}
};

template <typename T, typename P0>
class fake_ctor<T, P0> {
    static T * x (T *mem, P0 p0) {
        return mem ? new (mem) T(p0) : 0;
    }
    decltype(&x) y;
public:
    fake_ctor () : y(x) {}
};

template <typename T, typename P0, typename P1>
class fake_ctor<T, P0, P1> {
    static T * x (T *mem, P0 p0, P1 p1) {
        return mem ? new (mem) T(p0, p1) : 0;
    }
    decltype(&x) y;
public:
    fake_ctor () : y(x) {}
};

そして今、バインダー関数は単純にこれです:

template <typename... T>
void BindClassCtor (fake_ctor<T...>) {
    std::vector<int> Params;
    BindParams<T...>()(Params);
}

以下は、4つのコンストラクターを持つコンストラクター引数バインディングの図ですBar

struct Bar {
    Bar () {}
    Bar (int) {}
    Bar (int, int) {}
    Bar (int, std::string) {}
};

BindClassCtor(fake_ctor<Bar>());
BindClassCtor(fake_ctor<Bar, int>());
BindClassCtor(fake_ctor<Bar, int, int>());
BindClassCtor(fake_ctor<Bar, int, std::string>());
于 2012-07-25T06:00:38.020 に答える
1

この問題の回避策を提案します。私はよく「静的インスタンスコンストラクター」パターンを使用します。たとえば、サブクラス化されているクラスのインスタンスを逆シリアル化する場合などです。

class BaseClass
{
private:
    BaseClass();
    virtual void InternalLoad(MyStream s) = 0;

public:
    static BaseClass * Create(MyStream s);
};

(...)

BaseClass * BaseClass::Create(MyStream s);
{
    int type;
    s >> type;

    if (type == 0)
    {
        BaseClass * result = new DerivedClass1(); // DerivedClass1 : public BaseClass
        result->InternalLoad(s);
        return result;
    }
    else if (type == 1)
    ...
}

このパターンは、ケースで簡単に使用できます。静的インスタンスコンストラクターを作成するだけです。これは汎用であり、パラメーターを希望どおりに処理します。


編集:

私はあなたが次の方法で問題を解決できると思います:

class MyClass
{
private:
    MyClass();

public:
    template <typename T1, typename T2>
    static MyClass * Create(T1 t, T2 u);
};

(...)

template <typename T1, typename T2>
MyClass * MyClass::Create(T1 t, T2 u)
{
    MyClass result = new MyClass();
    // Process parameters t and u here
    return result;
}

次に、次の方法でクラスを作成できます。

MyClass* class = MyClass::Create(5, "Indiana Jones");

これは解決策ではありませんが、(非常にエレガントだと思いますが)回避策であることに注意してください。


編集:

私は今理解していると思いますが、コンストラクターの代わりに静的メソッドを使用して何をしようとしているのかは、実際には問題の回避策として証明される可能性があります(ただし、コンストラクターパラメーターに関する情報をどのように処理するかによって異なります) )。

明らかに、次のように、通常のパラメーターを使用して静的コンストラクターを実装する必要があります。

class MyClass
{
private:
    MyClass();

public:
    static MyClass * Create(int t, char * u);
};

静的関数へのポインターを取得して、ツールメソッドに渡すことができるはずです。


編集:

あなたが本当に何をしたいのかを説明したので、先に提案したように、クラスファクトリ(推奨)または静的コンストラクタを使用することをお勧めします。このようにして、必要なメソッド(静的かどうかに関係なく)へのポインターをいつでも取得して、現在のアーキテクチャーで使用できます(提示したコードに基づいて)。

また、C#/11のstd::function<>とlambdasも見てください。私は、それらがあなたのアーキテクチャをはるかに単純にするかもしれないと思います。

于 2012-07-23T05:57:54.693 に答える