I Wanted to Like C++

I really did. I programmed in C++ 20 plus years ago, first on a Sparc workstation and later on a Windows computer. At the time, it seemed to be a good language, and in fact about the only object oriented language for developing both server and client applications.

But something happened between then and now. That something was the development of a number of programming languages (Java, C#, D, Go, Rust) that tackled the myriad problems that C++ presented.

I will not enumerate the problems that compatibility with C has produced because we are familiar with them, and at least I could live with them. And the C++ standard has advanced enough to provide alternatives for some of these problems (e.g. smart pointers instead of “dumb” pointers). There are still a number of problems that may or will be addressed in future updates to the standard.

Where I have trouble with C++ is mostly with templating. Don’t get me wrong, I think templated classes and methods can be wonderful. Where I have problems is in its other uses: trying to overcome some of the limitations of the language, such as lack of reflection. For an example, have a look at my post titled Compile Time Checking of Class Template Types for how to use SFINAE to determine if a class contains a certain method. This would have been so much easier to do with reflection. Every time I attempt to do any template programming outside of simple class templates, it seems to take days to accomplish.

I am not the only person who has problems with templating and SFINAE. Just pop over to Stack Overflow and do a search for SFINAE. As I write this, there are 2073 questions about it.

There are many changes to the language and standard libraries that would make C++ easier to use, and some of these will be addressed in C++20 and beyond. There are libraries in Boost and elsewhere for a number of these, but their inclusion in the standard libraries will no doubt make changes to those existing APIs.

So I have decided to to look over some of the “replacement” languages: C#, D, Go, and Rust. Each of these languages has the advantage of observing C++ problem areas that arose during at least the first 15 to 30 years of C++ use, and could therefore address in the design of those languages.

Most of the problems could be addressed in C++ except for the stated requirement of backward compatibility between the various C++ standards. Some languages effectively reinvent themselves by introducing new versions that make breaking changes with previous versions; for example: Perl, Python, and D.

Java, C#, D, and Go get around some of C++’s problems by using a garbage collector. Garbage collectors are no-no’s in certain areas of programming where C and C++ are the go-to languages: real-time programming areas such as embedded systems, operating systems, and drivers. But there are some new replacements even in those areas; for example: Rust.

Since my programming efforts are unlikely to involve real-time programming, I can live with the occasional slight slowdown that a garbage collector will cause. At least my limited past experience with Java and C# have shown no problems in that area.

So, I am moving on from C++, and possibly back to a programming language that I have used in the past.

This is the last post on this site, at least for now. You may want to check out my other blogging site, Jim’s Adventures in Programming, for non-C++ related topics. There may not be much there yet, in fact there is nothing as of today, but I will be added content there soon.

Advertisements

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.