У меня есть библиотека, которая выполняет операции с пикселями. Пиксели могут использоваться во многих разных форматах. Я ищу эффективный способ описания форматов в библиотечном API (внутри и снаружи).
Для некоторых классов формат пикселей является аргументом шаблона, для других это аргумент времени выполнения. Таким образом, форматы пикселей должны использоваться как во время выполнения (как конструктор, так и в аргументе функции) и времени компиляции (в качестве аргумента шаблона). Я хочу описывать пиксельные форматы только один раз.
Теперь у меня есть что-то вроде этого:
enum class color_space : uint8_t { rgb, cmyk /* , etc... */ };
struct pixel_layout {
color_space space;
uint8_t channels;
/* etc... */
};
template <color_space ColorSpace, uint8_t Channels /* etc.. */>
struct pixel_type {
static constexpr color_space space = ColorSpace;
static constexpr uint8_t channels = Channels;
/* etc... */
static constexpr pixel_layout layout() {
return {space, channels /* , etc... */ };
}
};
struct rgb : public pixel_type<color_space::rgb, 3 /* , etc... */ > {};
struct rgba : public pixel_type<color_space::rgb, 4 /* , etc... */ > {};
Это работает довольно хорошо. Я могу использовать их как параметры времени выполнения и компиляции:
template <class PixelType>
class image { };
struct transform {
transform(const pixel_layout from, const pixel_layout to)
: from(from), to(to) { /* ... */ }
pixel_layout from;
pixel_layout to;
};
Также конвертировать из типа времени компиляции в тип выполнения:
transform(rgb::layout(), rgba::layout());
Однако, дублирование и сохранение деталей pixel_layout
типов пикселей, когда они используются во время выполнения, кажутся мне глупыми. Концептуально для всей программы необходимо указать ID/адрес/ссылку на конкретный pixel_type
и способ получить связанные свойства (цветовое пространство, каналы и т.д.) Во время компиляции и времени выполнения.
Кроме того, если я хочу получить производное свойство из типа пикселя, мне нужно реализовать его на pixel_layout
, если я хочу избежать дублирования логики. Затем, чтобы использовать его во время компиляции, мне нужно перейти от класса pixel_type<...>
к экземпляру pixel_layout
к производному свойству. Это тоже кажется немного глупым.
Могу ли я обойти детали pixel_layout
и вместо этого использовать некоторую ссылку на классы pixel_type<...>
(sub)?
Я попытался использовать enum
s, потому что перечисления работают как аргумент шаблона и аргумент функции. Но я изо всех сил пытался получить значение enum (например, rgba
) для свойства типа пикселя (например, 4 канала) во время выполнения и время компиляции в идиоматическом режиме С++.
Кроме того, перечисления как аргументы шаблона дают гораздо менее полезную диагностику во время ошибки компиляции. Например, я получаю image<(pixel_type)2>
, а не image<rgba>
в компиляции сообщений об ошибках с clang. Таким образом, это не похоже на полезный подход.