jma
jma

Reputation: 3789

C++, a table of functions, and templates

I have a table of functions. It looks something like this:

struct AnimalFunction {
    void (*Walk)(int a);
    void (*Sing)(int a, int b);
    void (*Dance)(int a, int b, int c);
};
using AnimalFunctionTable = map<string, AnimalFunction>;

In other words, I have rows of AnimalFunction's and a map from some string that I know to, essentially, a type. For simplicity, maybe I include some extra arguments so that they all have the same signature and just ignore what they don't need : WalkDog(int a, int, int) { ... }.

Now I have to define functions like WalkDog(), SingDog(), DanceDog(), and then WalkGiraffe() and SingGiraffe() (but our giraffes don't dance, so they get some default function that maybe I have to name at least).

My goal is simplicity of maintenance. I'd like to write some template functions like

template<typename AnimalT>
void WalkAnimal(int a) { ... }

so that the table can just be a mapping of the string to the type. (Sometimes I can write such a general function, sometimes I need to write a specialised one, but at least I'm getting the compiler to maintain my table.) But I'm not convinced that C++ (14 in my case, though this could be an argument to upgrade to 17) supports such a thing.

The reason this starts with a string is because of an API accessible on a network. Other clients can't be expected to talk the same C++ types as my system, so they tell me something like {action: "walk", animal: "dog"}. (It's a protobuf, not JSON, but this question is text.)

// What I want to say, in my dreams:
actions[action_string][animal_string](arg1, arg2, arg3);

though the precise operators aren't important, just an illustration here.

Any suggestions, other than writing the entire table explicitly?

Upvotes: 4

Views: 249

Answers (1)

Jarod42
Jarod42

Reputation: 218278

As I understand, you want to replace:

struct AnimalFunction {
    void (*Walk)(int a);
    void (*Sing)(int a, int b);
    void (*Dance)(int a, int b, int c);
};

void WalkDog(int a) {/*..*/}
void SingDog(int a, int b) {/*..*/}
void DanceDog(int a, int b, int c) {/*..*/}

void WalkGiraffe(int a) {/*..*/}
void SingGiraffe(int a, int b) {/*..*/}

std::map<std::string, AnimalFunction> animalFunctions = {
  {"Dog", {&WalkDog, &SingDog, &DanceDog}},
  {"Giraffe", {&WalkGiraffe, &SingGiraffe, nullptr}},
};

by something like:

struct AnimalFunction {
    void (*Walk)(int a);
    void (*Sing)(int a, int b);
    void (*Dance)(int a, int b, int c);
};

struct Dog {/*..*/};
struct Giraffe {/*..*/};

template <typename T> void WalkT(int a) {/*..*/}
template <typename T> void SingT(int a, int b) {/*..*/}
template <typename T> void DanceT(int a, int b, int c) {/*..*/}

template <> void DanceT<Giraffe>(int a, int b, int c) {/*Empty*/}
// ...

template <typename T>
AnimalFunction MakeAnimalFunction()
{
    return {&WalkT<T>, &SingT<T>, &DanceT<T>};
}

std::map<std::string, AnimalFunction> animalFunctions = {
  {"Dog", MakeAnimalFunction<Dog>()},
  {"Giraffe", MakeAnimalFunction<Giraffe>()},
};

Upvotes: 6

Related Questions