wolfeweeks
wolfeweeks

Reputation: 445

C++: Container of different types

I have some functions (simple for this example) with different paramaters:

int addNums(int a, int b) {
    return a + b;
}

int squareNum(int a) {
    return a * a;
}

I have a template class action which has a function pointer which will eventually point to either of the above functions:

template<class T>
class action {
public:
    std::string descriptionOfAction;
    T functionPtr;
};

I have a class user which should have some form of a container of actions:

class user {
public:
    std::string name;

    //container of actions
};

In main, I defined and initialized two actions to correspond with the first two functions listed above:

action<int(*)(int, int)> addAction;
addAction.descriptionOfAction = "Add two numbers together";
addAction.functionPtr = addNums;

action<int(*)(int)> squareAction;
squareAction.descriptionOfAction = "Square a number";
squareAction.functionPtr = squareNum;

Now I want to insert both of these actions into a users container of actions:

user aUser;
aUser.name = "User's Name";
//insert "addAction" and "squareAction" to user's container of actions

Because of the templating, these actions are not the same types. Is there an existing type of container to hold these? If not, is there a way to do something similar without templates? Ideally, when a user "uses" one of the actions in their container, the action should be erased.

Upvotes: 0

Views: 877

Answers (2)

Kaninchen
Kaninchen

Reputation: 1

Don't store pointers to functions as void*. A pointer-to-function is not the same as a pointer-to-variable.

If you want to store multiple types in one variable, use std::variant if you can use C++17, otherwise use a union if you can't.

Upvotes: 0

jvd
jvd

Reputation: 774

The problem here is that functions have different arity (i.e., the number of arguments).

It's not much of a problem to put your functions into some container. You can always put your functions into an std::vector<void*> (the compiler does not care), but then... what? What's the plan?

You will also need to add function arity to the same container, so it transforms to std::vector<std::pair<void*, unsigned int>> (function pointer + arity). This automatically complicates your action adding, since you will obviously won't want to specify the arity by hand, and you will want to automate this in order to avoid any human error. This will involve templates. Moreover, you will need to manually inspect the arities and pass the appropriate number of parameters. This will be error prone, too.

The most simple approach would be to define some

struct my_function_interface {
    virtual int operator()(<all your parameters here>) = 0;
};

And then define your actions in term of that interface. This is a classic OOP solution, which abstracts away all your problems. Moreover, it will most likely be more performant, since all your checks and special function invocations won't come for free. Here, you will have a single virtual function call.

Upvotes: 1

Related Questions