C++ and C – Mixed Code Issues and How to Rectify

c

Background/Scenario

I started writing a CLI application purely in C (my first proper C or C++ program that wasn't "Hello World" or a variation thereof). Around midway through I was working with "strings" of user input (char arrays) and I discovered the C++ string streamer object. I saw that I could save code using these, so I used them through the application. This means that I have changed the file extension to .cpp and now compile the app with g++ instead of gcc. So based on this, I would say the application is now technically a C++ application (although 90%+ of the code is written in what I would call C, as there is lots of cross-over between the two language given my limited experience of the two). It is a single .cpp file around 900 lines long.

Important Factors

I want the program to be free (as in money) and freely distributable and usable for all to have. My concern is that someone will look at the code and think something to the effect of:

Oh look at the coding, it's awful, this program can't help me

When potentially it could! Another matter is the code being efficient (it is a program for testing Ethernet connectivity). There should be no parts of the code that are so inefficient that they can severely hinder the performance of the application or its output. However, I think that is a question for Stack Overflow when asking for help with specific functions, methods, object calls, etc.

My question

Having (in my opinion) mixed C and C++ where perhaps I shouldn't. Should I look to rewrite it all in C++ (by this, I mean implement more C++ objects and methods where perhaps I have coded something in a C style that can be condensed using newer C++ techniques), or remove the use of string streamer objects and bring it all "back" to C code? Is there a correct approach here? I am lost and need some guidance on how to keep this application "Good" in the eyes of the masses, so they will use it and benefit from it.

The Code – Update

Here is a link to the code. It is circa 40% comments, I comment nearly every line until I feel more fluent. In the copy I have linked to though, I have removed pretty much all the comments. I hope this doesn't make it too hard to read. I am hoping though that no one should need to fully understand it. If I have made fatal design flaws though, I am hoping they should be identifiable easily. I should also mention, I am writing a couple of Ubuntu desktops and laptops. I'm not intending to port the code to other operating systems.

Best Answer

Let's start at the beginning: mixed C and C++ code is fairly common. So you're in a big club to start with. We have huge C codebases in the wild. But for obvious reasons many programmers refuse to write at least new stuff in C, having access to C++ in the same compiler, new modules start to be written that way -- at first just leaving the existing parts alone.

Then eventually some existing files get recompiled as C++, and some bridges can be deleted... But it may take really long time.

You are ahead somewhat, your full system is now C++, just most of it is written "C-style". And you see mix of styles a problem, what you should not: C++ is a multi-paradigm language supporting many styles, and allow them to co-exist for good. Actually that is the main strength, that you are not forced to a single style. One that would be suboptimal here and there, with some luck not everywhere.

Re-working the codebase is a good idea, IF it is broken. Or if it is in the way of development. But if it works (in the original sense of word), please follow the most basic engineering principle: if it ain't broke, don't fix it. Leave the cold parts alone, put your effort where it counts. On the parts that are bad, dangerous -- or in new features, and just refactor parts to make them a bed.

If you seek general things to address, here's what worth evicting from a C codebase:

  • all the str* functions and char[] -- replace them with a string class
  • if you use sprintf, create a version that returns a string with the result, or puts it in the string, and replace usage. (If you never bothered with streams do yourself a favor and just skip them, unless you like them; gcc provides perfect type safety out of the box for checking formats, just add the proper attribute.
  • most malloc and free -- NOT to with new and delete, but vector, list, map and other collectons.
  • the rest of memory management (after the previous two points it must be pretty rare, cover with smart pointers or implement your special collections
  • replace all other resource usage (FILE*, mutex, lock, etc) to use RAII wrappers or classes

When you're done with that you approach the point where the codebase can be reasonably exception-safe, so you can drop return-code football using exceptions and rare try/catch in high-level functions only.

Beyond that just write new code in some healthy C++, and if some classes are born that are good replacement in existing code, pick them up.

I didn't mentions syntax-related stuff, obviously use refs instead of pointers in all new code, but replacing old C parts just for that change is no good value. Casts you must address, eliminate all you can, and use C++ variants in wrapper functions for the remainder. And very importantly, add const wherever applicable. These interleave with the earlier bullets. And consolidate your macros, and replace what you can make into enum, inline function or template.

I suggest reading Sutter/Alexandrescu's C++ Coding Standards if not yet done and follow them closely.

Related Topic