I'm trying to create an abstraction for peripherals in an embedded project I'm working on. The thing is, not all of them behave in the same way nor have the same interface.
I'm trying to use CRTP with the Static Decorator design pattern, but I'm stuck on the CommunicableDevice constructor, as I don't know how to call multiple base class constructors with variadic templates.
#include <iostream>
struct Interface
{
Interface(int pin) {}
virtual void write()
{
}
virtual void read()
{
}
};
struct RTSCTSInterface : public Interface
{
template <typename ... Args>
RTSCTSInterface(int rts, Args&& ... args)
: Interface(std::forward<Args>(args)...), rts(rts)
{}
void write() override
{
}
int rts;
};
struct Powerable
{
Powerable(int dummy) : dummy(dummy)
{}
void turnOn()
{
}
int dummy;
};
struct Device
{
Device(const std::string& name) : name(name){}
std::string name;
};
template <typename CustomDevice>
struct PowerableDevice : public Powerable, public CustomDevice
{
template <typename ... Args>
PowerableDevice(int dummy, Args&&... args)
: Powerable(dummy), CustomDevice(std::forward<Args>(args)...)
{}
};
template <typename CustomInterface, typename CustomDevice>
struct CommunicableDevice : public CustomInterface, public CustomDevice
{
template <typename ... Args>
CommunicableDevice(Args&&... args)
// call constructors with args
{
}
};
int main()
{
CommunicableDevice<RTSCTSInterface, PowerableDevice<Device>> dev(1, 2, 300, 5, "dummy");
}
Is it actually possible? If so, how do I do it? I'm also open to suggestions on how to approach this problem.
I don't know how to call multiple base class constructors with variadic templates.
template <typename CustomInterface, typename CustomDevice>
struct CommunicableDevice : public CustomInterface, public CustomDevice
{
template <typename ... Args>
CommunicableDevice(Args&&... args)
// call constructors with args
{
}
};
I don't see a general solution.
The problem is: which argument are for the first base class and which for second?
The only solution that come in my mind is to wrap the arguments for every base class in a std::tuple
. Then use delegating constructors and index sequences to extract they.
I mean something as (simplified example: no perfect forwarding)
template <typename CI, typename CD>
struct foo : public CI, public CD
{
private:
template <typename ... As, typename ... Bs, std::size_t ... Ia,
std::size_t ... Ib>
foo (std::tuple<As...> const & ta, std::tuple<Bs...> const & tb,
std::index_sequence<Ia...>, std::index_sequence<Ib...>)
: CI(std::get<Ia>(ta)...), CD(std::get<Ib>(tb)...)
{ }
public:
template <typename ... As, typename ... Bs>
foo (std::tuple<As...> const & ta, std::tuple<Bs...> const & tb)
: foo(ta, tb, std::index_sequence_for<As...>{},
std::index_sequence_for<Bs...>{})
{ }
};
The bad part is you have to call the constructor making tuples
foo<bar1, bar2> f{std::make_tuple(1, 2l, "3", 4ull), // <-- for bar1
/* for bar2 --> */std::make_tuple(5, "6", std::vector<int>{7, 8, 9})};
The following is a full compiling C++14 example
#include <tuple>
#include <string>
#include <vector>
#include <type_traits>
template <typename CI, typename CD>
struct foo : public CI, public CD
{
private:
template <typename ... As, typename ... Bs, std::size_t ... Ia,
std::size_t ... Ib>
foo (std::tuple<As...> const & ta, std::tuple<Bs...> const & tb,
std::index_sequence<Ia...>, std::index_sequence<Ib...>)
: CI(std::get<Ia>(ta)...), CD(std::get<Ib>(tb)...)
{ }
public:
template <typename ... As, typename ... Bs>
foo (std::tuple<As...> const & ta, std::tuple<Bs...> const & tb)
: foo(ta, tb, std::index_sequence_for<As...>{},
std::index_sequence_for<Bs...>{})
{ }
};
struct bar1
{
template <typename ... Ts>
bar1 (int, long, Ts...)
{ }
};
struct bar2
{
template <typename ... Ts>
bar2 (int, std::string, Ts...)
{ }
};
int main ()
{
foo<bar1, bar2> f{std::make_tuple(1, 2l, "3", 4ull),
std::make_tuple(5, "6", std::vector<int>{7, 8, 9})};
}