Modern Ways of Handling Status and Error Conditions

The post Old School Ways of Handling Status and Error Conditions looked at ways of handling statuses and error conditions in functions and subroutines. Each of these methods worked with pre-C++11 compilers and still work with post-C++11 compilers.

This post will investigate returning statuses and error conditions using functionality that was added in C++11, C++14, and C++17.

std::pair and std::tuple

std::pair is a templated struct that was available priory to C++11, but additional functionality was added in C++11 and C++14, so it is discussed here. Also, std::tuple was added in C++11. A tuple may have any number of elements including 0. std::pair is basically a two-value std::tuple with an additional way of accessing the two values.

Generating a Pair

std::pair<int, vector<float>> foo()
{
    int status {OK};
    vector<float> values {1.0f, 3.14f};
    // do something
    return std::pair<int, vector<float>>(status, values);
}

Alternatively, the return statement in foo could be:

    return std::make_pair(status, values);

make_pair is the preferred method of creating a pair because the value types do not have to be specified; in the constructor, they do.

Even:

    return {status, values};

will work as long as the return type for foo is specified. That is, this final way of generating the pair will not work if foo was declared as:

auto foo();

instead of:

std::pair<int, vector<int>> foo();

 

Generating a Tuple

All of the methods shown above work for generating a std::tuple if you replace pair with tuple. In addition to those, there is one other:

    return std::tie(status, values);

Accessing Values in Pairs and Tuples

There are a number of ways of accessing the values in pairs and tuples. Each subsection below assumes the function foo created in Generating a Pair or Generating a Tuple, above.

first, second

std::pair comes with two member objects, first and second, to retrieve the values in the pair. This is how to use them:

std::pair<int, vector<float>> retValues = foo();
int status = retValues.first;
vector<float> values = retValues.second;
float value1 = retValues.second[1];    // 3.14

There are two weaknesses with first and second:

  1. They only exist for std::pair and not std::tuple; and,
  2. It is difficult at first glance to know what first and second represent.

std::get

Here is how to use std::get:

std::pair<int, vector<float>> retValues = foo();
// retrieve items by index
int status = std::get<0>(retValues);
vector<float> values = std::get<1>(retValues);
float value1 = std::get<1>(retValues)[1];    // 3.14

// retrieve items by type
int newStatus = std::get<int>(retValues);
vector<float> newValues = std::get<vector<float>>(retValues);
float newValue1 = std::get<vector<float>>(retValues)[1];    // 3.14

std::get works with both std::pair and std::tuple. Code that retrieves values by type will only compile if all types in the pair or tuple are different.

As with first and second for std::pair, it is difficult at first glance to know what the various gets represent.

You may want to read Arne Mertz’s post entitled Smelly std::pair and std::tuple where he covers some of the same problem areas.

std::tie

Here is how to use std::tie to retrieve values from pairs or tuples:

int status;
vector<float> values;
std::tie(status, values) = foo();

The types specified for the variables in std::tie must match the types returned by foo or there must be implicit conversions from the types returned by foo to the types of the variables in tie.

Structured Bindings

Structured bindings are new to C++17. Here is how to use structured bindings:

auto [status, values] = foo();

Structured bindings are available in clang 4, and gcc 7. Structured bindings will be recognized as of Visual Studio 2017 Update 3.

std::optional

In Old School Ways of Handling Status and Error Conditions, I discussed special output value. In that case, there is a single return value that indicates that an operation was not successful. Assume you have a configuration file that you read to retrieve the value for some key. Let’s say there are three possible “states” for the key-value pair, each of which has a different meaning:

  1. The key is in the file and has a value set for it.
  2. The key is in the file, but no value is set.
  3. The key is not in the file.

The special output value technique will not handle this because there are two special values. As an example, let’s say that the value is a string. If you return a non-empty string, that is the value that is set. If you return an empty string, does that mean that the key is in the file but has no value set, or that the key is not in the file? Note that I state that a key is in the file but has no value has a different meaning than the key is not in the file.

This is where std::optional is useful. std::optional may or may not contain a value. Using it with the example above, I could return a string containing the value in the file, either the set value or empty. If the optional value is not set, that is an indication that the key is not in the file.

Here is an even simpler example::

std::optional<std::string> optionalFunc(int key)
{
    switch (key) {
        case 1:
            return "key = 1"s;
        case 2:
            return ""s;
        default:
            return {};
    }
}

and how to use it:

 auto opt1 = optionalFunc(1);
 if (opt1) {
     std::cout << "Value returned for key=1: " << opt1.value() << '\n';
 }
 auto opt2 = optionalFunc(2);
 if (opt2.has_value()) {
     std::cout << "Value returned for key=2: " << opt2.value() << '\n';
 }
 auto opt3 = optionalFunc(3);
 if (!opt3) {
     std::cout << "No value returned for key=3\n";
 }

The output is:

Value returned for key=1: key = 1
Value returned for key=2:
No value returned for key=3

std::variant

std::variant is a type-safe union. Here is a simple function that returns a std::variant, either a float value or an int error code:

std::variant<int, float> variantFunc(int key)
{
    switch (key) {
        case 1:
            return 10.0f;
        case 2:
            return 0.0f;
        default:
            return 16; // some error code
    }
}

and here is some code that calls variantFunc:

 for (int key : {1, 2, 3}) {
     auto var = variantFunc(key);
     try {
         float value = std::get<float>(var); // or get<1>(var);
         std::cout << "value for key=" << key << ": " << value << '\n';
    }
    catch (std::bad_variant_access&) {
        int errCode = std::get<int>(var); // or get<0>(var);
        std::cout << "Error code for key=" << key << ": " << errCode << '\n';
    }
 }

Here is the output from this code:

value for key=1: 10
value for key=2: 0
Error code for key=3: 16

Note that if you try to retrieve the wrong type from the variant, a std::bad_variant_access exception is thrown.

Instead of std::get, you could use std::get_if which returns a pointer to the value, or nullptr, so the code above could be written:

for (int key : {1, 2, 3}) {
     auto var = variantFunc(key);
     auto floatPtr = std::get_if<float>(var);
    if(floatPtr != nullptr) {
         std::cout << "value for key=" << key << ": " << *floatPtr << '\n';
    }
    else {
        auto intPtr = std::get_if<int>(var);
        if(intPtr != nullptr) {
            std::cout << "Error code for key=" << key << ": " << *intPtr << '\n';
        }
    }
}

std::any

Similar to std::variant is std::any. In the simple function below, either a float value or an int error code is returned:

std::any anyFunc(int key)
{
    switch (key) {
        case 1:
        case 2:
            return 10.1f * key;
        default:
            return 4; // some error code
    }
}

and here is some code that calls this function and uses the returned value:

 for (int key : {1, 2, 3}) {
     auto var = anyFunc(key);
     try {
         float value = std::any_cast<float>(var);
         std::cout << "value for key=" << key << ": " << value << '\n';
     }
     catch (std::bad_any_cast&) {
         int errCode = std::any_cast<int>(var);
         std::cout << "Error code for key=" << key << ": " << errCode << '\n';
     }
 }

Here is the output from running this code:

value for key=1: 10.1
value for key=2: 20.2
Error code for key=3: 4

Note that std::any_cast will throw a std::bad_any_cast exception if you attempt to cast the std::any value to a type that it does not contain.

Alternatively, you can check the type of the value stored in your std::any variable by calling std::any::type. For example:

for (int key : {1, 2, 3}) {
     auto var = anyFunc(key);
     if(var.type() == typeid(float)) {
         float value = std::any_cast<float>(var);
         std::cout << "value for key=" << key << ": " << value << '\n';
     }
     else if(var.type() == typeid(int)) {
         int errCode = std::any_cast<int>(var);
         std::cout << "Error code for key=" << key << ": " << errCode << '\n';
     }
 }

Summary

Both this post and the Old School Ways of Handling Status and Error Conditions post looked at ways of returning statuses or error codes from a subroutine or function that might also return values. The old school ways still work, but modern C++ (C++11 and later) has provided many more ways of returning either a status or error code, or a valid value.

So, what would I use? For cases where there is a special output value that indicates an operation has failed, then I would use std::optional with no value indicating the failure. For functions where the returned value could be either a valid value or one of a number of different statuses or error codes, then I would use std::pair or std::tuple along with structured bindings (or std::tie until structured bindings are available in Visual C++) to retrieve the values. These object types are the simplest to use; there is no question about what the returned values are, and are the simplest to retrieve the values from. For error conditions where program termination or lengthy recovery procedures are needed, then I would throw an exception to indicate the error condition.

More on Naked Primitives

The post Strong Typing or Naked Primitives showed an example of problems that can occur when using common variable types (or if you prefer, built-in data types) as argument types in methods and functions, and a potential solution. The post discussed a Size struct with the constructor:

struct Size {
    Size(const uint32_t width, const uint32_t height);
};

and the possible problems that can occur, such as placing the height argument before the width argument.

User Defined Literals continued the example by showing how to use and convert different unit types in the arguments. Specifically, the best way to supply the width and height arguments in units of pixels, inches, and centimetres.

This post will continue to investigate ways of specifying arguments to remove the confusion that can occur when using common variable types as arguments.

Character String to Enumeration

In this post, we will declare a class that partially encapsulates the functionality of C File I/O. A good introduction to C File I/O is provided by programiz.com.

Here is a simple first File class and how to use it. We are interested only in the arguments; the actual implementation is not included. That is up to you to provide.

class File
{
public:
    File(const char* fileName, const char* mode) 
        { /* create file if necessary, and open it */}
    ~File() { /* close file */}
    void print(const char* charString, bool appendReturn) 
        { /* print charsString */}
};

int main()
{
    File file1("file1", "a");
    file1.print("A line", true);
    File file2("file2", "w");
    file2.print("A line, no return", false);
    File file3("file3", "xmf_");
    return 0;
}

The constructor for file1 will create the file if it does not exist, then move the cursor for writing to the file to past the last character in the file. The constructor for file2 will create the file if it does not exist, or erase the contents of the file if it does exist, and place the write cursor at the beginning of the file. The constructor for file3 contains invalid characters in the mode field, so you would have to add code to the File constructor to handle this.

Another problem with the constructor as it is declared is that there are two arguments of type const char* so if you specify the arguments in the wrong order when calling the constructor, the compiler will not catch the error. We will not look at this further as this has already been discussed in Strong Typing or Naked Primitives.

So, our only concern here is the second argument, the mode. Since there are a limited number of values, this appears to be a good candidate for an enumeration instead of a string. Looking at the possible values for mode you will notice that it serves two different purposes:

  1. Indicates that the file should be opened for some combination of reading, writing, or appending.
  2. Indicates whether the file contents are text or binary.

To simplify the code in the constructor, let’s use two separate enumerations, one for the operation type, and one for the contents type. Here is the code resulting from these changes:

enum Mode {
    eRead = 1,
    eWrite = 2,
    eAppend = 4
};

enum Type {
    eText = 1,
    eBinary = 2
};

class File
{
public:
    File(const char* fileName, unsigned int mode, enum Type type) 
        { /* create file if necessary, and open it */}
    ~File() { /* close file */}
    void print(const char* charString, bool appendReturn) 
        { /* print charString */}
};

int main()
{
    File file1("file1", eAppend, eText);
    file1.print("A line", true);
    File file2("file2", eWrite, eText);
    file2.print("A line, no return", false);
    File file3("file3", eRead | eWrite, eText);
    File file4("file4", eBinary | 16, eText);
    return 0;
}

The Mode enumeration contains only three values: eRead, eWrite, and eAppend.  No attempt has been made to distinguish between the fopen modes of “w+” and “r+”. That is left as an exercise for the reader because it is not germane to the topic of this post.

Since a file can be opened for both reading and writing, or reading and appending, the second argument for the File constructor is specified as unsigned int. Multiple values of mode can therefore be OR’ed together. See, for example, the constructor call for file3. Everything looks good so far. Now look at the constructor call for file4. Here the Mode has been set to a combination of eBinary, which is a Type not a Mode, and 16 which has no numerical equivalent in Mode.

One potential solution to this problem is to add two enumeration values to the Mode enumeration: eReadAndWrite, and eReadAndAppend, and to change the second argument type in the File constructor to enum Mode. This works in this case, but what if Mode had a large number of individual values which could be combined in many different ways? Defining a value for every combination would not be a viable option.

The solution is to change the Mode and Type enumerations to both be enum class and to add a class that can OR together multiple Mode values:

enum class Mode : unsigned int {
    eRead = 1,
    eWrite = 2,
    eAppend = 4
};

enum class Type : unsigned int {
    eText = 1,
    eBinary = 2
};

template <typename BitType, typename MaskType = unsigned int>
class Flags
{
public:
 Flags()
 : m_mask(0) {}

 Flags(BitType bit)
 : m_mask(bit) {}

 Flags(Flags<BitType> const& rhs)
 : m_mask(rhs.m_mask) {}

 Flags<BitType> operator|(Flags<BitType> const& rhs) const
 {
 Flags<BitType, MaskType> result(*this);
 result |= rhs;
 return result;
 }

private:
 MaskType m_mask;
};

using ModeFlags = Flags<Mode>;
ModeFlags operator|(Mode bit0, Mode bit1)
{
 return ModeFlags(bit0) | bit1;
}


class File
{
public:
    File(const char* fileName, ModeFlags mode, Type type) 
    { /* create file if necessary, and open it */}
    ~File() { /* close file */}
    void print(const char* charString, bool appendReturn) 
    { /* print charString */}
};

int main()
{
    File file1("file1", Mode::eAppend, Type::eText);
    file1.print("A line", true);
    File file2("file2", Mode::eWrite, Type::eText);
    file2.print("A line, no return", false);
    File file3("file3", Mode::eRead | Mode::eWrite, Type::eText);
    File file4("file4", Type::eBinary | 16, Type::eText);
    return 0;
}

The Flags class has been borrowed from the Vulkan C++ bindings, vulkan.hpp, available as part of the Vulkan SDK, or separately from its GitHub repository. I have included only those parts of the class that are required for this example to compile or not where required. Things to note:

  1. The second argument in the File constructor has been changed to type ModeFlags, which is just an alias for Flags<Mode>.
  2. The line containing the constructor call for file3 now compiles.
  3. The line containing the constructor call for file4 does not compile because Type::eBinary and 16 are both not of type Mode. But, since this is a programming error, that is what we want to happen.

Update (February 17, 2017): A few days after publishing this post, I ran across Alternative to select-many bitmask that discusses a number of methods for combining bitmask bits.

Boolean to Enumeration

In the section above, we changed a character string that could contain a limited number of values to enumerations. By doing so, we ensured that invalid values could not be coded, and we ensured that 6 months from now when you or someone else looks at the code, they will not have to reference the documentation or the class’s declaration to determine the meaning of each argument.

Now we move on to boolean arguments. In the main function in the examples above, there are calls to File::print. The second argument in these calls contains either true or false. Can you tell what those argument values mean without looking at the class declaration? What happens if you change the value of this argument to 6? Hint: the program will still compile, but the compiler will generate a warning.

Let’s change this boolean to an enum class.

enum class LineEnd : bool {
    eNoReturn = false,
    eReturn = true
};
.
.
.
    void print(const char* charString, LineEnd appendReturn) 
    { /* print charString */}
.
.
.
    file1.print("A line", LineEnd::eReturn);
    file2.print("A line, no return", LineEnd::eNoReturn);

Now there is no confusion as to the meaning of the second argument to the File::print method. Also, trying to use values like true or 6 for this argument cause a compiler error.

Update (February 17, 2017): A few days after I published this post, Andrzej Krzemieński published a post with an alternative that uses a tagged_bool class.

Conclusions

The examples in this post are contrived to illustrate the points I am trying to make. I do not expect that anyone would actually try to write the File class I have started to declare because the functionality that would be provided in such a class is already available in classes in the standard C++ library. However, a number of conclusions can be drawn from using the techniques shown in these examples:

  1. By changing function and method arguments from common variable types to classes, including enum classes, a number of errors that formerly would only show up at program execution time can now be caught at compile time. This moves the burden of dealing with these errors from the user to the developer where they belong.
  2. Assuming that the classes and enumeration values are properly named, the meaning of the argument values specified  in the source code are much clearer, thereby making the work of the code maintainer much easier.

User Defined Literals

Units

In my post, Strong Typing or Naked Primitives, I created a Size struct whose constructor takes two arguments, a Width object and a Height object. Here are the definitions:

struct Size final
{
public:
    Size(const Width& w, const Height& h) 
        : m_width(w), m_height(h) {}
    Width getWidth() const noexcept 
        { return m_width; }
    Height getHeight() const noexcept 
        { return m_height;}
private:
    Width m_width;
    Height m_height;
};

class Width
{
public:
	explicit Width(const uint32_t width) 
            : m_width(width) {}
	uint32_t getWidth() const noexcept 
            { return m_width; }
        operator uint32_t() { return m_width; }
private:
	uint32_t m_width;
};

class Height
{
public:
	explicit Height(const uint32_t height) 
            : m_height(height) {}
	uint32_t getHeight() const noexcept 
            { return m_height; }
        operator uint32_t() { return m_height; }
private:
	uint32_t m_height;
};

The arguments in the Width and Height constructors are integers, but what are the units that these integers specify? If you guessed pixels, you win a prize! Well, not really.

When documenting Width and Height, I would mention that the constructor arguments are in units of pixels. Even if you saw

Size size(Width(400), Height(300));

somewhere in source code you had to maintain, you would probably assume that 400 and 300 are in units of pixels. But what about Size size(Width(5), Height(3))? Could you tell without viewing either the documentation or the declaration of Height and Width if the units are pixels? Perhaps the units are inches instead.

I could add a second constructor to Width and Height that took doubles as values and in those constructors, convert the inches values input to pixels. Wait a minute, most of the world deals with metric units not Imperial or US customary units. What if I want to input values in centimetres? I can’t simply add another constructor that takes a double as the number of centimetres because there is already a constructor that takes a double argument.

One potential solution would be to have different classes for each unit, such as WidthInPixels, WidthInInches, WidthInCentimeters, and so forth. So then the Size struct would have constructors like this:

Size(const WidthInPixels& wp, const HeightInPixels& hp);
Size(const WidthInPixels& wp, const HeightInInches& hin);
Size(const WidthInInches& win, const HeightInPixels& hp);
Size(const WidthInInches& win, const HeightInInches& hin);

and so forth. The number of constructors for Size goes up as the square of the number of different units. This would quickly get out control.

A second alternative is to use a tag argument to indicate what the units are:

class Pixels{};
class Inches{};
class Centimeters{};
...
Width(Pixels, const uint32_t pixels) {...};
Width(Inches, const double inches) {...};
Width(Centimeters, const double cm) {...};

Assuming the Width and Height classes also had an operator= operator, you would be prevented from doing this:

Width width = 400;

A third alternative might be to use templates, but that gets even more complicated when you want to return the values in specific units.

None of these alternatives can be viewed as ideal solutions.

User-Defined Literals

For built-in, or common variable types, C++ uses a number of prefix and suffix literals to specify precision. For example:

auto i = 5UL;       / unsigned long
auto j = 3LL;       / long long
auto k = 'x';       / char
auto l = L'x';      / w_char_t
auto m = "one"s;    / string of chars
auto n = 6 + 14.3i; / std::complex
auto o = u32"one";  // UTF-32 encoded string

Wouldn’t it be great if you could have single constructor definitions for Width and Height and specify the units with the values? Something like this:

Height h(500pixels);
Height(3.2inches);
Height(12.1cm);

C++11 introduced user-defined literals and C++14 extended them. Here is the way to define user-defined literals:

ReturnType operator "" _Suffix (Parameters) { /* do something */ };

There are a number of rules and restrictions:

  1. ReturnType can be anything including void.
  2. Suffix must be preceded by an underscore. Literals that do not begin with an underscore are reserved for the literal operators supplied by the standard library.
  3. For C++11, there must be a whitespace between the “” and the underscore. C++14 removed that restriction.
  4. For C++11, the first character of Suffix must be lower case. C++14 removed that restriction. If the Suffix is upper case letters, then there must be no whitespace between “” and the underscore.
  5. For C++11, the Suffix cannot be a reserved word. C++14 removed that restriction. Again, there must be no whitespace between “” and the underscore.
  6. Parameters must be built-in types (like integers, floating-point values, char strings, and so forth).
  7. Integer parameters must be specified as unsigned long long and floating-point parameters as long doubleto ensure that all number types are accepted.
  8. User-defined literal definitions should be placed inside a namespace.
  9. Wherever possible, user-defined literals should be marked as constexpr.

Assuming we want to store the values as pixel values, and that there are 96 pixels per inch (MS Windows), here is how we could define literals and use them in our code:

namespace units {
constexpr uint32_t operator "" _pix(unsigned long long pixels)
{
    return static_cast<uint32_t>(pixels);
}

constexpr uint32_t operator "" _in(long double inches)
{
    assert(inches >= 0.0L);
    return static_cast<uint32_t>(inches * 96.0L);
}
}

using namespace units;
Size size(Width(400_pix), Height(100_pix + 3.0_in));
Size size2(Width(350), Height(200));

Notes:

  1. Because the user-defined literals are defined in a namespace above (units namespace), the using namespace units; line is required. You cannot preface the user-defined literal with the namespace name. For example:
    Size size(Width(400units::_pix), Height(100units::pix + 3.0units::_in))

    will not compile.

  2. Width and Height still accept uint32_t values as arguments. This is useful when accepting pixel values from other variables. For example:
    wxSize wxS = ...;
    Size size(wxS.GetWidth(), wxS.GetHeight());

Pros

  1. Values can be specified in different units.

Cons

  1. Use of user-defined literals is limited to constant values. That is the reason I included constexpr in the definitions above. You cannot use them with variables. For example, the following will not compile:
    uint32_t width = 200;
    Size size(Width(width_pix), Height(4.0_in));

    though the following does compile and provide the desired result:

    uint32_t width = 200_pix;
    uint32_t height = 4.0_in;
    Size size(Width(width), Height(height));
  2. User-defined literals are limited to use as suffixes; they cannot be used as prefixes. That is:
    uint32_t width = _pix200;
    uint32_t height = _pix(200);

    both will not compile.

This post has just scratched the surface of user-defined literals.
See the references included in Additional Information, below,for more information on user-defined literals, their limitations, and more examples.

Additional Information

  1. User-defined literals
  2. Modern C++ Features – User-Defined Literals
  3. User defined literals – Part 1, Part 2, Part 3
  4. User defined literals
  5. User-Defined Literals (C++)

Compile Time Checking of Class Template Types

But all I wanted to do was…

Oh, how many times have we all said that? This adventure started as I was coding ChaosExplorer. I had completed the coding for displaying Mandelbrot and Multibrot Sets and their associated Julia Sets. I have two classes derived from wxGLCanvas, called MultibrotPanel and MandelJuliaPanel. The first panel displays the Multibrot Sets and the second panel displays the corresponding Julia Sets. In the MultibrotPanel class, I have methods that are used to animate a number of properties. These methods are bound to timer events. I am not going to go into detail here; you can see the code on GitHub.

Here is the gist of it. I have a StartTimer method and a StopandReleaseTimer method that are defined as follows:

   void StartTimer(const int timerInterval, TimerHandler handler);
    void StopAndReleaseTimer(TimerHandler handler);

TimerHandler is defined using the following alias:

using TimerHandler = void(MultibrotPanel::*) (wxTimerEvent&);

Great! Everything works. Now I wanted to use other formulae to display other fractals and their Julia Sets. Much of the code in MultibrotPanel and MandelJuliaPanel would be duplicated, with the major differences being the use of different GLSL shader programs. The fractal panels similar to MultibrotPanel also reference different Julia Set panels because the Julia Set displays are created as the result of selecting menu items in the context menus in the fractal panels. So this suggested that the common functionality of the various panels should be placed in base classes and the code specific to each fractal formula should be in derived classes. Also, because the various panels reference different shader programs and Julia Set panels, templatizing the panel classes would be a good idea. The diagram, below, shows the class hierarchy.

panels.png

My first attempt resulted in the following declarations:

template <typename T, typename U>
class MultibrotPanel<T, U> : public PlottingCPanel<T, U>
{...};
template <typename T, typename U>
class PlottingCPanel<T, U> : public ChaosPanel<T>
{...};
template <typename T>
class ChaosPanel<T> : public wxGLCanvas
{...};

where T is the shader program class that is used to draw the fractal, and U is the panel class that displays the corresponding Julia Sets.

Compiling this produces an error on the TimerHandler alias definition that is shown above. I could not figure out how to proceed, so I jumped over to Stack Overflow to post the problem. Arunmu posted a possible solution that involved checking that the TimerHandler pointed to a class that contained an appropriate callback for the timer. A bit of an ugly solution, but the problem at least is caught at compile time.

While studying the solution, I realized that there is only one shader program and one Julia Set panel that the MultibrotPanel class references. Therefore, MultibrotPanel does not need to be a templated class at all; only PlottingCPanel (and ChaosPanel) need to be templated classes. So, the MultibrotPanel class declaration becomes:

class MultibrotPanel : public PlottingCPanel<GLMultibrotShaderProgram, MandelJuliaPanel> {...};

and the TimerHandler alias can stay in its original form. So, my initial problem is solved! But that got me thinking. I know, that is a dangerous thing. The first templated class in PlottingCPanel must be a shader program, and the second templated class must be a Julia Set panel. But as declared, there is nothing to prevent the programmer from using any class in either position. It would therefore be nice to ensure, at compile time, that the declared classes are the correct type. And this brings us back to the answer to my post on Stack Overflow and a number of other posts as well. The posts use a combination of constexpr and static_assert to ensure at compile time that the classes are of the right type.

The first class in the PlottingCPanel declaration is a shader program class. All shader program classes derive from the GLShaderProgram class. This class is not templated, so checking is quite easy:

#include <type_traits>
...
        static_assert(std::is_base_of<GLShaderProgram, T>::value, "T must be derived from GLShaderProgram");

The static_assert is the first statement in the ChaosPanel constructor. The second class in the PlottingCPanel class declaration must be a PlottingZ0Panel which is templated, so the simple solution used for checking the shader program class will not work.

How do we check that a templated class is the correct type? This brings us back to Arunmu’s answer to my Stack Overflow post and to other posts on Stack Overflow and a few other sites as well. I have created a simple program that tests these solutions. Here is the framework for the program. C is a templated class; the class specified by the template must be derived from A. All other classes should generate a compile error.

template<typename T>
class A
{
public:
// note that func only needs to be declared and not defined for test2 and test3 // static_asserts to work
void func();
};

class B : public A<int>
{};

class D {};

template<typename T>
class C
{
public:
C()
{
}
};

int main(int argsCount, char** args)
{
C<B> c;    // OK. Should compile
C<D> d;    // Should not compile because D is not derived from A
}

What do we need to add to generate the compile error for C<D> d;? In the Stack Overflow post How to Determine if a Type is Derived from a Template Class, Johannes Schaub – litb recommended a solution like the following:

template< typename T, typename U >
std::true_type is_based_impl(A const volatile&);
std::false_type is_based_impl(...);

template< typename T >
bool is_based_in_A(T&& t) {
    return decltype(is_based_impl(t))::value;
}

This solution uses templated variables which are not supported in Visual Studio 2015.

Solutions That Work

All of the solutions below use SFINAE (Substitution Failure Is Not An Error). The following statements comes from the SFINAE web page at cppreference.com. See that page for more information.

This rule applies during overload resolution of function templates: When substituting the deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error.

is_base_of_template

I have placed each potential solution inside a different namespace so that the solutions are easily separated. From the post is_base_of of generic type, the following solution is proposed:

namespace test1 {
    // From http://coliru.stacked-crooked.com/a/eaeac2b9008a97d9 in answer to 'is_base_of of generic type'
    // at http://stackoverflow.com/questions/32477691/is-base-of-of-generic-type/32489730#32489730
    template <template <typename...> class Base, typename Derived>
    struct is_base_of_template
    {
        using U = typename std::remove_cv<Derived>::type;

        template <typename... Args>
        static std::true_type test(Base<Args...>*);

        static std::false_type test(void*);

        using type = decltype(test(std::declval<U*>()));
    };

    template <template <typename...> class Base, typename Derived>
    using is_base_of_template_t = typename is_base_of_template<Base, Derived>::type;
}

To use this, simply add

static_assert(test1::is_base_of_template_t<A, T>::value, "No!");

to the C class constructor. Compiling the program using Visual Studio 2015 generates the following output:

main.cpp
1>...\main.cpp(88): error C2338: No!
1>  ...\main.cpp(86): note: while compiling class template member function 'C<D>::C(void)'
1>  ...\main.cpp(97): note: see reference to function template instantiation 'C<D>::C(void)' being compiled
1>  ...\main.cpp(97): note: see reference to class template instantiation 'C<D>' being compiled

Test For Method – 1

For this solution, the templated base class must have a method that is unique to that class declared; it is not necessary to provide a definition for the method if the only purpose for this unique method is to test that the class is being used. Here is the code:

namespace test2 {
    // Based on Arunmu's answer to a somewhat related question that I asked on Stack Overflow:
    // http://stackoverflow.com/questions/37509611/templating-classes-and-aliases-causes-compile-errors/37510350#37510350
    template <typename T>
    constexpr auto has_func(const T& obj) -> decltype(std::declval<T&>().func(), bool())
    {
        return true;
    }

    constexpr auto has_func(...)
    {
        return false;
    }
}

To use this code, add

static_assert(test2::has_func(T()), "Another No!");

to the C class constructor. When compiled with Visual Studio, the following error messages are generated:

1>...\main.cpp(89): error C2338: Another No!
1>  ...\main.cpp(86): note: while compiling class template member function 'C<D>::C(void)'
1>  ...\main.cpp(97): note: see reference to function template instantiation 'C<D>::C(void)' being compiled
1>  ...\main.cpp(97): note: see reference to class template instantiation 'C<D>' being compiled

Test For Method – 2

Here is a second technique for testing for a unique method in the base class. In it, sizeof is used to distinguish between a class that has the method, and a class that does not.

namespace test3 {
    // based on one of guestgulkan's answers to 'writing and using a C++ template to check for a function's existence'
    // at http://www.cplusplus.com/beginner/70134/
    template <typename T>
    class Has_Func
    {
        typedef char one;
        typedef long two;

        template <typename C>
        static const one test(decltype(&C::has_func));
        template <typename C>
        static const two test(...)
        {
            return 962;         // could be any long int value
        }

    public:
        template <typename U>
        static constexpr bool has_func()
        {
            return (sizeof(test(U)) == sizeof(char));
        }

        static constexpr bool has_func()
        {
            return false;
        }
    };
}

Adding the following line to the C class constructor

static_assert(test3::Has_Func::has_func(), "Does not have func()");

and compiling produces the following compile error messages:

1>...\main.cpp(90): error C2338: Does not have func()
1>  ...\main.cpp(86): note: while compiling class template member function 'C<D>::C(void)'
1>  ...\main.cpp(97): note: see reference to function template instantiation 'C<D>::C(void)' being compiled
1>  ...\main.cpp(97): note: see reference to class template instantiation 'C<D>' being compiled

Using All Three Techniques

The test program uses all three techniques. Having all three static_assert calls in the constructor yields the following compiler output:

main.cpp
1>...\main.cpp(88): error C2338: No!
1>  ...\main.cpp(86): note: while compiling class template member function 'C<D>::C(void)'
1>  ...\main.cpp(97): note: see reference to function template instantiation 'C<D>::C(void)' being compiled
1>  ...\main.cpp(97): note: see reference to class template instantiation 'C<D>' being compiled
1>...\main.cpp(89): error C2338: Another No!
1>...\main.cpp(90): error C2338: Does not have func()

Note that lines 3-5 are included only once.

Limitations

  1. These techniques work only for public base classes.
  2. The second and third techniques work only for public methods.

Analysis

  1. The is_base_of type trait only works with non-templated base classes.
  2. The second and third techniques require that a method with a name that is unique to the base class be declared in the base class.
  3. It is not necessary to provide a definition for this method if its only purpose is to check for use of the base class.
  4. The first technique is the cleanest because it does not require any additions to the base class.

Conclusions

  1. It is possible to check at compile time if a template class is derived from a templated base class.
  2. This check should be performed at compile time because using a non-templated base class as a template class when a templated base class should be used is a design/code error, and therefore should be caught by the developer and not shown to the end user.
  3. The first technique is the cleanest and therefore the preferred technique for determining if a template class is derived from a templated base class.

The source code for the program is available on GitHub.