Search code examples
c++templatesrangevariadic-templatesparameter-pack

Pass data via template argument, C++


I am thinking about having color spaces structs in my program. Now as you can see each color space has its limitations for each of its component. So consider a struct like this.

template<typename T>
struct SomeColorSpace{
    T component1,component2,.....; // or std::array<T,N> components, N is compile-time const
};

Now I need somehow define a range so I create a struct.

template<typename T>
struct Range{

    T min, max;

    // Some constructors initializing min and max...
};

Now I need my color space struct to know what is the range of each of its component. But as you see I can't just hold std::array<Ranges<T>,N> ranges in my struct as the ranges differ from type to type. As an example consider struct RGB each of the component may be float or unsigned char so when the components are float the ranges of each component becomes [0,1]; and when it is unsigned char it is [0,255];. Writing template specialization for each of these case is a solution but instead I want a signature like this. using RGBFloat = RGB<float,Range<float>...>. I mean I want to pass the ranges via template parameter pack. I know that for instance Range<float>(0,1); is a non-type template parameter so one workaround is to refactor struct Range like this

template<typename T,T _MIN, T _MAX>
struct Range{
    static constexpr T MIN = _MIN;
    static constexpr T MAX = _MAX;
};

So I can pass Range<T,T min, T max> as template parameter pack, but then I have to do keep the pack in a std::tuple(I do not want this either). My question is do you see any other possibility via refactoring the Range struct or something else to have this signature of defining a color space. using DefineColorSpace = ColorSpace<unsigned char, ranges....>. I know that C++ 20 has huge refactors for non-type template parameters , but I am using clang and it seem that they do not support that feature yet. Any advice can be helpful, thanks)


Solution

  • Support for literal classes as non-type template parameter is already in clang trunk. See it live on godbolt:

    template<typename T>
    struct Range
    {
        T min, max;
    };
    
    template<typename T, Range<T> R>
    struct SomeColorSpace
    {
        static constexpr Range<T> r = R;
    
        T component1, component2;
    };
    
    using Rgb = SomeColorSpace<float, Range<float>{0.0f, 1.0f}>;
    
    auto test()
    {
        Rgb color{0.4f, 0.1f};
    
        float c1 = color.component1;
        float min = Rgb::r.min;
    
        return Rgb::r.max;
    }
    

    So it will arrive soon in clang. Until then a possible workaround is to use traits. See it live on godbolt:

    template<class T, class Trait>
    struct SomeColorSpace
    {
        static constexpr T min = Trait::min;
        static constexpr T max = Trait::max;
    
        T component1, component2;
    };
    
    struct RgbTrait
    {
        static constexpr float min = 0.0f;
        static constexpr float max = 1.0f;
    };
    
    using Rgb = SomeColorSpace<float, RgbTrait>;
    
    auto test()
    {
        Rgb color{0.4f, 0.1f};
    
        float c1 = color.component1;
        float min = Rgb::min;
    
        return Rgb::max;
    }