4

文字列入力でさまざまな関数を呼び出す方法を探しています。

それぞれの一意の文字列を関数ポインタとルックアップ関数に結び付けてマップを検索し、見つかった場合はポインタを返すマップがあります。

ここでの秘訣は、可能であれば、異なるシグネチャを持つ、少なくとも異なる戻り型を持つ関数へのポインタを格納して返す方法が必要です。

使用法は次のようになります。

ネットワークソケットから文字列入力を取得します->見つかった関数を見つけて実行します->結果をソケットに直接戻し、シリアル化して送信します。実際に何が起こったのかは気にしません。

これは実行可能ですか?そうでない場合、このタスクにどのようにアプローチしますか?

4

7 に答える 7

4

これは、さまざまな方法でボイラープレートコードを使用して実行できます。署名の数が十分に少ない場合は、関数ポインターの複数のベクトル(関数の型ごとに1つ)を保持し、関数名を型識別子(ベクトルの選択に使用)とベクトル内の位置にマップするマップを保持できます。

2番目のオプションは、を格納することboost::variantです(ここでも、署名のセットが小さい場合)。(格納されている関数型ごとに)関数を評価し、結果を生成するビジターオブジェクトを提供する必要があります。タイプはタイプによって管理されるboost::variantため、タイプタグをマップに保存する必要はありません。

また、完全型消去を使用して、呼び出される関数のタイプを決定するタグとboost::any関数ポインターを格納するオブジェクトをマップに格納することもできます。タイプ情報を使用してポインタを取得し、関数を実行できますが、switch関数タイプに基づいて手動で処理する必要があります。

一方、最も簡単なアプローチは、固定インターフェースを持つアダプターを作成することです。次に、アダプタへのポインタをマップに保存します。

于 2012-10-18T14:06:18.543 に答える
2

異なる関数ポインタを格納することはできませんが、それらの関数を含むオブジェクトを格納することはできます。

#include <iostream>
#include <cmath>
#include <map>
#include <string>
using namespace std;

class Functor{
public:
    template<class T>
    void operator()(T data){}
};
template<class T>
class BaseFunctor : public Functor{
public:
    virtual void CallFunction(T data){ }
};
class FunctionPointer1 : public BaseFunctor<void *>{
public:
    void doFunction1(){
        cout << "Do Function 1"<<endl;
    }
    template<class T>
    void CallFunction(T data){ doFunction1(); }
    template<class T>
    void operator()(T data){ this->CallFunction(data); }
};

class FunctionPointer2 : public BaseFunctor<int>{
public:
    void doFunction2(int variable){ cout << "Do function 2 with integer variable" << variable <<endl; }
    template<class T>
    void CallFunction(T data) { doFunction2(data);} 
    template<class T>
    void operator()(T data){ this->CallFunction(data); }
};

class FunctionPerformer{
    private:
       map<string,Functor> functions;
    public:
       FunctionPerformer(){
         //init your map.
            FunctionPointer1 function1;
        FunctionPointer2 function2;
            //-- follows
        functions["Function1"] = function1;
        functions["Functions2"] = function2;
            //-- follows
       }
       Functor getFunctionFromString(string str){
                return functions[str]
       }
};
int main(int argc, char *argv[])
{
    map<string,Functor> functions;
    FunctionPerformer performer;

    Functor func1, func2; // to hold return values from perfomer()
    FunctionPointer1 *fn1; // to casting and execute the functions
    FunctionPointer2 *fn2; // to casting and execute the functions
    func1 = performer.getFunctionFromString("Function1");//get data
    func2 = performer.getFunctionFromString("Function2");

    //following two lines to cast the object and run the methods
    fn1 = reinterpret_cast<FunctionPointer1 *>(&func1);
    (*fn1)(NULL);

    //following two lines to cast the object and run the methods
    fn2 = reinterpret_cast<FunctionPointer2 *>(&func2);
    (*fn2)(10);

    system("Pause");
    return 0;
}

編集された部分はそれをより明確にすると思いますか?このコードは少し最適化できます。それで遊んでください。

于 2012-10-18T14:45:09.247 に答える
1

これは、可変個引数テンプレートを使用したC++11で実行できます。https://stackoverflow.com/a/33837343/1496826で私の答えを確認してください

于 2015-11-20T22:55:12.580 に答える
0

いいえ、それは実際には実行可能ではありません。このようなことをしたい場合は、実際の通訳言語が必要です。署名が一定でなくなるとすぐに、もっと複雑なものが必要になります。

于 2012-10-18T13:50:21.180 に答える
0

これらすべての関数に同じシグネチャを持たせるのはどうですか?すべての戻り型にインターフェースを実装させるか、コレクション、クラス、ユニオン、または構造体を使用することができます。引数についても同じです。

于 2012-10-18T14:00:40.223 に答える
0

スペシャライゼーションとテンプレートを使用して問題を回避することはできませんか?

template <class T>
T FooBar(void * params);

template<> int FooBar<int>( void * params ); 

template<> char FooBar<char>( void * params ); 
于 2012-10-18T14:13:06.897 に答える
0

同じデータ構造に収容するには互いに異なりすぎる関数ポインタ自体を格納する代わりに、不一致のブリッジを処理するアダプタを格納できます。これは型消去の一形態です。例:

// Imaginary important resources
blaz_type get_blaz();
qux_type get_qux();

// The functions we'd like to put in our map
int foo(blaz_type);
std::string bar(qux_type);

using context_type = std::tuple<blaz_type, qux_type>;
using callback_type = std::function<void(context_type, socket_type&)>;

using std::get;
std::map<std::string, callback_type> callbacks = {
    {
        "foo"
         , [](context_type context, socket_type& out)
           { marshall(out, foo(get<0>(std::move(context)))); }
    }
    , {
        "bar"
        , [](context_type context, socket_type& out)
          { marshall(out, bar(get<1>(std::move(context)))); }
    }
};

void (*)(context_type, socket_type&)この例では、アダプターはステートフルではないため、実際にとして使用できますcallback_type

この種の設計は、保存されたコールバックが必要とする可能性のあるすべての種類のパラメーターcontext_typeについて知る必要があるという点で、少し脆弱であることに注意してください。後で新しい種類のパラメータを必要とするコールバックを保存する必要がある場合は、変更する必要があります-上記の設計を改善して、のようなマジックナンバーをパラメータとして使用しないようにすると、苦痛を軽減できます(特にから型を削除する逆の状況)。すべてのコールバックが同じパラメーターを使用する場合、これは問題ではありません。その場合、完全に不要になり、それらのパラメーターをコールバックに直接渡すことができます。context_type01std::getcontext_typecontext_type

LWSでのデモンストレーション

于 2012-10-18T14:33:46.737 に答える