The Internals Pattern, or emulating Ada’s discriminated variant records

In C, unions are not typesafe and requires that the programmer be completely responsible with how the unions are used. Ada’s discriminated variant records are a typesafe way of doing unions and using the Internals pattern1 we can get the same functionality in C++. This is all the code that’s needed:

template<typename...>
struct Internals
{
    // Empty struct for the default case
};

Due to C++’s template instantiation rules, you can have different members of a class for any specialization of the template. In essence:

template<>
struct Internals<int>
{
    int int_data;  // Note that there is no requirement that the members are the same
};

template<>
struct Internals<std::string>
{
    std::string string_data;
};

template<>
struct Internals<int, int>
{
    std::tuple<int, int> int_tuple_data;
};

All of these structs are completely different types, even though they all come from the Internals templated type. They bear no relation to each other in the way inheritance would have done, and this is the main benefit. You can have compile-time polymorphism that is checked before you even run the code. This is now possible:

template<typename T, typename U, typename... Vs>
struct Scaffold
{
    Internals<Scaffold> internals;
    void print()
    {
        std::cout << "Default dummy data" << std::endl;
    }
};

template<>
struct Internals<Scaffold<int, float, char, double>>
{
    std::string data{"Specialized dummy data"};
};

template<>
void Scaffold<int, float, char, double>::print()
{
    std::cout << this->internals.data << std::endl;
}

int main()
{
    Scaffold<long, long>{}.print();
    Scaffold<int, float, char, double>{}.print();

    return 0;
}

The output of this program is:

Default dummy data
Specialized dummy data

This is a useful behaviour when you want something akin to aspects, or crosscutting concerns. Clients of your framework can specialize the internals without having to derive from any framework class, decoupling the framework scaffolding from the client.


  1. I have not found this pattern discussed anywhere and so do not know the correct name even if there is one 
Advertisements

A general algorithm for modern C++11 design

C++11’s main domain is systems programming. A system that does anything interesting is made up of components and its complexity emerges from the interaction of those components. Software engineering is about managing complexity, and C++11 helps you manage that complexity by offering you multiple programming styles1. As programmers, we choose which style best reduces the complexity of expressing the system’s constraints. One size doesn’t fit all.

This is my ad-hoc process whenever I’m in my design mode:

  1. Imagine a new programming language that does what you need. Don’t limit the syntax to C++11 – consider constructs from other languages or that don’t even exist.
  2. Imagine short sequences of actions written in that language that achieves the main goals of the system.
  3. Break down those actions into primitives – verbs and nouns that can be composed in any manner of sentences.
  4. Read up on C++11 and keep an eye out for language features that look like those primitives – eg, operator overloading, lambdas, return type deduction, variadics.
  5. Consider any similarities between primitives and work out if the differences between the similar primitives can be figured out during compile time by the compiler, or can only be figured out at runtime.
  6. If it can be figured out during compile time, consider using templates.
  7. If using templates, consider using preprocessor macros that improve readability and usability of using templates.

  1. Not paradigm. I view it as a stylistic choice. There is some aesthetic aspect to looking at code, but it’s a stretch to call syntactic sugar a paradigm. 

While I’m at it…

I guess my first piece of advice to coding C++11 is to always read the Standard Library documentation. Especially the Algorithms of the STL. Many complex algorithms, especially those that require loops, can be reduced to just a few lines of non-loop code. But an even more basic benefit is to never rely on remembering what anything is supposed to do. Just browsing the algorithms and rereading the specifications of those algorithms can often lead to ingenious ways to combine algorithms.