A functions-family is a countable list of functions y=f_i(x)
, for a counter i=0,...,n-1 for some integer n.
I have a derived struct in c++ that shall define such a functions-family for n=3
:
template<typename Tfloat, size_t n> Base{
template<size_t i> bool f(Tfloat* x){ static_assert(i<n); return false; }
};
template<typename Tfloat>
struct Derived: Base<Tfloat,3>{
template<> bool f<0>(Tfloat* x){ x[3]-=x[2]; return true; }
template<> bool f<1>(Tfloat* x){ x[1]*=x[3]; return true; }
template<> bool f<2>(Tfloat* x){ if(x[5]==0) return false; x[2]/=x[5]; return true; }
};
How can I achieve the following?:
std::sequence<n>
. Until now, I have given a different name f0, f1,...
to each function. But then one must manually write an if constexpr(i=0){ return f0(x); }
wrapper to traverse through all names in the definition of template<size_t i> f(Tfloat* x)
. Acutally, this is how I am doing it as of now.I know that there are a dozen of issues:
f<i>
for i>=n
can be statically prohibited, I see no way to enforce definedness for all i=0,...,n-1
.Thus I am very curious for your ideas.
i
have very little to do with one another; e.g., physical models for the i
-th stage of a rocket. I prefer the use of template for size_t i
.Taken from @HolyBlackCat's comment:
#include<iostream>
#include<functional>
#include<array>
template<typename Tfloat, size_t n>
class Base{
protected:
std::array<std::function<bool(Tfloat*)>,n> f_;
public:
// probably here in conjunction with an invalid default initialize for each element of f_, CRTP could be used to assert after construction of derived that each f_[i] is defined?
template<size_t i>
bool f(Tfloat* x){ return f_[i](x); } // accessor prevents external overwrite into f_.
};
template<typename Tfloat>
class Derived: public Base<Tfloat,3>{
public:
//
Derived(){
Base<Tfloat,3>::f_[0] = [this](Tfloat* x){
x[0] *= x[1];
return true;
};
}
//
};
template<typename Tfloat>
struct NaiveDerived{
//
bool f0(Tfloat* x){
x[0] *= x[1];
return true;
}
//
};
int main(){
Derived<float> d;
float x[10];
d.f[0](x);
}
This code works and achieves the requirements 2 and 3.
I do not like the use of a function array much because eventually we want to marry two things:
{ x[0]*=x[1]; return true; }
f<i>(x)
Introducing f_
into the mix only accomplishes to avoid giving names to each function body before we assign said body to its respective f<i>
. More readable would be a solution that states the i
-th function body directly with f<i>
.
If you might change signature, i.e calling d.f(IC<0>{}, x);
and not d.f<0>(x);
Following might help, using virtual
to ensure functions existence:
// shorten code
template <std::size_t N>
using IC = std::integral_constant<std::size_t, N>;
// helper node I
template <typename T, size_t I> struct BaseNode
{
virtual ~BaseNode() = default;
virtual bool f(IC<I>, T* x) = 0;
};
template<typename T, typename Seq> struct BaseImpl;
template<typename T, std::size_t... Is>
struct BaseImpl<T, std::index_sequence<Is...>> : BaseNode<T, Is>...
{
using BaseNode<T, Is>::f...;
};
// Use expecting name
template<typename T, std::size_t N>
using Base = BaseImpl<T, std::make_index_sequence<N>>;
// The derived class
template<typename Tfloat>
struct Derived : Base<Tfloat,3>{
bool f(IC<0>, Tfloat* x) override { x[3]-=x[2]; return true; }
bool f(IC<1>, Tfloat* x) override { x[1]*=x[3]; return true; }
bool f(IC<2>, Tfloat* x) override { if(x[5]==0) return false; x[2]/=x[5]; return true; }
};