Я думаю о том, чтобы в моей программе были структуры цветовых пространств. Теперь, как вы можете видеть, каждое цветовое пространство имеет свои ограничения для каждого из его компонентов. Итак, рассмотрим такую структуру.
template<typename T>
struct SomeColorSpace{
T component1,component2,.....; // or std::array<T,N> components, N is compile-time const
};
Теперь мне нужно как-то определить диапазон, поэтому я создаю структуру.
template<typename T>
struct Range{
T min, max;
// Some constructors initializing min and max...
};
Теперь мне нужно, чтобы моя структура цветового пространства знала, каков диапазон каждого ее компонента. Но, как видите, я не могу просто хранить std::array<Ranges<T>,N> ranges
в своей структуре, так как диапазоны различаются от типа к типу. В качестве примера рассмотрим struct RGB
каждый из компонентов может быть float
или unsigned char
, поэтому, когда компоненты float
, диапазоны каждого компонента становятся [0,1];
, а когда это беззнаковый символ, это [0,255];
. Написание специализации шаблона для каждого из этих случаев является решением, но вместо этого я хочу подобную подпись. using RGBFloat = RGB<float,Range<float>...>
. Я имею в виду, что хочу передать диапазоны через пакет параметров шаблона. Я знаю, что, например, Range<float>(0,1);
— это параметр шаблона, не являющийся типом, поэтому одним из обходных путей является рефакторинг struct Range
следующим образом.
template<typename T,T _MIN, T _MAX>
struct Range{
static constexpr T MIN = _MIN;
static constexpr T MAX = _MAX;
};
Таким образом, я могу передать Range<T,T min, T max>
как пакет параметров шаблона, но тогда мне нужно сохранить пакет в std::tuple
(я этого тоже не хочу). Мой вопрос: видите ли вы какую-либо другую возможность с помощью рефакторинга структуры Range
или чего-то еще, чтобы иметь эту сигнатуру определения цветового пространства. using DefineColorSpace = ColorSpace<unsigned char, ranges....>
. Я знаю, что в C++ 20
есть огромные рефакторинги для нетиповых параметров шаблона, но я использую clang
, и кажется, что они пока не поддерживают эту функцию.
Любой совет может быть полезен, спасибо)
Поддержка литеральных классов в качестве параметра шаблона, отличного от типа, уже есть в clang trunk. Смотрите в прямом эфире на 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;
}
Так что он скоро прибудет в clang. До тех пор возможным обходным путем является использование трейтов. Смотрите в прямом эфире на 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;
}
Не совсем понятно, что вы хотите, что касается цвета, r/g/b/(a) будет иметь одинаковый диапазон в зависимости от типа.
Таким образом, простые черты позволяют отображать минимум/максимум float
в [0.f,1.f]
и std::uint8_t
в [0,255]
:
template <typename T> struct RangeTraits;
template <> struct RangeTraits<double>
{
constexpr double min = 0.;
constexpr double max = 1.;
};
template <> struct RangeTraits<std::uint8_t>
{
constexpr std::uint8_t min = 0;
constexpr std::uint8_t max = 255;
};
// ...
И
template <typename T>
struct SomeColorSpace{
std::array<T, N> components;// N is compile-time constant
// use RangeTraits<T>::min, RangeTraits<T>::max when needed.
};
Другая интерпретация заключается в том, что ваш параметр шаблона является «просто» значением по умолчанию для инициализации:
template <typename T, T DefaultMin, T DefaultMax>
struct Range{
T min = DefaultMin;
T max = DefaultMax;
// Some constructors initializing min and max...
};
но эти значения по умолчанию следует забыть потом
Итак, вы можете сделать что-то вроде:
std::array<Range<std::int8_t>, 2> components{Range<std::int8_t, 0, 127>, Range<std::int8_t, -10, 10>};
Тогда вы можете сделать:
template <typename T, T...> struct Range;
template <typename T>
struct Range<T>
{
T min;
T max;
Range(T min, T max) : min(min), max(max) {}
template <T Min, T Max,>
Range(Range<T, Min, Max>& rhs) : min(rhs.min), max(rhs.max) {}
Range(Range& rhs) = default;
// ...
};
template <typename T, T Min, T Max>
struct Range<T, Min, Max>
{
T min = Min;
T max = Max;
Range() = default;
Range(Range& rhs) = default;
// Possibly keep constructors of conversion and the one with runtime min/max
// ...
};