2

I am building a C++ program that needs to store a map of strings to function pointers. However, every function may have different return types and parameters. The way I am attempting to solve this problem is by creating the functions as taking an array of void pointers and returning an array of void pointers, and then casting the arguments and return values as needed.

To figure out how this would work, I'm trying to build a simple dummy, but can't get it to compile. I've tried a number of things, but I keep getting different errors. here's an example:

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

when I try to compile, I get:

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

Obviously there are plenty of things wrong here, but I really just don't know where to start. Can anyone either show me what's wrong with the code above, or give me an alternative to the way I am currently doing it?

NOTE

The reason I am returning a void** instead of just void* is that there might be a circumstance where I need to return multiple values of different types. An example would be if, above, I wanted to return both the resulting map AND the number of entries in the map. I haven't even gotten to the point of figuring out how to construct that array yet, though.

EDIT

So based on the responses so far, it seems pretty clear that this is the wrong way of solving this problem. With that in mind, can anyone suggest a better one? I need to be able to store the various function in a single map, which means I need to be able to define a single data type to functions that take and return different types. And it IS important to be able to return multiple values.

ewok
  • 20,148
  • 51
  • 149
  • 254
  • 2
    The way you've written it, it will be wrong whatever you do because you're returning the address of a non-static local variable. – Kaz Dragon Dec 10 '12 at 15:19
  • Also, please consider marking the lines in your code which match the lines indicated in your error messages. It's not easy to work out which lines are 12 and 17 at a glance. – Kaz Dragon Dec 10 '12 at 15:20
  • You really only need one `void *` argument. If you need more you can always make that one argument be a vector or a struct. – Vaughn Cato Dec 10 '12 at 15:31
  • `&((void*) &test)` is essentially `&&test` and you can't do that. – Joe Dec 10 '12 at 15:35
  • [It's better if we don't answer this straight.](http://weblogs.asp.net/alex_papadimoulis/archive/2005/05/25/408925.aspx) Could you tell us what problem you're trying to solve with this design? – Beta Dec 10 '12 at 15:40
  • @Beta I thought it was clear what I needed to do. I need to be able to store a map, where the value of each entry is a function, but the various functions have different argument and return types. since I define function pointers using `typedef`, the function signatures all need to match that typedef, which means they all need to be the same – ewok Dec 10 '12 at 15:45
  • How do you know the exact signature when you call a function from that map? – hansmaad Dec 10 '12 at 15:48
  • @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. – ewok Dec 10 '12 at 15:59
  • Have you tried [templates](http://stackoverflow.com/questions/152318/learning-c-templates)? – Joe Dec 10 '12 at 16:21

6 Answers6

2

Ignoring my own horror at the idea of blatantly throwing type safety to the wind, two things spring immediately to mind.

First, what exactly do you think will be pointed to when string2map goes out of scope?

Second is that you don't have to cast to void*. Void* gets special treatment in C++ in that anything can be cast to it.

If you insist on trying to push this, I'd start by changing the return type to void, and then take the void* as an input parameter to your function.

For example:

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

This way you'd have to instantiate your map in a scope that will actually have a map to point to.

Shaun
  • 3,928
  • 22
  • 21
2

You're converting a map<string,string> to a void**, returning it then converting it back to a map<string,string. Why not just return a map<string,string>? It's also called string2map which implies you will only ever call it with a string (backed up by the fact you pass in a string, which is converted to a void** then converted straight back). Unless you have a good reason to convert to and from void** all over the place this is probably what you need:

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

EDIT:

I've just reread your question. You might want to look up Generalised Functors and look at Boost's std::function as possible solutions to this problem. It's possible to change the return type of a function via a wrapper class, something like:

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

Using this as a wrapper might help you store functors with different return types in the same array.

Bok McDonagh
  • 1,367
  • 10
  • 27
1

$5.3.1/3 - "The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualifiedid."

$5.3.1/2 - "The result of each of the following unary operators is a prvalue."

So, in effect you are trying to take the address of an rvalue which is not allowed.

Further, C++ does not allow to return an array.

So, you really want to start looking at what you want. Return the map by value instead is one definite option.

Chubsdad
  • 24,777
  • 4
  • 73
  • 129
0

The way I am attempting to solve this problem is by creating the functions as taking an array of void pointers and returning an array of void pointers, and then casting the arguments and return values as needed.

That's (really really) bad. Have a look instead at std::function and std::bind - those should cover differences between function signatures and bound arguments in an elegant way.

The reason I am returning a void** instead of just void* is that there might be a circumstance where I need to return multiple values of different types.

Then return an object that contains the values. For generics have a look at std::tuple or boost::any.

Here's some code:

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
utnapistim
  • 26,809
  • 3
  • 46
  • 82
0

Is this what you tried to do?

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);
}
hansmaad
  • 18,417
  • 9
  • 53
  • 94
0

@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. – ewok 33 mins ago

In that case, the typical solution is like this:

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

For brevity, I have used C-style casts of the function pointers. The correct C++ cast would be reinterpret_cast<>().

The function pointers are converted to a common type on insertion into the map and converted back to their correct type when invoking them.

Bart van Ingen Schenau
  • 15,488
  • 4
  • 32
  • 41