【问题标题】:return an array of void*返回一个 void* 数组
【发布时间】:2012-12-10 15:16:30
【问题描述】:

我正在构建一个需要将字符串映射存储到函数指针的 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** 而不仅仅是void* 的原因是可能存在我需要返回多个不同类型的值的情况。一个例子是,在上面,我想返回结果映射和映射中的条目数。不过,我什至还没有弄清楚如何构造该数组。

编辑

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

【问题讨论】:

  • 按照你写的方式,不管你做什么都是错误的,因为你返回的是一个非静态局部变量的地址。
  • 另外,请考虑在代码中标记与错误消息中指示的行匹配的行。一眼就知道哪一行是 12 和 17 并不容易。
  • 你真的只需要一个void * 参数。如果你需要更多,你总是可以将那个参数设为向量或结构。
  • &amp;((void*) &amp;test) 本质上是&amp;&amp;test,你不能那样做。
  • It's better if we don't answer this straight. 你能告诉我们你想用这个设计解决什么问题吗?

标签: c++ arrays function-pointers void-pointers


【解决方案1】:

您正在将map&lt;string,string&gt; 转换为void**,将其返回,然后将其转换回map&lt;string,string。为什么不直接返回map&lt;string,string&gt;?它也被称为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 并查看 Boost 的 std::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)();
};

使用它作为包装器可能会帮助您将具有不同返回类型的函子存储在同一个数组中。

【讨论】:

    【解决方案2】:

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

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

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

    如果你坚持尝试推送,我会先将返回类型更改为 void,然后将 void* 作为函数的输入参数。

    例如:

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

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

    【讨论】:

      【解决方案3】:

      $5.3.1/3 - "一元 & 运算符的结果是指向它的指针 操作数。操作数应为左值或qualifiedid。"

      $5.3.1/2 - "以下每个一元运算符的结果是 prvalue。”

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

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

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

      【讨论】:

        【解决方案4】:

        我试图解决这个问题的方法是通过创建函数来获取一个 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
        

        【讨论】:

          【解决方案5】:

          这是你试图做的吗?

          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);
          }
          

          【讨论】:

            【解决方案6】:

            @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&lt;&gt;()。

            函数指针在插入映射时转换为通用类型,并在调用它们时转换回正确的类型。

            【讨论】:

              猜你喜欢
              • 2023-01-28
              • 1970-01-01
              • 1970-01-01
              • 2011-01-25
              • 2016-04-22
              • 1970-01-01
              • 1970-01-01
              • 2021-12-18
              • 1970-01-01
              相关资源
              最近更新 更多