VSJ – March 2007 – Work in Progress

Dave Trollope, MIAP holds a degree in Computing and Informatics from the University of Plymouth and, since graduating in 1995, has worked in Chicago. Here he describes a set of C/C++ extensions he’s developing.

After more than ten years of working with C and C++ it seemed to me that there should be some simple extensions to the language that improve source code maintainability and reliability without adding significantly to its complexity.

So I’ve started a project called “Cimplicity”. It introduces extensions to C that are simple to implement and easy to understand, both conceptually and when reading the source code. It’s in its early stages and currently focuses on C, though at some point in the future, C++ may be addressed too because some of the extensions are equally valid. The project is open source and available under the GPL licence.

Cimplicity introduces the following important concepts to C (trivial examples are provided for the purposes of demonstration):

  • Functions with optional return codes

A default return value can be specified when declaring a function type. It is then unnecessary to specify the returned value throughout the function. For example:

int (55) afunc (int p) {return;} /*Returns 55*/

The default is overridden by an explicitly defined function value:

int (55) afunc (int p) {

if (p == 22)

return; /*Returns 55*/

else

return 88; /*Returns 88*/

}

This makes it easier to update functions in the future to return different results without updating every return. When there are multiple return points in a function (and in very large functions), this can save a lot of time when updating the source. However, this flexibility means that you can change legs of the source unintentionally. In C, you have little choice but the mechanism allows you to weigh the risks of each approach and use the solution that fits best.

  • Single Point of Return

Having a single return point in a function can dramatically simplify the logic, allowing you to focus on the overall application logic instead of the trivial details. A single point of return for a function can be marked using ‘@’. Everything from this point to the end of the function is considered part of the return code. So in a function with many error legs, you need not duplicate the error handling source code. For example:

void *afunc() {

struct { char *m, char *y; } *x;

x = malloc (sizeof (x));

if (!x) return;

memset (x,0,sizeof(*x));

x.m = malloc (25);

if (!x.m) return;

strcpy (m,“Hello World!”);

x.y = malloc (25);

if (!x.y) return;

strcpy (x.y, “A basic test”);

return x;

@

printf (“An error occurred allocating memory in afunc() x = %p\n”, x);

if (x) {

printf (“Attributes of x were %p %p\n”, x.y, x.m);

if (x.y) free (x.y);

if (x.m) free (x.m);

free (x);

}

return NULL;

}

This type of structure has many benefits. First, it allows the core of the function to focus on doing what the function was primarily intended to do and that makes the core logic easier to read. Second, it allows the error handling code to be centralised within the function. This means that it is simpler to consider all the combinations and therefore handle all the conditions logically and systematically, as this example demonstrates by freeing the fields that were successfully allocated irrespective of which malloc () fails.

  • Function Level Critical Regions

Defining critical regions is paramount to successful development of multithreaded applications. Cimplicity takes the first step of allowing a whole function to be protected by a mutex. This allows you to focus on the application development, not the mutex API implementation. With the following syntax, defining a critical region becomes as natural as using any other operator.

A function is identified as being protected by a mutex by placing a ‘~’ prior to the function block, e.g.:

int afunc () ~{ return 55; }

This function is fully protected and ensures that only one thread can execute the body at any one time.

Currently Cimplicity implements the protection of the critical region using an unconditionally blocking mutex and in many instances that type of blocking is undesirable. In the future, this syntax will be updated to support non-blocking mutexes and ones with timeouts. Also in the future this design will be extended to sub-blocks so that they can be protected the same way.

Using the Cimplicity parser (ctyparser)

Cimplicity is implemented as a parser that translates Cimplicity files (C files with extensions) into native C. It has been designed to work in cooperation with the standard C preprocessor. The Cimplicity parser is not itself a preprocessor. The output of the preprocessor is passed through the Cimplicity parser and the result is presented to the traditional compiler. I use the gcc compiler and make, so I’d typically compile a Cimplicity source file by defining a make rule like:

Eg.o: Eg.cty

$(CPP) $^ | $(CTYPARSER) >$^.i 2>$^.dbg ;\

$(CC) -o $@ $^.i

This approach is similar in nature to the original cfront that was written to create C++.

Extending to C++

These extensions will fit C++ too, but ctyparser is not yet ready to accept C++ sources. I expect to add this in the future but the focus is currently on creating a solid, robust parser that works well with C. Once that objective has been clearly met, Cimplicity++ would be a great next step. Some of the natural C++ features make it easier to implement logic using a similar structure to the Cimplicity extensions but I would contend that the Cimplicity solutions are quite frankly simpler.

Automated Tests

The Cimplicity package comes with a set of automated tests. These tests also serve as a good source of example cty files.

Potential Future Enhancements

These include, in no particular order:

q  Enhanced default return codes:

q  Complex data types as default return codes

q  Enhanced Critical Regions

q  Support sub-blocks

q  Support different types of mutex, e.g. Non-blocking or with timeouts

q  Enhanced Switch Statements

q  Allow individual cases to be marked for optimisation

q  This can be critical when a known subset of cases are the predominant cases, and highly valuable in terms of performance gains.

Conclusion

I believe the current extensions are very simple to understand and adopt. They greatly simplify many common problems in C-based applications and make the source code more maintainable and readable. This can only lead to more robust applications. These are the prime objectives of the extensions. The ideas listed in the potential future enhancements maintain this philosophy. I’m not trying to generate a whole new language or to deviate too far from the traditional C structure.

I would welcome feedback from the IAP community on the following topics:

  1. Are these constructs valuable to you as a software engineer?
  2. Will these constructs save you time and simplify your source?
  3. Of the potential future enhancements, which would be most valuable to you?
  4. What other constructs would you like to see added to Cimplicity?

See http://www.fsml-technologies.com/ for more details of Cimplicity. You can contact Dave at daveanddiane@kringlecottage.com.

[Interesting project or development? Let us know at eo@iap.org.uk!]

Comments are closed.