Reputation: 73
I want to create a system of "Mapper", i.e objects that takes an input (template) value and return a output (template) value. A mapper can have additional features / traits.
Sample code :
// a trait
template <typename I, typename O>
class Writable
{
public:
virtual ~Writable() = default;
virtual O & operator()(I i) = 0;
};
// another trait
template <typename I, typename O>
class Bounded
{
public:
virtual ~Bounded() = default;
virtual O min() const = 0;
virtual O max() const = 0;
};
// base class
template <typename I, typename O, template<typename, typename> class ... Traits>
class IMap
: public Traits<I, O>...
{
public:
virtual ~IMap() = default;
virtual O operator()(I i) const = 0;
};
// a test mapper
class Test1
: public IMap<int, double, Writable, Bounded>
{
public:
double operator()(int i) const override { return _d; }
double & operator()(int i) override { return _d; }
double min() const override { return 0; }
double max() const override { return 1; }
private:
double _d;
};
// another test mapper
class Test2
: public IMap<int, double, Bounded, Writable>
{
public:
double operator()(int i) const override { return _d; }
double & operator()(int i) override { return _d; }
double min() const override { return 0; }
double max() const override { return 1; }
private:
double _d;
};
// a class to use mappers
template <typename I, typename O>
class MapUse
{
public:
// I would like this function to accept an IMap containing "at least" the Bounded trait
// Ideally this function signature should be like
void method_requiring_writable(IMap<I, O, Bounded> & map)
{
map(123)
map(123) = 0;
}
// I would like this function to accept an IMap containing "at least" the Bounded and Writable traits
// (no matter the actual order of those traits in variadic template parameter )
// Ideally this function signature should be like
void method_requiring_bounded_and_writable(IMap<I, O, Bounded, Writable> & map)
{
map(123);
map(123) = 0;
map.min();
}
};
void test()
{
Test1 t1;
Test2 t2;
Test2 * p = dynamic_cast<Test2*>(&t1); // << always return nullptr, of course !
MapUse<int, double> mu;
mu.method_requiring_writable(t1); // << does not compile, not the right type
mu.method_requiring_bounded_and_writable(t1); // << does not compile, not the right type
return;
}
Error at line : mu.method_requiring_writable(t1) :
error C2664: 'void MapUse<int,double>::method_requiring_writable(IMap<I,O,Bounded> &)': cannot convert argument 1 from 'Test1' to 'IMap<I,O,Bounded> &'
> with
> [
> I=int,
> O=double
> ]
Error at line : mu.method_requiring_bounded_and_writable(t1) :
error C2664: 'void MapUse<int,double>::method_requiring_bounded_and_writable(IMap<int,double,Bounded,Writable> &)': cannot convert argument 1 from 'Test1' to 'IMap<int,double,Bounded,Writable> &'
I perfectly understand why this does not work, but I would like a "trick" to make it work !
First problem : Order of traits in variadic template parameter does count. It should easily be solved by forcing the developer to respect traits order, but it's not ideal
Second problem : Be able to create methods that requires a mapped with "at least" a specific set of traits. No clue about this one.
First approach was not variadic, but a - very - complex hierarchy with lots of virtual heritage --> ugly as hell, it's a real pain to add a new trait
With the code above, I thought about adding some kind of cast operators in IMap but failed to do so.
Any C++ / variadic / magic stuff specialist to help on this ?
Upvotes: 2
Views: 79
Reputation: 11516
You can use something like this:
#include <type_traits>
template <template <class, class> class... Traits,
std::enable_if_t<
std::is_base_of<Bounded<I, O>, IMap<I, O, Traits...>>::value
and std::is_base_of<Writable<I, O>, IMap<I, O, Traits...>>::value
, int> = 0>
void method_requiring_bounded_and_writable(IMap<I, O, Traits...> & map) {
// Do stuff
}
Upvotes: 1