Search code examples
c++c++11templatesgalois-field

Template Parameter Array


I'm trying to create a class that uses the value of an array as a value/type template parameter. See below

template< std::array<bool, M> arr>
class gf {
...
};

I believe my rationale for doing this is sane. This class implements operator overloading for addition and multiplication. These operations are only well defined if the instances of the class were instantiated with the same value and size of arr.

An example usecase:

std::array<bool,3> p1 = {0,0,1};
std::array<bool,3> p2 = {0,1,1};

gf<p1> a;
gf<p1> b;
gf<p2> c;

auto out1 = a + b; // works as both instances use p1
auto out1 = a + c; // Compiler error as we're adding incompatible types

My current work around is passing arr to the constructor call and throwing an error if any incompatible types are combined. I hope there's a better way to do this, thanks!

EDIT: Also, any other design pattern that might accomplish the same goals would be welcomed. I'm not married to using templates specialization, but it's a tool with which I'm familiar!


Solution

  • Purely from a technical aspect, you can(1) pass the address of a constexpr array with static storage duration as a non-type template parameter:

    #include <array>
    
    template<const auto& arr>
    struct gf {
        gf& operator+=(const gf& /* rhs */) {
            // ...
            return *this;
        }
    };
    
    template<const auto& arr>
    auto operator+(gf<arr> lhs, const gf<arr>& rhs) {
        lhs += rhs;
        return lhs;
    }
    
    int main() {
        // As we want to use the address of the constexpr std::array
        // at compile time, it needs to have static storage duration.
        static constexpr std::array<bool, 3U> p1{{0, 0, 1}};
        static constexpr std::array<bool, 3U> p2{{0, 1, 1}};
        
        gf<p1> a;
        gf<p1> b;
        gf<p2> c;
        
        auto out1 = a + b;  // OK.
        //auto out2 = a + c;  // Error: incompatible types.
    }
    

    such that every instantiation of the gf class template with a unique array object will become a unique type (/specialization).

    This relies on C++17 for auto as a template parameter; for a similar C++11 approach:

    #include <array>
    
    template<std::size_t M, const std::array<bool, M>& arr>
    struct gf {
        gf& operator+=(const gf& /* rhs */) {
            // ...
            return *this;
        }
    };
    
    template<std::size_t M, const std::array<bool, M>& arr>
    gf<M, arr> operator+(gf<M, arr> lhs, const gf<M, arr>& rhs) {
        lhs += rhs;
        return lhs;
    }
    
    int main() {
        // As we want to use the address of the constexpr std::array
        // at compile time, it needs to have static storage duration.
        static constexpr std::array<bool, 3U> p1{{0, 0, 1}};
        static constexpr std::array<bool, 3U> p2{{0, 1, 1}};
        
        gf<3U, p1> a;
        gf<3U, p1> b;
        gf<3U, p2> c;
        
        auto out1 = a + b;  // OK.
        //auto out2 = a + c;  // Error: incompatible types.
    }
    

    (1) This answer does not, in any way, try to present this as any kind of good approach for the XY-esque problem of the OP.