5 C++ Tricks for C Programmers

Over the past several years, C++ has become a more popular language to use in embedded systems than C. Don’t get me wrong, C will continue to be a dominant language for years to come, but C++ offers developers modern tools to leverage when designing more reusable, scalable, and portable code. Teams aren’t just abandoning C, but instead, when starting new product development are instead adopting C++. The language has been evolving with the times and offers many improvements over C. In this post, let’s examine five simple C++ tricks that C programmers will immediately appreciate.

Trick #1 – Conditional compilation using constexpr

The bane of many embedded code bases written in C is the large numbers of #if / #elif / #else preprocessor directives. The preprocessor directives are often used to conditionally compile code in and out of the image. For example, if we have three different versions of hardware, we’ll often create a macro that is used and then checked based on the build configuration to determine pin assignments and so forth. The problem with conditional compilations using the preprocessor is that the code gets messy and sometimes quite difficult to follow.

Starting in C++ 17, the language introduced the ability for developers to conditionally compile code using constexpr. Developers can leverage this compiler feature to optimize code based on templates and even to remove preprocessor directives that are using #ifdef blocks. For example, if we had a code base that had three different hardware configurations, we might find that our code to initialize GPIO looks something like this:

void Gpio_Init()

{

#ifdef __HARDWARE_REV1__

// Initialize pin set #1

#elif __HARDWARE_REV2__

// Initialize pin set #2

#elif __HARDWARE_REV_3__

// Initilialize pin set #3

#endif

}

The code above is configurable, but it’s quite nasty to look at. Yes, a modern IDE will shade some of the options, but it’s just not a very elegant solution and leads to some messy code. In C++, we can leverage the constexpr compile time optimization and write code like the following:

constexpr Hardware_t HardwareRev = Hardware::Rev1

void Gpio_Init()
{
    if constexpr (HardwareRev == Hardware::Rev1)
    {
        // Initialize pin set #1
    }
    else if constexpr (HardwareRev == Hardware::Rev2)
    {
        // Initialize pin set #2
    }
    else if constexpr(HardwareRev == Hardware::Rev3)
    {
        // Initialize pin set #3
    }
}

When we compile the above code for Rev2, only the code for “Initialize pin set #2” makes it into our executable. If we compile for Rev1, only the code for “Initialize pin set #1” makes it into the executable, and so forth.

Trick #2 – Ranged for loops

A fundamental flow control mechanism in C and C++ is the for loop. The C for loop has been stuck in the dark ages by not having a simplified range-based option. For example, languages like Python allow a programmer to iterate over a range using syntax like:

for x in range (1, 5):
    print(x)

In C, we need to write (depending on the C standard used of course):

for(int x = 1; x <= 5; x++)
{
    printf(“%d \r\n”, x);
}

Starting in C++ 11, an additional version of the for loop was added that makes working with ranged values easier. For example, if one wanted to write the above code examples in C++, we could now write it as:

int MyNums[] = {1, 2, 3, 4, 5};

for(int i : MyNums)
{
    std::cout << i << std::endl;
}

At first, to a C developer, this may seem awkward; However, given how often we want to work over a range in an enum or object, the syntax is cleaner and easier to read.

Trick #3 – Use auto

For C developers, auto is a language keyword that was deprecated a long time ago. Developers used to use auto to specify a variable that was limited to the current scope. Auto is a storage class specifier like static, only it specifies that the storage is local and the variable should be automatically destroyed once our of scope, unlike static which allows the variable to persist.

In C++, auto can be a very useful keyword that tells the compiler to automatically assign the datatype for the developer. For example, in Trick #2, we had the following for loop code:

int MyNums[] = {1, 2, 3, 4, 5};

for(int i : MyNums)
{
    std::cout << i << std::endl;
}

MyNums is already defined as an int, so I can let the compiler decide on the type that should be used for i as follows:

for(auto i : MyNums)
{
    std::cout << i << std::endl;
}

At first, this may appear sloppy. As the developer, shouldn’t I control what my variable types are? The answer is yes, but we should allow the compiler to manage types where it could save us time or where we don’t care to manually specify. Do we care if i for example is uint8_t, uint16_t, uint32_t and so forth? Probably not. We just want something that can iterate over the range of one to five and the compiler is more than capable of deciding. Auto can also help when we change a data type. We can change it in one place without having to worry about changing anything downstream that uses or interacts with it.

Trick #4 – The spaceship operator

It can sometimes be annoying when you need to write a conditional statement that checks whether a value is less than, greater than, or equal to another value. Just recently, C++20 add a three-way comparison operator that can simplify readability and the code. This operator <=>, is often called the “spaceship” operator because it looks like a spaceship.

Using the spaceship operator is simple. For example, if we have two variables and we want a three-way comparison, we might write code like the following:

int Var1 = Value1;
int Var2 = Value2;

auto Result = Var1 <=> Var2;

If Var1 < Var2, then Result will be less than 0. If Var1 > Var2, the Result will be greater than 0. If Var 1 is equal to Var2, then Result will be 0.

Trick #5 – Getting the size of a string

Strings in C are nothing more than a char array with ‘\0’ as the last array entry. Many bugs have resulted in applications from how C deals with strings. It’s not uncommon to forget the string terminator in a string, incorrectly size the array, or use string functions that can result in buffer overruns, and so forth.

In C++, strings can be managed much more safely. I’m not going to go into all the cool details in this post, that is up to the reader, but I do want to just point out how simple things can be in C++. For example, if a developer needs to get the length of a string, they can simply use the length() method associated with the string. A simple code example might be where we allow the user to input a string and then verify its length prior to use:

string Input;

getline(cin, Input);

if(Input.length() < 50)
{
    std::out << “Processing String …” << std::endl;
}
else
{
    std::out << “String to long! Try again” << std::endl;
}

It’s a simple example, but I highly encourage the reader to investigate the string libraries that are provided in C++. You’ll be surprised how safe, and easy it is to use strings!

Conclusions

C++ is gaining traction in many embedded applications, especially ones that are developed from the ground up and don’t rely on large amounts of legacy code. At first glance, C++ may seem intimidating to C programmers; However, C programmers will find that C++ is a natural extension of C into a more modern programming language. Design patterns and techniques that would be nearly impossible in C are easy in C++. In this post, we’ve explored a few simple C++ tricks that I think will interest C developers. We’ve barely begun to scratch the surface, but if you are a C programmer, I think you’ll find that C++ has a lot to offer to embedded applications.

5 thoughts on “5 C++ Tricks for C Programmers”

  1. This is probably the least evangelical piece about C++ vs. C in the embedded space I’ve seen lately (this is a good thing!). My only remarks is that if you have polymorphism and inheritance, you probably should NOT write GpioInit this way 🙂
    For this argument I’ve sometimes hear a counterargument that using C++ does not mean you are obliged to use polymorphism and inheritance, but then why use C++? Yes, C++ has couple nice things which should have been migrated to C a long time ago (spaceship! Please!) and sometime ago a group of developers argued for something like C+ (or C+-, C-+ etc.) but that went nowhere … My other idea was to borrow from ADA the concept of language profiles … but who knows what ADA is … (unless you write for spacecrafts).

  2. Just tiny fix of mistype:
    “std::cout << I << std::endl;"
    needs to be replaced with
    "std::cout << i << std::endl;"
    anywhere.

    1. Dang! I thought I caught all the autocorrect! Good catch! (The problem with writing code examples in Word!)

  3. Just a small fix to Python code: you should have a colon before the next block begins, i.e.

    for x in range (1, 5)
    print(x)

    =>

    for x in range (1, 5):
    print(x)

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.