Moving from C++ to C

Working on an embedded project, I tried working in all C once, and just couldn't stand it. It was just so verbose that it made it hard to read anything. Also, I liked the optimized-for-embedded containers I had written, which had to turn into much less safe and harder to fix #define blocks.

Code that in C++ looked like:

if(uart[0]->Send(pktQueue.Top(), sizeof(Packet)))
    pktQueue.Dequeue(1);

turns into:

if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet)))
    Queue_Packet_Dequeue(pktQueue, 1);

which many people will probably say is fine but gets ridiculous if you have to do more than a couple "method" calls in a line. Two lines of C++ would turn into five of C (due to 80-char line length limits). Both would generate the same code, so it's not like the target processor cared!

One time (back in 1995), I tried writing a lot of C for a multiprocessor data-processing program. The kind where each processor has its own memory and program. The vendor-supplied compiler was a C compiler (some kind of HighC derivative), their libraries were closed source so I couldn't use GCC to build, and their APIs were designed with the mindset that your programs would primarily be the initialize/process/terminate variety, so inter-processor communication was rudimentary at best.

I got about a month in before I gave up, found a copy of cfront, and hacked it into the makefiles so I could use C++. Cfront didn't even support templates, but the C++ code was much, much clearer.

Generic, type-safe data structures (using templates).

The closest thing C has to templates is to declare a header file with a lot of code that looks like:

TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ /* ... */ }

then pull it in with something like:

#define TYPE Packet
#include "Queue.h"
#undef TYPE

Note that this won't work for compound types (e.g. no queues of unsigned char) unless you make a typedef first.

Oh, and remember, if this code isn't actually used anywhere, then you don't even know if it's syntactically correct.

EDIT: One more thing: you'll need to manually manage instantiation of code. If your "template" code isn't all inline functions, then you'll have to put in some control to make sure that things get instantiated only once so your linker doesn't spit out a pile of "multiple instances of Foo" errors.

To do this, you'll have to put the non-inlined stuff in an "implementation" section in your header file:

#ifdef implementation_##TYPE

/* Non-inlines, "static members", global definitions, etc. go here. */

#endif

And then, in one place in all your code per template variant, you have to:

#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE

Also, this implementation section needs to be outside the standard #ifndef/#define/#endif litany, because you may include the template header file in another header file, but need to instantiate afterward in a .c file.

Yep, it gets ugly fast. Which is why most C programmers don't even try.

RAII.

Especially in functions with multiple return points, e.g. not having to remember to release the mutex on each return point.

Well, forget your pretty code and get used to all your return points (except the end of the function) being gotos:

TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{
    TYPE * result;
    Mutex_Lock(this->lock);
    if(this->head == this->tail)
    {
        result = 0;
        goto Queue_##TYPE##_Top_exit:;
    }

    /* Figure out `result` for real, then fall through to... */

Queue_##TYPE##_Top_exit:
    Mutex_Lock(this->lock);
    return result;
}

Destructors in general.

I.e. you write a d'tor once for MyClass, then if a MyClass instance is a member of MyOtherClass, MyOtherClass doesn't have to explicitly deinitialize the MyClass instance - its d'tor is called automatically.

Object construction has to be explicitly handled the same way.

Namespaces.

That's actually a simple one to fix: just tack a prefix onto every symbol. This is the primary cause of the source bloat that I talked about earlier (since classes are implicit namespaces). The C folks have been living this, well, forever, and probably won't see what the big deal is.

YMMV


Nothing like the STL exists for C.
There are libs available which provide similar functionality, but it isn't builtin anymore.

Think that would be one of my biggest problems... Knowing with which tool I could solve the problem, but not having the tools available in the language I have to use.


I moved from C++ to C for a different reason (some sort of allergic reaction ;) and there are only a few thing that I miss and some things that I gained. If you stick to C99, if you may, there are constructs that let you program quite nicely and safely, in particular

  • designated initializers (eventually combined with macros) make initialization of simple classes as painless as constructors
  • compound literals for temporary variables
  • for-scope variable may help you to do scope bound resource management, in particular to ensure to unlock of mutexes or free of arrays, even under preliminary function returns
  • __VA_ARGS__ macros can be used to have default arguments to functions and to do code unrolling
  • inline functions and macros that combine well to replace (sort of) overloaded functions

Tags:

C++

C