Concepts: The Next Major Change Coming to C++

The upcoming C++ standard is set to promise many different features and changes that will simplify and expand the way we write and think of code. Upon the wide variety of additions to the language, the inclusion of Concepts tops the list as the most significant one, as they are essential to simplify the use of generic programming. The currently ongoing CppCon has sparked my interest on the topic thanks to the talk given by Bjarne Stroustrup on the future of generic programming. Which covered the importance of concepts in the future of the language as described by the Concepts TS.

What is a Concept?

A Concept is a set of requirements that yield a boolean value at compile-time. They describe the characteristics needed for a certain group of objects to be used inside a template e.g.
Number: - Must provide arithmetic operators(+, +=, -, -=, *, *=, /, /=)
- Must be constructible from 0
- Must be copyable
- Must be movable

Why do we need Concepts?

- Simplify the way we write generic code by removing the verbosity and repetitiveness that templates bring e.g.

//Old C++ template version
template<typename T>
void sort(T& container)
{
    //Sort algorithm
}

//New C++ concept version
void sort(Sortable& container) //Sortable is a concept
{
    //Sort algorithm
}

- Provide more precise and helpful error messages which in the past have been known to daunting and irrelevant e.g.

int a = 42;

//Old error message 
sort(a) // -> error: 'a' doesn't have a [] operator

//New error message with concepts
sort(a) // -> error: 'a' is not Sortable (int doesn't provide [] operator)

- Allow the use of overloads for functions using concepts e. g.

template<RandomAccessIterator Iter>  //RandomAccessIterator is a concept
void distance(Iter first, Iter last)
{
  //Distance Implemnetation for random access iterators
}

template<Iterator Iter> //Iterator is a concept
void distance(Iter first, Iter last)
{
  //Distance implementation for the rest of iterators
}

- Remove the ugliness and complexity coming from the use of enable_if by replacing it with the use of the keyword requires e.g.

//Old code using enable_if
template<typename T>
class Foo
{
  public:
  template<typename U, std::enable_if<std::is_same<T, U>::value && std::is_copy_constructible<U>::value>>
  explicit Foo(const T& object) :
      object(object)
  {}

  private:
  T object;
};

//New code using concepts
template<typename T>
class Foo
{
  public:
  explicit Foo(const T& object)
  requires std::is_copy_constructible<T>::value :
  object(object)
  {}

  private:
  T object;
};


How do we use Concepts?

The first step is to either define a new concept or use an already existing one. Here is an example using the previously mentioned concept of a number:

template<typename T>
concept Number = requires(T a, T b)
{
  { a + b } -> T;
  { a += b } -> T&;
  { a - b } -> T;
  { a -= b } -> T&;
  { a * b } -> T;
  { a *= b } -> T&;
  { a / b } -> T;
  { a /= b } -> T&;
  
  { T{0} };
  
  requires std::is_copy_constructible<T>;
  requires std::is_move_constructible<T>;
}

Then we simply use the already defined concept in a function:

template<Number N>
N pow(N number)
{
  //power exponent implementation
}

Conclusion

Concepts fill in a void that was missing in the language; they effectively improve and simplify the use of generic code. It’s important to understand and appreciate their benefits to start adhering them into our code base as soon as C++20 arrives.

Range-V3: The Blueprint for Standardized Ranges

Iterators have been one of the basic and most important components in the Standard Template Library(STL) ever since its creation. Their usage has become so fundamental that they’ve been provided overloads for almost all generic algorithms in the standard library. Iterators seem to do the job they’re supposed to; but are they the most convenient solution? Most of the time we want to modify the elements of an entire container without having to bother about anything else. The standard library has imposed the use of begin() and end() iterators to manage the totality of a container, but this in turn has damaged the readability and simplicity of our code in multiple scenarios. The solution to this problem has been presented through the use of ranges in the Ranges TS, expected to be part of the new standard in C++20.

Since the integration of ranges to the standard is still a couple years away, we have to use library alternatives such as Range-V3 to unveil the power of ranges. Luckily for us, Range-V3 is the basis for the new proposal; meaning that most of its interface will be very similar to what we’ll see in C++20.

What is a Range?

A range is a sequence of objects with a beginning and an end. The motivation behind them is mainly convenience and composability. They’re convenient because they allow algorithms to take a single range object instead of a pair of iterators, making code more readable. In terms of composability, they enable the use of algorithm chaining adding more expressiveness in less lines of code.

Convenience

std::vector<int> unorderedNumbers{ 3, 1, 8, 2, 0, 5, 6, 4 };

//Iterator version
std::sort(unorderedNumbers.begin(), unorderedNumbers.end()); 

//Range version
ranges::sort(unorderedNumbers); 

Composability

std::vector<int> numbers{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };

//reverse + remove even numbers

//Iterator version
std::reverse(numbers.begin(), numbers.end());
numbers.erase(std::remove_if(numbers.begin(), numbers.end(), 
[](auto number) { return number % 2 == 0; }), numbers.end());

//Range version
auto reversedOddNumbers = numbers | ranges::view::reverse | ranges::view::remove_if(
[](auto number) { return number % 2 == 0; });

Conclusion

Ranges are the next big leap for the standard library and we need to expand our perspective to welcome them into our codebase. The convenience and composability inherent in ranges makes them a worthwhile addition to the language.

DynaMix: An Alternative for Polymorphism

Polymorphism in C++ has usually been implemented through the use of an inheritance hierarchy and virtual function calls. This method while effective has one key drawback originating from the problem of multiple inheritance: a derived class having two or more parents can be the source of many ambiguities. The 'solution' to this problem was addressed by means of virtual inheritance: a technique used to prevent multiple instances of one class in an inheritance hierarchy. Although virtual inheritance fixes the diamond problem, it generates additional performance overhead and higher levels of complexity to a code base. An optimal alternative to the use of virtual inheritance is the concept of Mix-Ins, founded on the idea of granularity and composition. 

DynaMix is a useful library that elaborates on the concept of Mix-Ins, letting the user compose and modify polymorphic objects at runtime. It provides users with an effective alternative to the commonly used approach of inheritance, while still not adding many of the nuances that come from the solution using virtual inheritance. The library encompasses the notion of composition by the use of a dynamix::object that can be mutated with any number of Mix-Ins. Its benefits show when dealing with larger projects that need the use of complex objects composed of multiple parts; an example of such project would be a game composed of entities that have different behaviors or characteristics that define them.

Here is a basic example of the library in action:

#include <dynamix/dynamix.hpp>
#include <iostream>

struct Label
{
    void display()
    {
        std::cout << "Label\n";
    }
};

struct Sprite
{
    void display()
    {
        std::cout << "Sprite\n";
    }
};

DYNAMIX_DECLARE_MIXIN(Label);
DYNAMIX_DECLARE_MIXIN(Sprite);

DYNAMIX_MESSAGE_0(void, display);

DYNAMIX_DEFINE_MIXIN(Label, display_msg);
DYNAMIX_DEFINE_MIXIN(Sprite, display_msg);

DYNAMIX_DEFINE_MESSAGE(display);

int main()
{
    dynamix::object object;

    dynamix::mutate(object).add<Label>();

    display(object);

    dynamix::mutate(object).remove<Label>().add<Sprite>();

    display(object);
}

Output:
Label
Sprite

DynaMix offers a different view to what polymorphism can look like. Mix-Ins are more flexible and effective in handling complex objects that depend on multiple traits, composition through the use of Mix-Ins is the prime alternative to what inheritance can offer.