Like most projects I started long ago, mltt was written in C. It’s my goto programming language for most things but most especially anything in any way low level. But at some point I always end up asking myself wouldn’t this be easier to write in C++? (I’m just waiting for someone to say Python). C++ had such a terrible reputation among embedded coders for so long that it’s ingrained in my subconscious to avoid it. But after I’ve written my own linked list for the 1000th time I’m starting to wonder why am I making life so hard for myself. Is it really so bad to declare vector<int>
to implement a list instead of writing dozens of lines of C? This feels like deja-vu all over again as I went through the same a couple of years at my day job (thanks ElĂas !).
C is such simple language that it’s not hard to master. There really are not that many options for how to do most things. I’ve always managed to make it object oriented when I needed to with modules performing data hiding by return opaque handles, so why would I use C++? A for loop really can only be:
for (i = 0; i < 10; i++)
array[i] = foo(i);
With C++ it’s so different. If array
is a container then should I use
for (i = 0; i < array.size(); i++)
array[i] = foo(i);
or
for (auto it = array.begin(); it != array.end(); ++it)
*it = foo(*it);
or
for (auto it : array)
it = foo(it);
And why is it
sometimes a pointer and sometimes not? And why is it ++it
and not it++
? (it turns out it’s more effecient if the ++
operator is overloaded). In fairness, that last option was only introduced in C++11. Which in itself is a problem – the language is still evolving. I’m not going near for_each
and lambdas yet. When I looked for a replacement for printf ("%02x")
I found std::format
and I thought that’s cool, only to find out it’s a C++20 feature not yet supported by gcc12 on debian bookworm. So that leads to finding coding examples online may or may even work.
When I started refactoring the disk volume and file code in mltt it dawned on me that the natural object hierarachy of volumes to files to sectors would be ideal to implement as classes, so I bit the bullet and started translating what I had. Rather than wrestling with extern "C"
declarations around all my legacy code I renamed all source files and began compiling as C++. In general, C will compile fine with g++ but there are a few cases where it is just a little bit stricter. char *s = "foo"
is not allowed for example so lots of const
keywords have to be added. Due to name mangling, any slight changes to function prototypes causes linker errors, which isn’t such a bad thing (did you forget to include your own header file again?). In general though, it isn’t too painful and is easier in the long run that trying to maintain mixed C and C++ code.
The much bigger problem is actually changing the code to be reasonable C++ and not just straight C compiled with g++. Once you start converting it, OCD starts to kick in and the inconsistency of seeing some files using cout << endl
and some using printf ("\n");
starts to become almost physically painful. Resisting the temptation to rewrite everything is soooo hard. And I can see so many opportunities for rewrites too. My TI99 emulator has had separate modules to implement different types of memory (ROM, RAM, cartridge ROM) as well as all memory mapped devices (speech, VDP, GROM, etc) since I started the project 20 years ago. But seeing them now I can see they are just crying out for a little bit of polymorphism.
The real kicker here is after all this refactoring, there is zero new functionalty. The end user will see no difference whatsoever other than things run a little bit more slowly. This is what makes refactoring painful and a really hard sell to your manager (luckily I don’t have one of those so I just get to be hard on myself). That brings to my other major gripe with C++. Engineers just love to use any feature that’s available whether they need to or not. C++ has so many more options available to it than C that writing consistent code is a lot more effort and dev teams need very strict rules to stay efficient.
On balance though I’m happier with the code now that it is C++’ish. At least any new code will be more structured and maintainable and I’ll try to learn to live with the legacy code as is.