2017-10-18 18 views
0

私は、それぞれが基本型(int、float、bool、string)であり、異なる数をサポートするために8つのオーバーロードを持つ関数に渡す、可変数の引数を取るコンソールコマンドを設定していますさまざまなタイプの引数のコマンドラインの文字列を型に基づいて値を解析し、それを関数に渡すにはどうすればよいですか?可変数と型の引数をテンプレート関数に渡すには?

私は各引数を関数const char* GetArg(int index)で取得できます。 char*を適切なタイプに変換することは問題ではないので、その部分については心配しないでください。値を格納し、何とかテンプレート関数に渡すことは、私が立ち往生している部分です。例えば

、コマンドは次の文字列を用いて実行された場合、「コマンド66真5 『の文字列値』 0.56」

それは、次の引数に分割することと何とか保存されたであろう:

int arg1 = GetArg(1); // 66 
bool arg2 = GetArg(2); // true 
int arg3 = GetArg(3); // 5 
char* arg4 = GetArg(4); // "string value" 
float arg5 = GetArg(5); // 0.56 

そして、引数の数に基づいて、正しいテンプレート関数を呼び出す:あなたは、これが可能になるだろうどのように

// The function definition looks something like this: 
void SomeFunc(); 
template<typename T1> 
void SomeFunc(const T1& arg1); 
template<typename T1, typename T2> 
void SomeFunc(const T1& arg1, const T2& arg2); 
// etc... 

// And then somehow it would be called. This is just an example. I don't 
// know how to call it in a way that would work with variable number and 
// type of args. 
switch (argCount) 
{ 
case 0: 
    SomeFunc(); 
    break; 
case 1: 
    SomeFunc(arg1); 
    break; 
case 2: 
    SomeFunc(arg1, arg2); 
    break; 
case 3: 
    SomeFunc(arg1, arg2, arg3); 
    break; 
case 4: 
    SomeFunc(arg1, arg2, arg3, arg4); 
    break; 
case 5: 
    SomeFunc(arg1, arg2, arg3, arg4, arg5); 
    break; 
} 

を?各引数の型が分からないように、テンプレート関数に渡すことができる何らかの方法でargsを格納していますが、私は何かを考えていないように感じます。

このインターフェイスも変更できません。これは私が対処しなければならない第三者の機能です。したがって、どのように実装されても、最終的にはSomeFunc()を通過する必要があります。

重要:私はVisual Studio 2012でこれをやっているので、新しいC++の機能にかなり制限されています。それはC++ 11の少しをすることができますが、それはそれです。プロジェクトを新しいバージョンにアップグレードしようとしていますが、これは私が対処しなければならないものです。

+0

コンパイル時と実行時のロジックを混在させようとしているようです。テンプレートはコンパイル時にコンパイル時にパラメータの数とその型を知る必要があります。それは本当にあなたが達成しようとしている他のものですか? –

+0

タプルを構築できれば、[this](https://stackoverflow.com/questions/7858817/unpacking-a-tupall-a-matching-function-pointer)を使ってタプルを回すことができます関数呼び出しに変換します。 – NathanOliver

+0

@TommyAndersenそれは正確に聞こえる。私は本当にその周りには何の道もない。私は、簡単にイベントをトリガするためのヘルパーコンソールコマンドを作成しようとしています。これが手に入らなければ、私はおそらくちょうどそのように思えるようになり始めているアイデアをちょうど打ち明けます。 – Shenjoku

答えて

1
using basic_type = std::variant<int, float, bool, std::string>; 

using flat_arguments = std::vector<basic_type>; 

template<std::size_t...Ns> 
using packed_arguments = std::variant< std::array<basic_type, Ns>... >; 

template<class T, std::size_t...Ns> 
std::array<T, sizeof...(Ns)> pack_one(std::vector<T> n, std::index_sequence<Ns...>) { 
    return {{ std::move(n[Ns])... }}; 
} 

template<class T, std::size_t...Ns> 
std::optional<std::variant< std::array<T, Ns>... >> 
pack_all(std::vector<T> n, std::index_sequence<Ns...>) { 
    std::optional<std::variant< std::array<T, Ns>... >> retval; 
    if (n.size() >= sizeof...(Ns)) { return retval; } 
    (
    (
     (n.size()==Ns)? 
     void(retval.emplace(pack_one(std::move(n), std::make_index_sequence<Ns>{}): 
     void() 
    ),... 
); 
    return retval; 
} 

flat_arguments get_arguments(int argc, char const* const*argv); // write this 

auto invoke_somefunc = [](auto&&...args){ 
    return SomeFunc(decltype(args)(args)...); 
}; 

int main(int argc, char const*const* argv) { 
    auto args = get_arguments(argc, argv); 
    auto args_packed = pack_all(std::move(args), std::make_index_sequence<9>{}); 
    if (!args_packed) return -1; 
    std::visit([](auto&& args){ 
    std::apply([](auto&&...args){ 
     std::visit(invoke_somefunc, args...); 
    }, args); 
    }, args_packed); 
} 

そうする必要があります。おそらくタイプミスを含んでいるでしょう。

boostには、上記のstdの代わりに使用できる同等のタイプ(variantoptional)があります。

倍数パックの展開は、配列内の展開のハック以上で置き換えることができます。

+0

申し訳ありませんが、私は新しい言語の機能には限られています。私はVS2012でこれをやっているので、これのほとんどが利用可能だとは思わない。その情報で元の投稿を更新しました。 – Shenjoku

+0

@Shenjokuああ、そうではありません。つまり、あなたが何でもできるTuring Tar Pitがありますが、基本的には上記の作業を手動でスパゲッティコード全体に拡張しています。 VS 2012でサポートされているC++のサブ言語で書くよりも、C++コンパイラをアップグレードする方が簡単です。たぶんVS20で動作するバリアントをサポートする古代版のブーストを見つけることができ、ラムダとその痛みを置き換える関数オブジェクトを手作業で書くことができます。 – Yakk

関連する問題