Husband’s notes: Multiparadigm

C3 is a multi-paradigm language. It means that, like in C++, you can mix and match various programming styles. Here is the classical multi-paradigm example combining object-oriented, generic and functional programming and given by Bjarne Stroustrup himself:

for_each(shapes.begin(), shapes.end(), mem_fun(&Shape::Draw));

where Shape is a base class with a Draw virtual function and shapes is a container of pointers to different kinds of shape. The object-oriented part is the polymorphic call to Draw that will automatically dispatch to the appropriate Draw function depending on the actual type of the shapes. The generic part is the for_each algorithm that can be applied to any container of any type.

c3wifehusband1The mem_fun function is just an adapter that convert the member function pointer into a functor that accept a single argument: an object pointer. This last part constitutes the “functional” part of the example but as you can see, C++ does not handle this paradigm is an elegant way. The mem_fun function is a necessary evil because the object-oriented syntax is not directly compatible with function object as first-class citizen.

Please compare this last example with the same thing, written in C3:

foreach(shapes) {
    {1}->Draw();
}

The following syntax is also valid and equivalent:

foreach(shapes, {1}->Draw());

The first syntax was valid because the last parameter of a function can be outside the parenthesis when it is itself a function. This allow us to define basic language constructs like for and foreach loops as functions.

The {1} refers to the first parameter of a lambda procedure that is implicitly defined. The following example makes more explicit use of a lambda procedure:

foreach(shapes) void(Shape* pShape) {
    pShape->Draw();
}

And, if we decide to name the procedure and make it global:

void DrawShape(Shape* pShape) {
    pShape->Draw();
}

foreach(shapes, DrawShape);

Since a method is automatically also a procedure with an additional “this” parameter, we can also skip the additional procedure and simply pass the Draw method as parameter:

foreach(shapes, Draw);

But the previous examples will be more common since we usually need to do a little more than call a single function inside the loop.

C3 algorithms act on ranges. Containers are interpreted as ranges for all their content but there are other ranges that can be passed. For example, there is a method in random access containers to get a reference to a subrange of their content. It is used here to get the first 5 elements:

foreach(shapes.subrange(0, 5), Draw);

As a bonus for those interested, without additional explanations, here is the definition of the foreach procedure:

template <type T>
void foreach(Range<T> _range, void _action(T)) {
    for(Iterator<T> i = GetIterator(_range); !AtEnd(_range, i); ++i) {
        _action(*i);
    }
}

I will explain more details of the C3 algorithm library in another post. Meanwhile, make sure you know STL since my library will be mostly inspired by Alex Stepanov’s masterpiece.

Leave a Reply

Your email address will not be published. Required fields are marked *