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.