2

文字列のマップを関数ポインターに格納する必要がある C++ プログラムを作成しています。ただし、すべての関数が異なる戻り値の型とパラメーターを持つ場合があります。この問題を解決しようとしている方法は、void ポインターの配列を取り、void ポインターの配列を返す関数を作成し、必要に応じて引数と戻り値をキャストすることです。

これがどのように機能するかを理解するために、単純なダミーを作成しようとしていますが、コンパイルできません。いろいろ試してみましたが、さまざまなエラーが発生し続けます。例を次に示します。

#include <string>
#include <iostream>
#include <map>

using namespace std;

void** string2map(void** args){
    //takes a string of the form "key:value;key:value;..." and returns a map<string,string>
    string st = *((string**) args)[0];
    map<string, string> result = map <string, string>();
    //code doesnt matter
    return (void*) &((void*) &result);
}

int main(){
    string test = "hello:there;how:are you?";
    map<string, string> result = *(map<string, string>**)string2map((void*) &((void*) &test))[0];

    return 0;
}

コンパイルしようとすると、次のようになります。

void.cpp: In function 'void** string2map(void**)':
void.cpp:12:34: error: lvalue required as unary '&' operand
void.cpp: In function 'int main()':
void.cpp:17:89: error: lvalue required as unary '&' operand

明らかに、ここには多くの問題がありますが、どこから始めればよいかわかりません。上記のコードの何が問題なのかを誰かに教えてもらえますか、または現在行っている方法に代わる方法を教えてもらえますか?

ノート

void**just ではなくa を返す理由はvoid*、異なる型の複数の値を返す必要がある場合があるためです。例としては、上記のように、結果のマップとマップ内のエントリ数の両方を返したい場合があります。ただし、その配列を構築する方法をまだ理解していません。

編集

したがって、これまでの回答に基づいて、これがこの問題を解決する間違った方法であることは明らかです。それを念頭に置いて、誰かがより良いものを提案できますか? さまざまな関数を 1 つのマップに格納できる必要があります。つまり、さまざまな型を取り、返す関数に対して 1 つのデータ型を定義できる必要があります。そして、複数の値を返すことができることが重要です。

4

6 に答える 6

2

型の安全性をあからさまに風に投げ捨てるという考えに対する私自身の恐怖を無視して、2 つのことがすぐに思い浮かびます。

まず、string2map が範囲外になると、正確には何を指すと思いますか?

2 つ目は、void* にキャストする必要がないことです。Void* は、何でもキャストできるという点で、C++ で特別な扱いを受けます。

これをプッシュしようとする場合は、まず戻り値の型を void に変更してから、void* を関数への入力パラメーターとして使用します。

例えば:

void string2map(void* args, void* returnedMap);

この方法では、実際に指すマップを持つスコープでマップをインスタンス化する必要があります

于 2012-12-10T15:25:39.617 に答える
2

a を に変換し、map<string,string>それvoid**を返してから に戻しmap<string,stringます。を返さないのはなぜmap<string,string>ですか?またstring2map、これは、文字列でのみ呼び出すことを意味するように呼び出されます (文字列を渡すという事実によってバックアップされます。これは、変換されてから にvoid**変換されます)。あちこちと変換する正当な理由がない限り、void**これはおそらく必要なものです。

#include <string>
#include <iostream>
#include <map>

using namespace std;

map<string, string> string2map(string st){
    map<string, string> result = map <string, string>();
    //code doesnt matter
    return result;
}

int main(){
    string test = "hello:there;how:are you?";
    map<string, string> result = string2map(test);
    return 0;
}

編集:

あなたの質問を読み直しました。一般化されたファンクターを調べてstd::function、この問題に対する可能な解決策として Boost を調べてみてください。次のように、ラッパー クラスを介して関数の戻り値の型を変更することができます。

template< class T >
class ReturnVoid
{
public:
    ReturnVoid( T (*functor)() ) : m_functor( functor ) {}

    void operator() { Result = functor(); }

private:
    T (*m_functor)();
    T Result;
};

//  Specialise for void since you can't have a member of type 'void'
template<>
ReturnVoid< void >
{
public:
    ReturnVoid( T (*functor)() ) : m_functor( functor ) {}

    void operator() { functor(); }

private:
    T (*m_functor)();
};

これをラッパーとして使用すると、戻り値の型が異なるファンクターを同じ配列に格納するのに役立つ場合があります。

于 2012-12-11T10:32:30.313 に答える
1

$5.3.1/3 - 「単項 & 演算子の結果は、そのオペランドへのポインターです。オペランドは、左辺値または修飾 IDでなければなりません。」

$5.3.1/2 - 「次の各単項演算子の結果は prvalueです。」

したがって、事実上、許可されていない右辺値のアドレスを取得しようとしています。

さらに、C++ では配列を返すことができません。

だから、あなたは本当にあなたが欲しいものを見始めたいと思っています。代わりに値でマップを返すことは、1 つの明確なオプションです。

于 2012-12-10T15:26:55.837 に答える
0

これはあなたがやろうとしたことですか?

int Foo(int a) { return a; }
typedef int (*FooFunc)(int);

void Bar(){}

typedef std::map<std::string, void*>  FunctionMap;  
// you should use boost::any or something similar instead of void* here

FunctionMap CreateFunctionMap(const std::string& args)
{
    FunctionMap result;
    result["Foo"] = &Foo;
    result["Bar"] = &Bar;
    return result;
}

void Call(FunctionMap::const_reference functionInfo)
{
    // @hansmaad The key will give information on the signatures. 
    // there are a few distinct options, so it will be a conditional 
    // with a couple of clauses.
    if (functionInfo.first == "Foo")
    {
        auto f = static_cast<FooFunc>(functionInfo.second);
        std::cout << f(42);
    }
    else if (functionInfo.first == "Bar")
    {
        /* */
    }
}

int main()
{
    auto functions = CreateFunctionMap("...");
    std::for_each(begin(functions), end(functions), Call);
}
于 2012-12-10T16:14:42.463 に答える
0

この問題を解決しようとしている方法は、void ポインターの配列を取り、void ポインターの配列を返す関数を作成し、必要に応じて引数と戻り値をキャストすることです。

それは(本当に本当に)悪いです。代わりに std::function と std::bind を見てください。これらは、関数シグネチャとバインドされた引数の違いをエレガントな方法でカバーする必要があります。

void* だけでなく void** を返す理由は、異なる型の複数の値を返す必要がある場合があるためです。

次に、値を含むオブジェクトを返します。ジェネリックについては、std::tuple または boost::any をご覧ください。

ここにいくつかのコードがあります:

void function1(int, const char); // defined elsewhere
std::tuple<int,int> function2(std::string&); // defined elsewhere

std::map<std::string,std::function<void(void)>> functionmap;
functionmap.insert( std::make_pair("function1", std::bind(&function1, 2, 'c')) );

std::tuple<int,int> result;
functionmap.insert( std::make_pair("function2", [&result] { 
    result = function2("this is a test"); } );

// call function1
functionmap["function1"]();

// call function2
functionmap["function2"](); // result will now contain the result 
                            // of calling function2
于 2012-12-10T16:04:17.993 に答える
0

@hansmaad キーは署名に関する情報を提供します。いくつかの異なるオプションがあるため、いくつかの句を含む条件付きになります。– ewok 33 分前

その場合、典型的な解決策は次のようになります。

typedef void (*func_ptr)();
std::map<std::string, func_ptr> func_map;

map<string,string> string2map(string arg){
    //takes a string of the form "key:value;key:value;..." and returns a map<string,string>
    map<string, string> result = map <string, string>();
    //...
    return result;
}

// ...

// Add function to the map
func_map["map<string,string>(string)" = (func_ptr)string2map;

// Call function in the map
std::map<std::string, func_ptr>::iterator it = ...
if (it->first == "map<string,string>(string)")
{
    map<string,string> (*func)(string) = (map<string,string>(*)(string))it->second;
    map<string,string> result = func("key1;value1;key2;value2");
}

簡潔にするために、関数ポインターの C スタイルのキャストを使用しました。正しい C++ キャストはreinterpret_cast<>().

関数ポインターは、マップへの挿入時に共通の型に変換され、呼び出し時に正しい型に変換されます。

于 2012-12-10T16:44:35.937 に答える