0

次の情報を持ついくつかのリフレクション メタデータがあるとします。

enum class type { t_int, t_double, t_str /* etc... */ };

struct meta_data
{
    void* function;
    void* instance;
    std::vector<type> param_types;
};

std::map<std::string, meta_data> ftable;

関数名とパラメーターの両方を文字列として指定すると、このマップで関数を呼び出したいと思います。私の問題は、パラメーターを変換することではなく (たとえば、を使用boost::lexical_cast)、正しいタイプの関数ポインターにキャストして関数を呼び出すことです。可能な 8 つの型と最大 8 つのパラメーターを許可する場合、それは既に私のコードに多くの分岐があります。避けたいこと (疑似コード):

    スイッチ (md.param_types.size())
    {
    ケース 0:
         関数ポインタをキャストして呼び出す
         壊す;
    ケース 1:
         スイッチ (md.param_types[0])
         {
         ケースt_int:
                    int param = boost::lexical_cast(param_strings[0]);
                  関数ポインタをキャストし、param で呼び出す
         場合 ...
         }
         壊す;
    ケース 2:
         スイッチ (md.param_types[0]) {
         ケースt_int:
             int param = boost::lexical_cast(param_strings[0]);
             switch (md.param_types[1]) {...} // 2 番目のパラメータ タイプ..
         }
         壊す;
    ケース n...
    }

これは、パラメーターと可能なタイプの数によって非常に急速に爆発します。(疑似コード)の行に沿った解決策を探しています:

for (auto& p : paramter_strings)
{
    convert p to a variable of matching type (type id comes from meta_data).
    store value
}

call function with stored values

つまり、関数呼び出しの分岐はありません。最小限の定型コードでこれを行うにはどうすればよいですか (任意の数のパラメーターをサポートする可能性があります)。これは、カスタム スクリプト言語へのバインドを作成することと考えることができます。

4

1 に答える 1

0

私は一つのアプローチを思いつきました。

unsigned int V のベクトルがあり、ベクトルのすべての要素が N (または、たとえば 20) より小さい非負の数であることがわかっているとします。ベクトル V を正の整数に変更するには、次のように呼び出します。

n = sequence_to_code(V,N); // or n = encode(V,20U);

これがコードです。

long sequence_to_long(const std::vector<unsigned int> & L,
                      unsigned long n) {
  long result = 0L;
  std::vector<unsigned int>::const_iterator w=L.begin(),e=L.end();
  if(w!=e) {
    result += (*w)+1;
    unsigned long the_pow = n;
    unsigned int i = 1U;
    ++w;
    while(w!=e) {
      result += (*w+1)*(the_pow);
      ++w;++i;the_pow *= n;
    }
  }
  return result;
}

実際には、おそらく「unsigned long」を返す必要がありました。

さらに、この同じルーチンを、テキスト ファイルを作成するプログラムで使用できます。このテキスト ファイルには、C++ コードが含まれます。「the_defines.hpp」を作成したとします。例で説明します...

たとえば、t_int=0; があるとします。t_double = 1、d_str = 2 と 3 種類しかありません。次に、「the_define.hpp」がファイルになる可能性があります。

#define TYPE_EMPTY 0U
#define TYPE_INT   1U
#define TYPE_DOUBLE 2U
#define TYPE_STR    3U
#define TYPE_INT_INT 4U
#define TYPE_DOUBLE_INT 5U

このコードは次のように使用できます。

std::vector<unsigned int> L;
// add entries to L
long n = sequence_to_long(L,3UL);
switch(n) {
  case TYPE_INT:
    std::cout << "an integer\n";
    break;
  case TYPE_INT_DOUBLE:
    std::cout << "two args; first is an int; second is a double\n:
    break;
}

もちろん、非常に長い列挙型のコードを含むテキスト ファイルを作成することもできます (#define を避けたい場合)。例えば、

enum class extended_type { 
  type_int,
  type_double,
  type_str,
  type_int_int,
  type double_int,

等々。

(1 つまたは複数の) テキスト ファイルを作成するプログラムを作成することもできます。これらのテキスト ファイルは、C++ コードによっても作成されます。たとえば、作成したファイルは次のようになります。

swtich(n) {
  case empty:
    FILLIN
    break; 
  case t_int:
    FILLIN
    break;

などなど

  case t_str_str:
    FILLIN;
    break;
 }

インライン関数または通常の関数を使用してキャストすることもお勧めします。例えば、

inline int inside_int(foo f) {
  const bar & b = * reinterpret_cast<const bar *>(f.pointer());
  return b.d_x;
}

DRY (繰り返さないでください) であり、関数のすべてのインスタンスを検索できるため、これをお勧めします。

これらのプログラムがエラー (オーバーフロー、ゼロ ポインターなど) を処理しないことはわかっています。

マーク

于 2015-07-08T03:51:45.863 に答える