The full story:
I'm trying to build a framework that looks a bit like this:
#include <tuple>
#include <memory>
using namespace std;
// this class allows user to call "run" without any args
class simulation_base{
public:
int run(){ execute_simulation_wrapped(); };
protected:
virtual int execute_simulation_wrapped(); {return 0;};
}
// this class funnels some stored inputs into a soon-to-be-overridden method
template <typename Ts...>
class simulation_wrapper : public simulation_base {
tuple<shared_ptr<Ts>... > stored_inputs;
public:
int execute_simulation_wrapped() {/* how do you call simulation method? */};
protected:
virtual int simulation(const Ts&...){return 0};
}
Now we can use the framework to define a couple of simple-looking classes that can be simulated..
class jones_household : public simulation_wrapper< woman, girl, cat >{
int simulation(woman mrs_jones, girl mary, cat sniffles)
// mrs_jones and her daugther mary play with sniffles the cat
return 1;
}
}
class smith_household : public simulation_wrapper< man, dog >{
int simulation(man mr_smith, dog fido)
// mr_smith and his dog fido go for a walk
return 1;
}
}
And then build a multiverse of these simulatable households...
smith_household uinverse_1_smiths;
smith_household uinverse_2_smiths;
jones_houshold uinverse_1_jones;
jones_houshold uinverse_2_jones;
// set the values of the stored_inputs (i.e. fido, sniffles etc.)
Finally, we get to the point: we want to be able to write a function which is agnostic of household type, but is still able to call run
on the simulation:
void play_simulation(simulation_base& some_household){
// do some general stuff...
some_household.run();
}
In summary: run
calls the relevant templated-instance of the virtual method execute_simulation_wrapped
, which then unpacks the stored_inputs
and provides them to the virtual simulation
function which has a customized implementation for each household.
The question that I think I should be asking:
So, I think I've got most of the above set up right, but I've been looking at this for a long time and I still cant work out how the simulation_wrapper::execute_simulation_wrapped
function can make the call to simulation
and provide the unpacked tuple stored_inputs
as a parameter pack.
I know there are SO questions and blogs out there that give details as to how to call a regular function with an unpacked tuple, but I haven't managed to extend this to member functions, and specifically virtual member functions.
TMP is new to me and still thoroughly confusing, so fairly explicit answers would be much appreciated!
This is usually done with a help of index_sequence
:
template <typename... Ts>
class simulation_wrapper : public simulation_base
{
tuple<shared_ptr<Ts>... > stored_inputs{new Ts...};
public:
// MAGIC STARTS HERE
int execute_simulation_wrapped() { return execute_simulation_wrapped(std::make_index_sequence<sizeof...(Ts)>{}); }
private:
template <std::size_t... Is>
int execute_simulation_wrapped(std::index_sequence<Is...>) { return simulation(*std::get<Is>(stored_inputs)...); }
// MAGIC ENDS HERE
protected:
virtual int simulation(const Ts&...){return 0;};
};
If you need an index_sequence
, which is available in <utility>
only since C++14, you can use the below implementation:
template <std::size_t... Is>
struct index_sequence {};
template <std::size_t N, std::size_t... Is>
struct make_index_sequence_h : make_index_sequence_h<N - 1, N - 1, Is...> {};
template <std::size_t... Is>
struct make_index_sequence_h<0, Is...>
{
using type = index_sequence<Is...>;
};
template <std::size_t N>
using make_index_sequence = typename make_index_sequence_h<N>::type;
DEMO
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With