c++template-specializationstatic-assert

Best practice implementation of functions-family in cpp


Preliminary

A functions-family is a countable list of functions y=f_i(x), for a counter i=0,...,n-1 for some integer n.

Minimum example

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; }
};

Question

How can I achieve the following?:

  1. force by virtue of inheritance from Base<Tfloat,3> at compile-time that all three functions of the family are defined.
  2. I need to be able to statically loop through f with, e.g. an 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.
  3. Be able to write the definitions within the body of Derived.

Issues

I know that there are a dozen of issues:

Thus I am very curious for your ideas.

Remarks

Proposal: Function Array

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:

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>.


Solution

  • 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; }
    };
    

    Demo