I would like to write an abstraction of linear superpositions using variadic templates. To do that, I would like to define a base type that exhibits a certain form of operator() like so
template <typename Result, typename... Parameters>
class Superposable {
public:
typedef Result result_t;
void operator()(Result& result, const Parameters&...) const = 0;
};
then inherit from it for the current problem, for instance like so
class MyField : public Superposable<double, double, double> {
public:
void operator()(double& result, const double& p1, const double& p2) const {
result = p1 + p2;
}
};
And I would then like to write an abstract base class that can form linear superpositions and gets the Superposable-derived class as a template parameter to determine the call signature of operator(). I would like to have something like
template<typename S> // where S must be inherited from Superposable
class Superposition {
private:
std::vector< std::shared_ptr<S> > elements;
public:
// This is the problem. How do I do this?
void operator()(S::result_t& result, const S::Parameters&... parameters) const {
for(auto p : elements){
S::result_t r;
p->operator()(r, parameters);
result += r;
}
}
};
Here are my questions:
Thanks for your help!
First, a solution to your problem directly.
Some metaprogramming boilerplate:
template<class...>struct types{using type=types;};
A hana-style function that extracts the types of an argument, optionally taking a template from which to do the extraction based on:
template<template<class...>class Z, class...Args>
constexpr types<Args...> extract_args( Z<Args...> const& ) { return {}; }
An alias that wraps the above in a decltype for ease of use:
template<template<class...>class Z, class T>
using extract_args_from = decltype( extract_args<Z>( std::declval<T>() ) );
The primary template of Superposition is left empty, with a default argument that extracts the type arguments of Superposable:
template<class S, class Args=extract_args_from<Superposable, S>>
class Superposition;
then a specialization that gets the Result and Parameters types of the (possibly base class of) S's Superposable:
template<class S, class Result, class...Parameters>
class Superposition< S, types<Result, Parameters...>> {
live example. Btw, you missed a virtual.
Note that I don't approve of your design -- the result should be the return value (naturally), making () virtual seems like a bad idea (I'd use CRTP instead, and type-erase if I really need to hide the particular implementation).
You can remove the body of Superposable and it works as-is:
public:
typedef Result result_t;
void operator()(Result& result, const Parameters&...) const = 0;
which I'd recommend at the least.
The next step is to get rid of inheritance and deducing Parameters at all:
class MyField {
public:
double operator()(const double& p1, const double& p2) const {
return p1 + p2;
}
};
template<typename S> // where S must be inherited from Superposable
class Superposition {
private:
std::vector< std::shared_ptr<S> > elements;
public:
template<class...Parameters,
class R=std::result_of_t<S&(Parameters const&...)>
>
R operator()(const Parameters&... parameters) const {
R ret = {};
for(auto p : elements){
ret += (*p)(parameters...);
}
return ret;
}
};
which has all the flaws of perfect forwarding, but all the advantages as well.
std::result_of_t<?> is C++14, but replace it with typename std::result_of<?>::type in C++11 as a drop-in.
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