I am working on a pipeline-like design pattern. One of my design goals is to enable dynamic linking of pipeline segments by providing pointers to function members of a certain data class.
Each of the data classes has a set of function members (representing the data class output ports) indexed using an integer template argument. These functions deduce the return type dynamically using keyword auto
, but all accept the same integer argument c_Idx
, i.e. template <int N> auto getOutput(int c_Idx) const
. The functionality associated with each function getOutput
is defined (by the user) in a set of partially specialised structures getOutputImpl
. Thus, each data class can have from 1 up to some fixed number K
of output data ports.
In order to allow for dynamic linking between the pipeline segments in a generic manner they can be stored in a container of the type std::vector<boost::any>
. However, I need to be able to fill this vector with pointers to the function member templates automatically.
An example of a manual implementation is shown below
template<class TLeafType>
class AlgorithmOutput
{
protected:
std::vector<boost::any> OutputPorts;
public:
AlgorithmOutput()
{
//////////////////////////////////////////
// This procedure needs to be automated //
//////////////////////////////////////////
std::function<std::unique_ptr<double>(int)> pOutFun1 = std::bind(
std::mem_fn(
true ? &AlgorithmOutput<TLeafType>::getOutput<0> : nullptr
),
this,
std::placeholders::_1
);
OutputPorts.push_back(pOutFun1);
std::function<std::unique_ptr<int>(int)> pOutFun2 = std::bind(
std::mem_fn(
true ? &AlgorithmOutput<TLeafType>::getOutput<1> : nullptr
),
this,
std::placeholders::_1
);
OutputPorts.push_back(pOutFun2);
}
virtual ~AlgorithmOutput() {}
protected:
TLeafType* asLeaf(void)
{
return static_cast<TLeafType*>(this);
}
TLeafType const* asLeaf(void) const
{
return static_cast<TLeafType const*>(this);
}
public:
template <int N>
auto getOutput(int c_Idx) const
{
return asLeaf() -> getOutput<N>(c_Idx);
}
boost::any getOutputPort(int PortIdx)
{
return OutputPorts[PortIdx];
}
};
class PipeOutputClass: public AlgorithmOutput<PipeOutputClass>
{
public:
template <int N>
auto getOutput(int c_Idx) const
{
return getOutputImpl<N>::apply(this, c_Idx);
}
template<int N, typename S> friend struct getOutputImpl;
template<int N, typename = void>
struct getOutputImpl
{
static auto apply(
PipeOutputClass const* p_Self,
int c_Idx
)
{ throw std::runtime_error("Wrong template argument."); }
};
template <typename S>
struct getOutputImpl<0, S>
{
static std::unique_ptr<double> apply(
PipeOutputClass const* p_Self,
int c_Idx
)
{
std::unique_ptr<double> mydouble(new double(10));
return mydouble;
}
};
template <typename S>
struct getOutputImpl<1, S>
{
static std::unique_ptr<int > apply(
PipeOutputClass const* p_Self,
int c_Idx
)
{
std::unique_ptr<int > myint(new int(3));
return myint;
}
};
};
The problem with the example above is that I define the member function pointers pOutFunX
manually, whereas I would like to automate this procedure.
Please note that I am not considering design solutions which differ significantly from the design specified above.
Here I present some thoughts on some of the possible approaches for solving this problem. I developed a plan for a solution that I am currently considering, which may be of use if you attempt to answer this question:
- Obtain the number of the user defined partially specialised structures named
getOutputImpl
. - For each such structure determine the output type of its member named
apply
. - Set up a (recursive) meta-template procedure that creates pointers to functions with the relevant signature and adds them to the
OutputPort
vector.
I would assume that the steps 1-3 above will all have to be done at compile time. I am not concerned about the aesthetics of the solution, if it does not require any intervention from the user designing the data output classes. However, I would prefer not to use custom compiler macros.
This post shows how one can infer a member function signature, which may be useful.