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

显然这里有很多问题,但我真的不知道从哪里开始。谁能告诉我上面的代码有什么问题,或者给我一个替代我目前这样做的方式?

笔记

我返回 avoid**而不是 just的原因void*是可能存在我需要返回多个不同类型的值的情况。一个例子是,在上面,我想返回结果映射和映射中的条目数。不过,我什至还没有弄清楚如何构建该数组。

编辑

因此,根据迄今为止的回应,很明显这是解决此问题的错误方法。考虑到这一点,有人能推荐一个更好的吗?我需要能够将各种函数存储在单个映射中,这意味着我需要能够为接受和返回不同类型的函数定义单一数据类型。能够返回多个值很重要。

4

6 回答 6

2

无视我自己对公然将类型安全抛诸脑后的恐惧,两件事立即浮现在脑海中。

首先,您认为当 string2map 超出范围时会指向什么?

其次是您不必强制转换为 void*。Void* 在 C++ 中得到特殊处理,因为它可以转换任何东西。

如果您坚持尝试推动这一点,我将首先将返回类型更改为 void,然后将 void* 作为函数的输入参数。

例如:

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

这样你就必须在一个实际上一个地图指向的范围内实例化你的地图。

于 2012-12-10T15:25:39.617 回答
2

您正在将 a 转换map<string,string>为 a void**,将其返回然后将其转换回 a 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;
}

编辑:

我刚刚重读了你的问题。您可能想查找 Generalized Functors 并查看 Booststd::function可能解决此问题的方法。可以通过包装类更改函数的返回类型,例如:

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 - “一元 & 运算符的结果是一个指向其操作数的指针。操作数应该是一个左值或一个qualifiedid。”

$5.3.1/2 - “以下每个一元运算符的结果都是 纯右值。”

因此,实际上您正在尝试获取不允许的右值的地址。

此外,C++ 不允许返回数组。

所以,你真的想开始看看你想要什么。按值返回地图是一个明确的选择。

于 2012-12-10T15:26:55.837 回答
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

这是你试图做的吗?

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

@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 回答