Category Archives: Husband’s notes

Husband’s notes: C³ as a Systems Programming Language

Bjarne Stroustrup describes C++ as a “general purpose programming language with a bias towards systems programming“. I aim to make C³ simply a “general purpose programming language”. It will provide high-level features that I hope will qualify C³ as the best language for tasks that are currently best served by domain-specific, scripting, or other general purpose languages. It is an incremental improvement over C++: it means that C³ will be suitable for a wider range of software projects than C++. It does not mean that systems programming will receive less care! Some languages make low-level tasks impossible or harder because they try to provide high-level facilities. In C³, all “levels” will get full support! In today’s notes, I explain the design choices that will make C³ the best systems programming language: there will be no bias, but there will be no compromise either.

I want C³ to become the first choice for systems programming tasks such as heavy-load application infrastructures, cutting edge 3D games, real-time applications, and operating systems. To achieve this, C³ will follow the C++ design guideline “you don’t pay for what you don’t use” even more than its predecessor. (See The Design and Evolution of C++.)

I always hear about new languages that add “just a little bit” of overhead to enable easier programming techniques. For example, they have garbage collection and built-in types like strings and maps. I believe it is possible to design a core language that will make easy programming possible without these integrated features. The key is to make the “user-defined” features first-class in syntax and performance as if they were integrated. Some C++ features like copy constructors, operator overloading and inline functions are steps in this direction but the resulting types often have rough edges and are not as efficient as built-in types. C³ users will be able to define seamless and fully optimizable modules, data types, and even control structures! Features like communication facilities, dynamic arrays, and loop structures (including multi-threaded ones!) will be available in the C³ library.

Because I want to support all possible programming environments, there will be no core language features that require any kind of system runtime support. When coding an OS, you’re on your own; there is no system support! The only requirement is a compiler back-end that maps a set of “native” abstractions to the target machine language. Everything will be provided to build higher abstractions from the ground up. Of course, many facilities that require support will be provided! You’ll find them in the C³ library and they’ll be very easy to use, as explained in my previous point.

The C programming language succeeded for OS programming because its core abstractions were close enough to the available hardware platforms. But we can do even better! An innovation in C³ is that the set of native abstractions may change between target machines. This flexibility will allow the C³ system programmer to target different memory models (remember near and far pointers?) and new computer architectures (quantum computing, anyone?). Moreover, it will enable more target platforms that are not necessarily hardware. Someone could implement a back-end that targets the JVM, the CLR, or even javascript (for web applications).

C is still the number one language for kernel-level programming. Unfortunately, it makes OS hacking harder than it should be because C does not provide high-level features. C++ tried to combine the best of both worlds but failed in this niche in part because of its system support requirements, even if they are small. It also does not give enough control on the implementation of its complex features. For example, sometimes, the exact layout of the vtable of an object must be in the control of the programmer. (See this interesting discussion featuring the Linux creator.) In C³, everything that is of a higher-level than C constructs will be fully customizable and implemented in C³. For known object-oriented features, usage will be mostly like in C++, but the definition will be accessible in the C³ library instead of its compiler. This will also enable programmers to design their own interoperability layer with other languages.

I could continue to write for hours about various details that will make systems programming in C³ both effective and enjoyable but I also have some compiler code to write. Maybe next time I will talk more about the C³ library that I keep mentioning without further explanations… See you soon!

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.