‘Abusing’ the C switch statement – beauty is in the eye of the beholder

Copyright: <a href='https://www.123rf.com/profile_stocksnapper'>stocksnapper / 123RF Stock Photo</a>

The C Language

Before we start, let’s get something straight; for over 30 years now I have had a love-hate relationship with the C programming language. The ‘engineer’ in me[1] sometimes just cannot believe we are still using C as the dominant embedded programming languages after all these years, and yet, I also see the simplicity and elegance the C code can bring. After all it’s just a tool, and even a good tool in the wrong hands; well we have plenty of examples of that…

To the task ahead

Let’s assume we’ve got a simple task to program; we have a byte-based interface where we’ll receive either command or status messages. The command messages are the byte value 0..3 and the status 10..12. From this we can create a pair of enum’s:

enum { CMD1, CMD2, CMD3, CMD4, END_CMD};
enum { STATUS1 = 10, STATUS2, STATUS3, END_STATUS };

We have three functions to invoke depending on the byte value:

void process_command_msg(int i);
void process_status_msg(int i);
void report_error(int i);

We could start with a if-else-if chain:

if ((num >= CMD1) && (num < END_CMD))
    process_command_msg(x);
else if ((num >= STATUS1) && (num < END_STATUS))
    process_status_msg(x);
else
    report_error(x);

Then refactored to:

#include <stdbool.h>

bool valid_command_message(int num) {
    if ((num >= CMD1) && (num < END_CMD)) return true;
    return false;
}

bool valid_status_message(int num) {
    if ((num >= STATUS1) && (num < END_STATUS)) return true;
    return false;
}

void process_message(int x)
{
    if (valid_command_message(x)) {
        process_command_msg(x);
    }
    else if (valid_status_message(x)) {
        process_status_msg(x);
    }
    else {
        report_error(x);
    }
}

Alternatively, we might consider that for the small set of message types it might be more efficient to use a switch statement:

void process_message(int x)
{
    switch (x) {
        case CMD1:
        case CMD2:
        case CMD3:
        case CMD4:
            process_command_msg(x);
            break;
        case STATUS1:
        case STATUS2:
        case STATUS3:
            process_status_msg(x);
            break;
        default:
            report_error(x);
            break;
    }
}

If the implementation uses a form of jump-table then this has the benefit of giving you an O(1) performance based on the messages, whereas with the if-else-if chain, the commands will be checked and processed before status and errors.

Adaptability

The common issue associated with switch statement is typically maintenance; especially where the set of ‘valid’ values needs extending. Let’s assume in v2.0 of the system we want to extend the message set to include two new commands and one new status message; thus:

enum { CMD1, CMD2, CMD3, CMD4, CMD5, CMD6, END_CMD};
enum { STATUS1 = 10, STATUS2, STATUS3, STATUS4, END_STATUS };

The significant difference is, as the code stands, that the if-else-if version will handle the change without modification, whereas the existing switch statement treats the new commands still as errors.

So, could we have structure the switch in a way it could have accommodated these potential changes?

Accommodating change

By simply combining the switch and the if-else-if we’d naturally achieve the desired result:

void process_message(int x)
{
    switch (x) {
        case CMD1: case CMD2: case CMD3: case CMD4:
            process_command_msg(x);
            break;
        case STATUS1: case STATUS2: case STATUS3:
            process_status_msg(x);
            break;
        default:
            if (valid_command_message(x)) {
                process_command_msg(x);
            }
            else if (valid_status_message(x)) {
                process_status_msg(x);
            }
            else {
                report_error(x);
            }
            break;
    }
}

Okay it works, but it just doesn’t sit right, does it?

Abuse ahoy

Now here it comes; please don’t read on if you have a nervous disposition…

Let’s start by moving the default around:

void process_message(int x)
{
    switch (x) {
        default:
            if (valid_command_message(x)) {
                process_command_msg(x);
            }
            else if (valid_status_message(x)) {
                process_status_msg(x);
            }
            else {
                report_error(x);
            }
            break;
        case CMD1: case CMD2: case CMD3: case CMD4:
            process_command_msg(x);
            break;
        case STATUS1: case STATUS2: case STATUS3:
            process_status_msg(x);
            break;
    }
}

We’ve moved the default from the last to the first label; as the switch is a conceptual jump-table this means if x == CMD1 it will bypass the default and jump straight to the case label CMD1.

Now if this means we will always jump to the label, then we can do this:

void process_message(int x)
{
    switch (x) {
        default:
            if (valid_command_message(x)) {
            case CMD1: case CMD2: case CMD3: case CMD4:
                process_command_msg(x);
                break;
            }
            else if (valid_status_message(x)) {
            case STATUS1: case STATUS2: case STATUS3:
                process_status_msg(x);
                break;
            }
            else
                report_error(x);
            break;
    }
}

And for the cherry on the top, the else statement (in this context) can act as the equivalent of a break statement. We can, therefore, remove all the break statements (thus also allowing us to remove the need for all the blocks)!

void process_message(int x)
{
    switch (x)
        default:
            if (valid_command_message(x))
            case CMD1: case CMD2: case CMD3: case CMD4:
                process_command_msg(x);
            else if (valid_status_message(x))
            case STATUS1: case STATUS2: case STATUS3:
                process_status_msg(x);
            else
                report_error(x);
}

Finally…

At this point I’d expect one of two responses[2]; the majority of you are reaching for a paper bag to help control your breathing and quoting how many MISRA-C rules this single bit of code actually breaks and why we should be using [insert your favorite language here] instead, etc.

But maybe, just maybe, some of you see the beauty…

[1] C.Eng.

[2] Well maybe a 3rd from all the HN trolls who claim they were taught this in kindergarten

Posted in C/C++ Programming | Tagged , , | 29 Comments

2016 – The Year of the Hack

IoT security has been headline news for at least the past 12 months, and we’ve also had an unprecedented number of incidents affecting consumers in more traditional areas: online banking account thefts, online fraud, OS vulnerabilities, you name it, it’s probably happened. In researching this blog, I found over 400 hack-related stories in The Register alone.

With increasing numbers of embedded devices becoming connected, we are going to hear more and more about security breaches, and guaranteeing security in increasingly sophisticated end-to-end systems is becoming an increasingly difficult task.

Here I’ve picked out just 12 examples:

Continue reading

Posted in Design Issues, General, Linux | Tagged , , | Leave a comment

A convenient untruth

Array notation in C is a lie!

Sorry, dear reader*, but I cannot participate in this conspiracy any longer.  You have been lied to, manipulated and coerced into thinking arrays are a construct of the C language.  I feel it is my solemn duty to blow the whistle on this charade and expose the dirty secrets of C’s so-called arrays.

(* It is statistically possible that more than one person might read this, of course)

Continue reading

Posted in C/C++ Programming | Tagged , , , | 9 Comments

Death and (virtual) destruction*

This time, we’ll have a more detailed look at one of those everybody-knows-that elements of C++ – virtual destructors.

More specifically, I want to reinforce under what circumstances you should make your destructor virtual; and when you don’t need to (despite what your compiler might say)

(*there’s no death)

Continue reading

Posted in C/C++ Programming, Design Issues | Tagged , , , , , , , , | 9 Comments

Getting your head around auto’s type-deduction rules

Automatic type-deduction is perhaps one of the more divisive features of Modern C++.  At its core it’s a straightforward concept:  let the compiler deduce the type of an object from its initialiser.   Used in the right way this can improve the readability and maintainability of your code.

However, because auto is based on template type-deduction rules there are some subtleties that can catch the unwary programmer.

In this article we’ll have a look at auto in the context of the template type-deduction rules to see where all these subtleties come from.

Continue reading

Posted in C/C++ Programming | Tagged , , , , , , , , , , | 1 Comment

An Introduction to Hypervisors

Hypervisors are becoming commonplace in the embedded world, especially in high-end multi-core systems. If you’d asked me about virtualisation or hypervisors 2 years ago, like most people I didn’t know much about them. A hypervisor, that’s a super-supervisor, right? Virtualisation, you mean Virtual Machines, right? Running Linux on Windows using VMware, right? Not any more!

screen-shot-2016-10-19-at-16-02-07

Here at Feabhas we’ve noticed a lot of our clients and contacts are starting to look at designs using hypervisors in embedded systems for a number of reasons. The main drivers are connected devices being used in the Internet of Things, multi-core split RTOS/Linux systems offering real-time Linux solutions, and the need for security and trusted execution environments, where critical processes are isolated from the outside world.

There is a lot of potential for using hypervisors on projects that designers haven’t considered using them in. Hypervisors can be used on bare metal systems or run on operating systems, they offer good task protection and isolation of hardware and memory resources. If you haven’t considered using a hypervisor on your system, or if you are considering using one and are just getting started, our Introduction to Hypervisors webinar is worth tuning in to.

 

 

Posted in ARM, Design Issues, General, Industry Analysis, Linux | Tagged , , , , , | Leave a comment

Off to the Embedded Linux Conference Europe and Open IoT Summit, Berlin 11th-13th October 2016

It’s hard to believe another year has passed and it’s time once again for the Embedded Linux Conference, and next week I’ll be off to Berlin to join a couple thousand other Linux enthusiasts for our annual bash.

A lot has happened in the past 12 months especially in the fields of security and the Internet of Things (IoT). A lot of people were talking about the IoT a year ago, we’re now seeing a lot more projects being completed, especially in industrial IoT, and there have been a number of high-profile hacking cases in the past 12 months (baby monitors, automotive (Tesla this time) and industrial (Ukraine National Grid)) to give food for thought about security.

Continue reading

Posted in General | Leave a comment

Great Expectations

Previously, we’ve looked at the basic concepts of function parameter passing, and we’ve looked at the mechanics of how parameters are passed at the Application Binary Interface (ABI) level.

Far too often we focus on the mechanisms and efficiency of parameter passing, with the goal: if it’s efficient then it’s good; that’s all there is to it.  In this article I want to move past simple mechanics and start to explore function parameter design intent – that is, what can I expect (to change) about the objects I use as function arguments; and what can I expect to be able to do with an object as a function implementer.

To that end, we’ll take a look at parameter passing from the perspective of the mutability (ability to be modified) of the parameters from both a caller’s and a function’s (callee’s) point of view.

Continue reading

Posted in C/C++ Programming, Design Issues | Tagged , , , , , , , , | 2 Comments

The three ‘No’s of sequential consistency

In the previous article we looked at the memory consistency problem that occurs when writing multi-threaded code for modern multi-processor systems.

In this article is we’ll have a look at how we can solve the sequential consistency problem and restore some sanity to our programming world.

Continue reading

Posted in ARM, C/C++ Programming, Cortex | Tagged , , , , , , | Leave a comment

Memory consistency made simple(ish)

The C++11 memory consistency model is probably one of the most significant aspects of Modern C++; and yet probably one of the least well-understood.  I think the reason is simple:  it’s really difficult to understand what the problem actually is.

The memory consistency problem is a concurrency problem.  That is, it’s a problem that occurs when we start writing multi-threaded code.  More specifically, it’s a parallelism problem – the real subtleties occur when you have two or more processors executing code.

In the first part of this two-part article we’ll have a look at the causes of the memory consistency problem.

Continue reading

Posted in C/C++ Programming, Design Issues, General | Tagged , , | 1 Comment