Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++11 unpack std::tuple into virtual member function

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!

like image 456
dan-man Avatar asked Dec 18 '14 14:12

dan-man


1 Answers

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

like image 189
Piotr Skotnicki Avatar answered Oct 20 '22 15:10

Piotr Skotnicki