12 Jun 2002 anselm   » (Journeyer)

I notice that there is a specific difference between how C programmers use C as opposed to C++ programmers use C++.

This difference is a contrast to the more common difference - the argument of overall simplicity. The common argument has often been voiced in terms of how C developers claim that they "can do everything in C that others do in C++" and that they "can use C just like C++".

It is obviously true that C developers can make C++ like objects by hand (instead of using C++ language features) but what is not true is that this is really how they work every day. In fact C developers use a different work pattern from C++ developers.

When a C developer wants to synthetically model a problem landscape they do the same kind of partitioning that a C++ developer does; they partition the functionality of their solution up into clumps of locally associated behavior. A single clump is any area where the density of relationships is highest, and the relationships between clumps is often lesser than the relationships going on inside of one clump with itself. This is a typical colloquial human approach to problem solving; divide and conquer - where division occurs along the easiest cleaving lines. Humans are particularly clever at finding ideal cleaving lines between any two things - it is a powerful technique. For example Democritus proposed the existence of the atom by simply considering what would happen if one kept trying to divide a grain of salt in two. (All of this is just classic prototype based composition as well documented by George Lakoff and friends).

In other ways a problem is often thought of by both camps as some kind of dataflow or some kind of execution flow. Typically these problems can be arranged as a stack, where each of the clumps of functionality gets executed in turn, and the whole thing is driven from the bottom or the top by some kind of input. Clumps of functionality can be thought of as belonging to layers, as one more step along a pipeline. This design pattern is useful for representing everything from user interface applications (where the application is driven by user input) to state machines (where the application is driven by state events such as timeouts or external events).

When a C programmer implements a solution they will typically define a formal API for each of the layer of behavior, and traffic will flow between clumps by invoking methods on each others global API's. If in some cases one clump has to talk to an API method that isn't normally nearby (such as say a user interface needing to set the value of a cryptographic key that is closely associated with a low level transport layer) then it can do that because the design pattern at use is one of global API's which are always visible all of the time and always accessible.

As opposed to how a C programmer works, when a C++ programmer implements a solution they will typically define objects to represent (or be in) each layer. Here we are seeing quite a different design pattern at work. Because these are real objects, as opposed to global API's, they tend to be visible only to the layer above and the layer below. When a C++ programmer wants to touch some low level functionality from a high level area, they have to find a way to promote the interfaces of the low level layer up to the high level, or they have to resort to using global API methods. There is a strong discouragement to use statically global API methods because it is contrary to the idea of an object altogether - and if one has a specially shaped kind of implement that has been a significant investment then to not use that implement seems wasteful. There is at least a tendency towards complexity because of the desire to use the tools at hand - but also a real sense that if global objects are used then one loses the ability to multiply instance the application because there may now also be global state. C++ lacks an ability to fork off a copy of global state although of course one can instance another copy of the application.

To help C++ programmers not avoid some of the strengths of C - there is a way of thinking about this problem that will help C++ programmers to be more effective. Most things in life seem to be persuasively represented best as spatial or visual metaphors. In domains as disparate as music and mathematics or in any kind of problem solving it is often useful to conceive the system using spatial or visual metaphors, the clarity of the right solution, or the ability to predict next steps seems to jump out much more strongly - probably because as humans we have so much wetware dedicated towards spatial analysis.

In this case the spatial analogue of what C programmers are doing in their code is to define API's that cleave vertically through all layers of an application. Because the API's are vertical, they are accessible to everybody, and this tends to empower C programmers to actually make those API's useful globally. The limiting factor affecting C++ programmers is that their objects are only visible horizontally. Clearly C++ programmers should think more often of using static global classes to represent functionality in some cases, and should think more about aggregating related objects into a specific layer of functionality, where that layer as a whole may itself have some global methods - even if some of the contained objects do not.

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!