Cave Dweller
Cave Dweller

Reputation: 542

C++ Nested Primitive Type Templates

I've been working on a pixel format and canvas system using templates with primitive type arguments, that is:

enum class PIXEL_FORMAT : char {
    PALETTE,
    RGB,
    RGBA
};

template<unsigned int bit_depth, PIXEL_FORMAT fmt> struct Pixel {};

//specializations for 8-bit, 16-bit, etc. RGB/RGBA formats

These all work perfectly fine, but when I attempted to create a Layer struct:

template< template<unsigned int depth, PIXEL_FORMAT fmt> class PixelType > struct Layer {
using pixel = PixelType<depth, fmt>;
pixel** pixels;

The errors I got were:

F:\Personal Projects (Java or Other)\C_C++\Independent\GUI Apps\FreeArt\Canvas.h|7|error: 'depth' was not declared in this scope
F:\Personal Projects (Java or Other)\C_C++\Independent\GUI Apps\FreeArt\Canvas.h|7|error: 'fmt' was not declared in this scope
F:\Personal Projects (Java or Other)\C_C++\Independent\GUI Apps\FreeArt\Canvas.h|7|error: template argument 1 is invalid
F:\Personal Projects (Java or Other)\C_C++\Independent\GUI Apps\FreeArt\Canvas.h|7|error: template argument 2 is invalid

So obviously I'm not understanding the way nested templates using primitive types as parameters work. I've looked for similar issues, but can't seem to find anything on using primitive types instead of regular template arguments (typename T, etc).

I'm hoping this can be used for other types with templates using primitive type arguments, for example a possible

template<template<unsigned int bit_depth, double samp_per_sec> class Sound> struct SoundEffect {};

Upvotes: 2

Views: 332

Answers (1)

n. m. could be an AI
n. m. could be an AI

Reputation: 119877

If you want to use a single pixel type in a layer, then you need this:

template < typename PixelType > struct Layer {
   ...
};

using RGBA8Layer = Layer < Pixel<8, RGBA> >;

If you need to access bit_depth and format from within Layer, define static const members in either Pixel or in a separate "traits" template.

The construct you are trying to use is called "template template parameter". They are needed when you have potentially many templates with the same signature:

template <unsigned int bit_depth, PIXEL_FORMAT fmt> struct Pixel {};
template <unsigned int bit_depth, PIXEL_FORMAT fmt> struct FancyPixel {};
template <unsigned int bit_depth, PIXEL_FORMAT fmt> struct FastPixel {};
template <unsigned int bit_depth, PIXEL_FORMAT fmt> struct PackedPixel {};

and you wanted to use any of them with Layer, and you wanted to select depth and format from inside the layer, then you would use this:

template< template<unsigned int, PIXEL_FORMAT> class PixelType > 
struct Layer {
    using MyNormalPixel = PixelType<8, RGB>;
    using MyHighDefinitionPixel = PixelType<16, RGBA>;
};

using FancyLayer = Layer<FancyPixel>;
using PackedLayer = Layer<PackedPixel>;

Notice how parameter names in PixelType are omitted — they cannot be used in the code at all. The only thing they are useful for is documentation. The situation is analogous to that of function arguments:

// signature off the top of my head
double integrate (double func(double), 
                  double from, double to, double epsilon, double step);

Indeed, templates are in essence functions in the types domain and "template template parameters" are analogous to "function function parameters" like func above.

Upvotes: 3

Related Questions