Tag Archives: example

C++ Templates for Embedded Code

Templates are a powerful feature of the C++ language but their syntax can be complex. This causes some developers to not use them, while others are concerned that templates might cause bloat to the compiled code.

I will explain how templates work and how you may use them in your code. Although the examples I provide are for the Arduino Uno and Adafruit Feather M0, the concepts will work with any platform and with all modern C++ compilers.

The first part explains the basics and function templates. Then I will explain template classes and template specialisation.

What are Templates

Templates were introduced to allow a single function to work with many different types or to build generic types from classes. They also enable an efficient way for the compiler to generate many variants of the same code for you.

If you are experienced with Python or JavaScript you may be familiar with dynamically typed variables. In these languages, the same function can be called with parameters of various types. Be careful not to confuse dynamically typed variables with the concept of templates.

In C++ the types of function templates and template classes are defined at compile-time and thus, they are type-safe. In Python and JavaScript, however, the dynamic types of variables are checked at runtime and are not type-safe.

Here is a simple example written for the Arduino Uno:

template<typename Value>
void circularShift(Value &value) {
    const bool firstSet = (value & 0b1u);
    value >>= 1;
    if (firstSet) {
        value |= static_cast<Value>(0b1u) << (sizeof(Value)*8-1);
    }
}

uint32_t value1 = 0xabcd0123u;
uint16_t value2 = 0x4545u;

void setup() {
    Serial.begin(115200);
    while (!Serial) {}
}

void loop() {
    Serial.println(value1, BIN);    
    Serial.println(value2, BIN);    
    circularShift(value1);
    circularShift(value2);
    delay(1000);
}

In this example, the circularShift function is similar to executing >> 1 on a value, shifting all of the bits one position to the right. The difference is that circularShift “wraps” the bits, so the right-most bit (which would otherwise be shifted out of the value) moves to the left-most bit. For example:

0b11100101 >> 1  // results in 01110010
circularShift(0b11100101) // results in 11110010

To test the function, we use a 32-bit and a 16-bit value declared in lines 10 and 11 of the example code. In the loop() function the values are printed and then circular-shifted to the right in lines 21 and 22.

It is not necessary to understand the syntax of the function template for now, I will discuss this later on.

Compiling the Example

Processing our example the compiler will treat the function template differently than regular functions. It will keep the template function prototype in memory as it is but will not generate any code for it, yet.

Continue reading C++ Templates for Embedded Code

Auto and Structured Binding

This article is just a short follow-up article for “Write Less Code using the ‘auto’ Keyword”. Structured binding is something handy, introduced in C++17. Therefore, only the latest compiler will support it.

If you are mainly write embedded code, you may skip this article, because it will take some years until C++17 support is available for your platform. If you also write desktop code, read on, you may like this feature.

Find a detailed reference about structural binding here. The following sections will explain the feature using several use cases.

Multiple Return Values using Tuples

Let us write a function to convert a string into an integer. For error handling, we like to use parameters or return values, but no exceptions. You may consider these variants:

uint32_t getInt(const std::string &text, bool *ok);
uint32_t getInt(const std::string &text, bool &ok);
bool getInt(const std::string &text, uint32_t &result);

The problem is, if you like to use the full range of the uint32_t, you need a second value to report of the conversion was successful or not. It may be a bool, or even better an enum to report this result.

#include <iostream>
#include <cstdint>
#include <string>
#include <limits>

uint32_t safeMultiplication(uint32_t a, uint32_t b, bool *ok = nullptr) {
    uint32_t result;
    if (__builtin_mul_overflow(a, b, &result)) {
        if (ok != nullptr) {
            *ok = false;
        }
        return 0;
    }
    if (ok != nullptr) {
        *ok = true;
    }
    return result;
}

uint32_t safeAdd(uint32_t a, uint32_t b, bool *ok = nullptr) {
    uint32_t result;
    if (__builtin_add_overflow(a, b, &result)) {
        if (ok != nullptr) {
            *ok = false;
        }
        return 0;
    }
    if (ok != nullptr) {
        *ok = true;
    }
    return result;
}

uint32_t getInt(const std::string &text, bool *ok = nullptr) {
    uint32_t result = 0;
    uint32_t factor = 1;
    bool factorValid = true;
    for (auto it = text.rbegin(); it != text.rend(); ++it) {
        if (*it < '0' || *it > '9') {
            if (ok != nullptr) {
                *ok = false;
            }
            return 0;
        }
        auto value = static_cast<uint32_t>(*it - '0');
        if (value > 0) {
            if (!factorValid) {
                if (ok != nullptr) {
                    *ok = false;
                }
                return 0;
            }
            bool multiplicationOk;
            value = safeMultiplication(factor, value, &multiplicationOk);
            if (!multiplicationOk) {
                if (ok != nullptr) {
                    *ok = false;
                }
                return 0;
            }
            bool additionOk;
            result = safeAdd(result, value, &additionOk);
            if (!additionOk) {
                if (ok != nullptr) {
                    *ok = false;
                }
                return 0;
            }
        }
        if (factorValid) {
            bool multiplicationOk;
            factor = safeMultiplication(factor, 10u, &multiplicationOk);
            if (!multiplicationOk) {
                factorValid = false;
            }
        }
    }
    if (ok != nullptr) {
        *ok = true;
    }
    return result;
}

void testText(const std::string &text) {
    bool ok;
    const auto number = getInt(text, &ok);
    std::cout << text << " -> " << number << " ok: " << (ok ? "yes" : "no") << std::endl;
}

int main(int argc, const char * argv[]) {
    testText("text");
    testText("4294967295");
    testText("4294967296");
    testText("00004294967295");
    testText("x123");
    return 0;
}

The example code works well. For simplicity, it uses two built-in functions __builtin_add_overflow and __builtin_mul_overflow from the GCC and clang compiler.

Using two return values would simplify the interface. It can be done using std::tuple and structured binding.

#include <iostream>
#include <cstdint>
#include <string>
#include <limits>
#include <tuple>

using Result = std::tuple<uint32_t, bool>;

Result safeMultiplication(uint32_t a, uint32_t b) {
    uint32_t result;
    if (__builtin_mul_overflow(a, b, &result)) {
        return std::make_tuple(0, false);
    }
    return std::make_tuple(result, true);
}

Result safeAdd(uint32_t a, uint32_t b) {
    uint32_t result;
    if (__builtin_add_overflow(a, b, &result)) {
        return std::make_tuple(0, false);
    }
    return std::make_tuple(result, true);
}

Result getInt(const std::string &text) {
    uint32_t result = 0;
    uint32_t factor = 1;
    bool factorValid = true;
    for (auto it = text.rbegin(); it != text.rend(); ++it) {
        if (*it < '0' || *it > '9') {
            return std::make_tuple(0, false);
        }
        const auto digitValue = static_cast<uint32_t>(*it - '0');
        if (digitValue > 0) {
            if (!factorValid) {
                return std::make_tuple(0, false);
            }
            const auto[value, multiplicationOk] = safeMultiplication(factor, digitValue);
            if (!multiplicationOk) {
                return std::make_tuple(0, false);
            }
            bool additionOk;
            std::tie(result, additionOk) = safeAdd(result, value);
            if (!additionOk) {
                return std::make_tuple(0, false);
            }
        }
        if (factorValid) {
            bool multiplicationOk;
            std::tie(factor, multiplicationOk) = safeMultiplication(factor, 10u);
            if (!multiplicationOk) {
                factorValid = false;
            }
        }
    }
    return std::make_tuple(result, true);
}

void testText(const std::string &text) {
    const auto[number, ok] = getInt(text);
    std::cout << text << " -> " << number << " ok: " << (ok ? "yes" : "no") << std::endl;
}

int main(int argc, const char * argv[]) {
    testText("text");
    testText("4294967295");
    testText("4294967296");
    testText("00004294967295");
    testText("x123");
    return 0;
}

It simplified the code with just a single downside. While the initial implementation made the ok optional, the new version will get it for each result.

Bind Elements of a Struct

You can bind to members of any struct using the same syntax:

struct Result {
    uint32_t value;
    bool ok;
}

Result result = {10, true}

int main() {
    auto[value, ok] = result;
}

If we use a struct in our example, it simplifies several lines, but requires some additions elsewhere.

#include <iostream>
#include <cstdint>
#include <string>
#include <limits>
#include <tuple>

struct Result {
    uint32_t value;
    bool ok;
    constexpr static Result error() { return {0, false}; }
    constexpr static Result success(uint32_t value) { return {value, true}; }
};

Result safeMultiplication(uint32_t a, uint32_t b) {
    uint32_t result;
    if (__builtin_mul_overflow(a, b, &result)) {
        return Result::error();
    }
    return Result::success(result);
}

Result safeAdd(uint32_t a, uint32_t b) {
    uint32_t result;
    if (__builtin_add_overflow(a, b, &result)) {
        return Result::error();
    }
    return Result::success(result);
}

Result getInt(const std::string &text) {
    uint32_t result = 0;
    uint32_t factor = 1;
    bool factorValid = true;
    for (auto it = text.rbegin(); it != text.rend(); ++it) {
        if (*it < '0' || *it > '9') {
            return Result::error();
        }
        const auto digitValue = static_cast<uint32_t>(*it - '0');
        if (digitValue > 0) {
            if (!factorValid) {
                return Result::error();
            }
            const auto[value, multiplicationOk] = safeMultiplication(factor, digitValue);
            if (!multiplicationOk) {
                return Result::error();
            }
            const auto[immediateResult, additionOk] = safeAdd(result, value);
            if (!additionOk) {
                return Result::error();
            }
            result = immediateResult;
        }
        if (factorValid) {
            const auto[newFactor, multiplicationOk] = safeMultiplication(factor, 10u);
            if (!multiplicationOk) {
                factorValid = false;
            }
            factor = newFactor;
        }
    }
    return Result::success(result);
}

void testText(const std::string &text) {
    const auto[number, ok] = getInt(text);
    std::cout << text << " -> " << number << " ok: " << (ok ? "yes" : "no") << std::endl;
}

int main(int argc, const char * argv[]) {
    testText("text");
    testText("4294967295");
    testText("4294967296");
    testText("00004294967295");
    testText("x123");
    return 0;
}

The struct made the status optional, because we can write a line like this:

const auto value = getInt(text).value;

Or only check if the value is valid:

if (getInt(text).ok) {
    // ...
}

Binding to an Array

Binding values from an array is another interesting use:

#include <iostream>

int table[4][4] = {
    { 1,  2,  3,  4},
    { 5,  6,  7,  8},
    { 9, 10, 11, 12},
    {13, 14, 15, 16},
};

int main(int argc, const char * argv[]) {
    for (const auto &row : table) {
        const auto [a, b, c, d] = row;
        std::cout << a << ", " << b << ", " << c << ", " << d << std::endl;
    }
    return 0;
}

References

I found structural binding also useful creating references to values for actual modification, or const references to avoid copy operations.

#include <iostream>

int table[4][4] = {
    { 1,  2,  3,  4},
    { 5,  6,  7,  8},
    { 9, 10, 11, 12},
    {13, 14, 15, 16},
};

int main(int argc, const char * argv[]) {
    for (auto &row : table) {
        auto& [a, b, c, d] = row;
        std::swap(a, d);
        std::swap(b, c);
    }
    for (const auto &row : table) {
        const auto& [a, b, c, d] = row;
        std::cout << a << ", " << b << ", " << c << ", " << d << std::endl;
    }
    return 0;
}

Learn More

Conclusion

If you already use a C++17 capable compiler, like the GCC, clang or Visual Studio, try to use structural binding in your project. You can find a table with the current compiler support in this document.

  • It can create cleaner interfaces, where all values are returned with the return statement.
  • Returned objects can be directly split into variables.
  • Working with std::tuple values is straightforward.
  • Array tables can be accessed in a column like way.

If you have questions, miss some information or have any feedback, feel free to add a comment below.

Guide to Modular Firmware

This article is for embedded software developers with good knowledge of C or C++, but struggle with large and complex projects.

If you learn developing embedded code, e.g. using the Arduino IDE, you find plenty of small example programs. It is helpful for a quick start, but as soon your project grows, help about software design is rare.

In contrast, if you learn software development for desktop applications, project structures and software design is an integral part of the learning process.

With this short article, I will give you a simple guideline, how you can build a modular structure for your firmware, which will keep your code clean and maintainable for large and complex projects.

Refactor your Code

If you start a new project, you can already prepare the structures as described. I assume in this article you already have a working firmware but need to improve the code quality.

Improving the code in an iterative process is called refactoring. Testing is an integral part of this process. After each small change, you test if the software is still working as expected.

In desktop application development, there are unit tests to ensure the integrity of smaller modules. I found it difficult to apply unit tests to embedded code, if not for small independent functions or modules. Therefore you have to use a simple run-time test of your software, to make sure it is still working as expected.

Refactoring only changes the code, but not the functionality. Even if you change names, move code around and change implementations, the function of your code stays exactly the same. It is important you either change or extend functionality or do refactoring, but never do both at the same time (or in the same commit).

Use a Version Control System

Changing your code without version history is a bad idea. If you do not already manage your code in a version control system, now it is the time to start using one.

If you never used a version control system before, use GIT and read one of the many tutorials on how to use it. There are graphical user interfaces for any operating system, so you do not have to work on the console. It does not matter how you manage your code – it is important that you use a version control system.

After each small successful change, you should commit a new version. If you run into troubles at a later stage, you can easily analyse every change you did on the code and go back to the last working version.

The Demo Setup

If you like to follow along using the real demo setup, you will need an Arduino Uno, three LEDs with matching resistors and two pushbuttons. The example code expects a circuit shown in the next illustration.

A Shockingly Bad Example to Start With

The example code to start with is something I sadly see often. Please open a second browser window with the code at the following URL:

https://github.com/LuckyResistor/guide-modular-firmware/blob/master/fade_demo_01/fade_demo_01.ino

I can not use a really complex firmware for this article, and your source code may be in a different state. Nevertheless, this example code contains most of the elements I like to discuss.

Because of the length of the code, I will just link to the full examples. The code snippets in the article should have the correct line numbers, so you can easily find the locations.

Continue reading Guide to Modular Firmware

Class or Module for Singletons?

Should you use a class or a module with a namespace for a singleton interface in your firmware? I found there are many misunderstandings which lead beginners to make a wrong decision in this matter. With this article, I try to visualize these misunderstandings with simple example code for the Arduino platform.

Before we start, as with all of these topics, there is no simple rule, and there are a lot of exceptions. In the end, it heavily depends on the compiler and architecture you use.

The Example Use Case

I like to write simple driver code for my firmware, which flashes two LEDs for a given duration. The used PINs for the LEDs shall be configurable. In my main loop, I will flash the two LEDs at different durations.

The use case is no real-world example, but it contains all elements of configuration, initialization and usage.

Using a Simple Class

For the first test case, I write a simple class, without constructor and all required methods for the use case.

Continue reading Class or Module for Singletons?