0

プログラム内の関数を、スクリプトのようなテキスト ファイルから呼び出せるようにしたいと考えています。

特定の署名に強制的に準拠させることなく、この「スクリプトマネージャー」に任意の関数を登録できるようにしたいと考えています。したがって、スクリプトから MyFunc(bool, string) または MyFunc2(int, float, char) を呼び出すことができます。解析側では、これらのパラメーターをパラメーター リストに入れることができますが、問題はこれらのパラメーターを関数に渡す方法です。

特定の署名を強制するため、 MyFunc(paramlist[0], paramlist[1]) のように呼び出すことはできません。また、呼び出される関数が「スクリプト マネージャー」について知る必要がないため、パラメーター リストを処理できる必要はありません。

これらの 2 つのコンポーネント (呼び出される関数と「スクリプト マネージャー」) を、前者 (呼び出される関数) のラッパーを記述せずに切り離すにはどうすればよいでしょうか?

4

2 に答える 2

0

他の人たちと同じように、車輪の再発明を避けることができるのであれば、それをしないことをお勧めします。つまり、可能であれば既存のフレームワークを使用してください。しかし、私は可能な解決策に少し拡張するつもりです.

C++ にはリフレクションがありません。つまり、実行時に関数ポインターを受け入れて検査し、引数が何であるかを解釈することはできません。しかし、コンパイル時にそれを行うことができます。解決策は簡単ではありませんが、型消去で解決できます。問題を単純化するために、すべての関数がvoidそれを返すと仮定します。

考えられる解決策の 1 つとして、ディスパッチャー構造は次のようになります (エラーは無視されます)。

while (true) {
   std::string func = read_function_name();
   std::vector<parameter_t> params = read_parameters();
   functions[ func ]( params );
}

ここで空白を埋める:read_function_nameは、呼び出す関数の名前を返す単純な関数です。機能ではなく、名前だけです。入力ファイルを処理し、関数に渡されるread_parameters一連のパラメーターを作成する関数です。functions関数名を関数の 1 つにマップする連想コンテナーです。パラメーターは、実際のパラメーターの型の一種の型消去であるため、単一のコンテナーでそれらを一般的に管理できます。operator()私たちの関数は、一連のパラメーターを取り、呼び出す正確な関数に対して型消去を実行する を実装するクラスのインスタンスです。それは簡単でした!少なくとも詳細を無視する場合。

パラメーターの実装はそれほど複雑ではありません。使用boost::anyして最善を尽くすこともできますし、より良いエラー検出や暗黙的な変換を実行できるように、もう少し情報を使用して独自の型消去を実装することもできます。名前を付けることができます。 .

型の実装はfunction_tもう少し複雑です。呼び出し可能な実際の要素で型消去を実行する必要があり、std::function<>ここまでの問題に適合します。しかし、それを使用することはできません。これは、関数の可能な限り最小のインターフェイスを残すことになるためです:operator() あなたはパラメーターoperator() を知っていますが、パラメーターを知っているのは誰ですか?

これは、ほとんどの作業が行われる場所であり、手動タイプの消去が必要になる場合があります. function_baseこれは、を提供する基本抽象クラスによって実装できますvirtual operator()( std::vector<parameter_t> [const] & ) [const](問題を少し単純化するために、const ネスで遊ぶか、当面は忘れてください)。はそのfunction_t基本型へのポインターを保持し、呼び出しを実行します。ここまではまだ簡単です。

関数の型消去の実装... さて、次の問題は、 からの各具体的な派生型をどのように実装するかfunction_baseであり、少しトリッキーになるところがあります (残りは単純であることに同意しましたよね?)任意のfunction_t関数を受け取り、 から派生したテンプレート化された型をインスタンス化し、ポインタを に格納するコンストラクタ テンプレートを に提供します。function_basefunction_t

ここでも、複雑さを避けるために、単一の引数で実行できると仮定します。残りは単なるコードです... の実装ではfunction_impl<>、元の関数ポインターを格納し、引数の型と数 (この場合は 1) を記憶する必要があります。の実装はoperator()、パラメーターのベクトルを反復処理し、各パラメーターについて型を消去せず (元の型に変換し直して)、関数を呼び出します。このコードは、テンプレートではほとんど手動である必要がありますが、同じアリティのすべての関数で再利用できます。

ここで物事を少し簡単にするために、受け取る関数から引数の型と数を抽出できる関数特性ライブラリを利用できます。

では、ユーザー コードに何が残るでしょうか。それは簡単です。この場合、私はそれを意味します。あなたのインターフェースは、任意のfunction_t関数ポインタから構築できるを提供する必要があります (そこに実装したものの要件に適合する限り)、および名前を指定してシステムにそのようなものを登録する関数:function_t

// user code: registration of a new function
void print_single_int( int );
lib::function_t f( &print_single_int );
lib::register( "print_single_int", f );

これらの 2 行は、システムに追加する機能ごとに入力する必要があります。もちろん、ソリューションの複雑さでは問題が補償されず、手動実装に進むと判断する場合があります。ユーザー コードが、パラメーターのベクトルを手動で処理し、関数を呼び出すハンド コードの派生物を作成するだけfunction_baseの場合...スクリプト言語で実装したい操作がほとんどない場合、それを汎用化するために余計な努力をする価値がないかもしれません。

于 2011-07-22T08:11:10.063 に答える
0

もちろん自分でもできますが、Lua (http://lua.org ) を使ったほうがいいと思います。Lua は c/c++ プログラムと非常に統合されており、多数の包括的なドキュメントとチュートリアルが利用可能です。たとえば、これを見てください: http://csl.sublevel3.org/lua/ (「Lua からの C 関数の呼び出し」セクションに注意してください。これが必要です)。

于 2011-07-22T05:38:49.760 に答える