I would like to design a way to call one of many very similar functions - where the only difference is the number of arguments. Bare in mind this is a minimal example of my larger issue. I have lots of functions for doing different tasks. Each function prototype is very similar - except they have a different number of parameters.
I want to be able to call some wrapper class that I tell it the test type and pass it a list (vector) of params and it sorts the rest out for me.
Here is a working setup (with the parts that don't work commented out):
#include <vector>
#include <map>
#include <functional>
#include <iostream>
enum class test_type {t0, t1, t2, t3, t4, tn};
class tester
{
public:
void run(test_type type, const std::vector<int> &args)
{
func_map[type].func();
};
private:
void function_0() { std::cout << "f0\n"; };
void function_1(int p1, int p2) { std::cout << "f1\n"; };
void function_2(int p1, int p2, int p3) { std::cout << "f2\n"; };
void function_3(int p1, int p2) { std::cout << "f3\n"; };
void function_4(int p1) { std::cout << "f4\n"; };
void function_n(int p1) { std::cout << "fn\n"; };
struct test_info
{
std::function<void()> func; // variadic function std::function<void(...)> ?
int num_params;
};
std::map<test_type, test_info> func_map {
{test_type::t0, {[this]{function_0();}, 0}}
// {test_type::t1, {[this]{function_1();}, 2}}
// {test_type::t2, {[this]{function_2();}, 3}}
// {test_type::t3, {[this]{function_3();}, 2}}
// {test_type::t4, {[this]{function_4();}, 1}}
// {test_type::tn, {[this]{function_n();}, 1}}
};
};
int main() {
tester test;
test.run(test_type::t0, {});
//test.run(test_type::t1, {1, 2});
//test.run(test_type::t2, {1, 2, 3});
//test.run(test_type::t3, {1, 2});
//test.run(test_type::t4, {1});
//test.run(test_type::tn, {1});
return 0;
}
so this works since function_0 has 0 parameters and therefore matches std::function<void()>
type. I am not really sure what the best direction to take from here is.
I think there is an option to use variadic std::function<void(...)>
but reading up on this took me beyond my confort zone and my understanding of variadic templating is not so great.
So my questions are:
std::funtion<void(...)>
in some way?Pass the vector
itself to the wrapper lambdas, and let them extract the int
values to pass along to their target functions, eg:
#include <vector>
#include <map>
#include <functional>
#include <iostream>
#include <stdexcept>
enum class test_type {t0, t1, t2, t3, t4, tn};
class tester
{
public:
void run(test_type type, const std::vector<int> &args)
{
func_map[type](args);
};
private:
void function_0() { std::cout << "f0\n"; };
void function_1(int p1, int p2) { std::cout << "f1\n"; };
void function_2(int p1, int p2, int p3) { std::cout << "f2\n"; };
void function_3(int p1, int p2) { std::cout << "f3\n"; };
void function_4(int p1) { std::cout << "f4\n"; };
void function_n(int p1) { std::cout << "fn\n"; };
static void validate(const std::vector<int> &args, size_t needed) {
if (args.size() != needed)
throw std::invalid_argument("wrong number of arguments");
}
using func_type = std::function<void(const std::vector<int> &)>;
std::map<test_type, func_type> func_map {
{test_type::t0,
[this](const std::vector<int> &args){
validate(args, 0);
function_0();
}
},
{test_type::t1,
[this](const std::vector<int> &args){
validate(args, 2);
function_1(args[0], args[1]);
}
},
{test_type::t2,
[this](const std::vector<int> &args){
validate(args, 3);
function_2(args[0], args[1], args[2]);
}
},
{test_type::t3,
[this](const std::vector<int> &args){
validate(args, 2);
function_3(args[0], args[1]);
}
},
{test_type::t4,
[this](const std::vector<int> &args){
validate(args, 1);
function_4(args[0]);
}
},
{test_type::tn,
[this](const std::vector<int> &args){
validate(args, 1);
function_n(args[0]);
}
},
};
};
int main() {
tester test;
test.run(test_type::t0, {});
test.run(test_type::t1, {1, 2});
test.run(test_type::t2, {1, 2, 3});
test.run(test_type::t3, {1, 2});
test.run(test_type::t4, {1});
test.run(test_type::tn, {1});
return 0;
}