Quick Tips to Fake Your Way Through C++ As A C Programmer

In 2020, many things were just unusual. My previous project had a mixture of C and modern C++ but two things were clear: It was one person who setup everything (and since left the day I arrived) who was driving the more interesting (and difficult to understand) C++ and most of the team was truly more comfortable with C.

I am definitely part of the larger group of C programmers who remember the 90’s and 00’s where C++ was still considered an excess memory user. When the team doubled and many felt more comfort with modern C++, there was a push to go full C++ and at least go C++ 17 or better. I felt this was a great time to update since so many of the team would need to do so. Here’s a handful of things I learned that give you some “C++ ness”.

Warning: I’m still just a noob here despite technically learning C++ before C. If you are already rolling C++ deep, you’ll likely find this boring, annoying, or worse case feel the need to “well actual” me. None of us is there for that, so let’s just part ways and meet up on twitter.

Additional observation before we begin: After spending the better part of 2020 in C++, I have observed that even those with years of experience in a professional setting, cannot easily explain the intricacies of C++. It seemed that C++14 onward provided some new interesting features, but also created some inconsistencies. It was easy to notice this in a few weeks and no one was great at explaining the “whys” or “how do you know when to…’s” and so I won’t be addressing this. The fact is this language has evolved and continues to evolve.

Namespaces

Perhaps you are aware of this concept from Linux kernel work or are a lover of the keyword extern. The names matter and vagueness is a problem. Modern C++ uses namespaces to scope limit modules including functions, classes, constants, etc…

namespace MyNewSpace {
  // All your favorite code stuff in here.
  int kFavoriteInt = 13;
  void FavoriteFunction(int input) {}
 
} // MyNewSpace

MyNewSpace::FavoriteFunction(MyNewSpace::kFavoriteInt);

You can nest namespaces too. You can use the same namespaces for a device driver, a subsystem and the unit tests. Consider it a way to organize your code.

Also there are anonymous namespaces which act as a replacement for the keyword static in C. This is particularly true if you want your static functions that are not part of a class.

Speaking of … how do you include your well-vetted C libraries?

Integrating C Libraries

So you are dedicated to using the C++ compiler but all you have are your C headers and libraries? Try this one. Just wrap your headers like this:

#ifdef __cplusplus
extern "C" {
#endif

// All your C code declarations.

#ifdef __cplusplus
}
#endif

Now just include the file as usual. You can find more details here.

#define No More!

Sure you could just keep on #define away but you wanna keep people wondering “Hey aren’t they just a C programmer!?!?” Try this to keep them guessing. Replace your #define and avoid the preprocessor by using constexpr (I would read up as this has changed several times since C++11).

#define MAX_FAVORITE_VALUE (0xFF)
// Becomes ...
constexpr uint8_t kMaxFavoriteValue = 0xFF;

Notice that above that value is now const AND typed. Pretty simple and straightforward. You can also have derived expressions. One benefit is debugging is easier. I was no longer buried trying to figure out what was wrong in an obfuscated code.

Typecasting

My opinion is that this is where C++ gets cluttered. You DON’T HAVE to do this but it will let people wonder. You have 4 types of casts:

  • static_cast – pretty much use it in 95% of all cases
  • const_cast – add/removes const from target expression
  • reinterpret_cast – less restrictive casting than previous two. Use cautiously and read up.
  • dynamic_cast – casting a class up/down its hierarchy.
int array[5];  // Using a c-style array here. No biggie.
byte * ptr = static_cast<byte*> (array);

References Are the New Hotness

References aren’t really new or hot, but for a C programmer, we are really used to passing pointers and references are a new concept. Maybe you just need to pass a class instance to call a function later on, so just pass the reference or a pointer to the class and save the instance as <type>& instead of a <type>*. It will mean you don’t need to check for NULL pointers (in C++ nullptr) anymore.

Quicky Classes

So you want to use classes but are used to passing in a big struct with the “class” details to your function, classes are just flipping the script.

  1. Everything you passed in to modify and keep track of in your C code, is going to become a private member variable in your C++ class. Note: Don’t go struct crazy.
  2. The Init, Read, Write, Verb etc… functions are your public member functions.
  3. Any other functions that were static or only called by the public member functions are now your private functions.

Bonus Faker Topic

You still here? Clearly you ran out of stuff to do during this pandemic or you are begging to change things up. It’s ok to check in on your sourdough starter or garden instead of this. But if you insist, let’s do it.

Getting out of the “Just C” mode means you have more options in modern software development. Namely, you have more options for doing unit tests since many assume you are using C++. With this in mind, if you are looking for a C supported one, Unity works and can support Mocks. However, now that you are faking… and making it… why not try CppUtest or GTest? I highly recommend reading “Test Driven Development in Embedded C” by James Grenning.

What can you do to easily set yourself up for more than just a quick host unit test using mocks? Well, if you make a virtual base class for the type module you are making (ex. audio driver class as virtual base class) and then later a concrete class (ex. audio driver class for a specific chip ) inheriting the previously mentioned virtual base class, now you have some options. With that virtual base class you can now:

  • Make as many audio drivers as you need in the event you are swapping in/out a bunch of audio chips.
  • Anyone using the driver class instance should use a reference of the virtual base class. It can take in any of the new audio driver classes you made based on this virtual base class and they ALL share the same API.
  • When you need a fake of a particular audio driver to support the unit test of another module, it is easily whipped up (by you) as long as the class type needed by the module under test needs an instance of type virtual base class.

Read more about fakes, mock, and doubles here.

Ok. That’s all I had on my mind. Not too many things and most of them didn’t make you have to learn a bunch of new things.