Using Modern C++ Techniques With Arduino

C++ has been quickly modernizing itself over the last few years. Starting with the introduction of C++11, the language has made a huge step forward and things have changed under the hood. To the average Arduino user, some of this is irrelevant, maybe most of it, but the language still gives us some nice features that we can take advantage of as we program our microcontrollers.

Modern C++ allows us to write cleaner, more concise code, and make the code we write more reusable. The following are some techniques using new features of C++ that don’t add memory overhead, reduce speed, or increase size because they’re all handled by the compiler. Using these features of the language you no longer have to worry about specifying a 16-bit variable, calling the wrong function with NULL, or peppering your constructors with initializations. The old ways are still available and you can still use them, but at the very least, after reading this you’ll be more aware of the newer features as we start to see them roll out in Arduino code.

How big are your integers?

C++11 introduced a series of fixed width integer types aliases that will give you the number of bits (in multiples of 8) you want. There are both signed and unsigned versions. If you use int16_t for a variable you know it’s 16 bits, regardless of which Arduino is being targeted.

int8_t/uint8_t - a signed/unsigned type that is exactly 8 bits in size.
int16_t/uint16_t - a signed/unsigned type that is exactly 16 bits in size.
int32_t/uint32_t - a signed/unsigned type that is exactly 32 bits in size.
int64_t/uint64_t - a signed/unsigned type that is exactly 64 bits in size.

These are aliases of the underlying types, so on Arduino Uno, int8_t is the same type as a char, and uint16 is the same size as an unsigned int. One note, if you’re editing a ‘.cpp’ file, you’ll have to #include <stdint.h>, but in a ‘.ino’ file, you don’t.

Anonymous Namespaces

The concept of an anonymous namespace has been around for a while in C++, but it came to prominence with the introduction of C++11. In C++11, the anonymous namespace is now the preferred way to specify a variable, function, or class that is accessible only in the current file (ie., they have internal, rather than external, linkage). It’s also a nice way to consolidate things which only need to be accessed within the current file. Anonymous namespace are also called ‘unnamed namespaces’ because, as the name suggests, they are defined without a name:

namespace {
  ... 
}

Anonymous namespaces take the place of declaring things static. Anything you may have declared as static or const, or written as a #define can now put into an anonymous namespace and have the same effect – anything defined in inside cannot be accessed outside of the ‘.cpp’ file that the namespace is in. In the Arduino IDE, though, if you add a tab and give the new tab a name that doesn’t end in ‘.cpp’ or ‘.h’ then the file is given an extension of ‘.ino.’ When you click the ‘Verify’ button, all the ‘.ino’ files are concatenated into a single ‘.cpp’ file. This means that anything you declare as being static or const or in an anonymous namespace will be available in each ‘.ino’ file because, when they are concatenated, they all end up in the same ‘.cpp’ file. This usually isn’t a problem for small Arduino programs, but it’s good to be aware of it.

Okay, so now we know about internal and external linkages and how files are concatenated before being compiled. How does this help us in our code? Well, we now know how to define variables so that they won’t leak into areas they’re not supposed to be.

rather than write:

// This should be used sparingly anyway
#define SOME_VAR 1000    
// Static variables declared in a file are local to the file
static int16_t count = 0;    
// const variables declared in a file are local to the file as well
const int16_t numLeds = 4;  

you can now write:

namespace {
  const int16_t SOME_VAR = 1000;  // Now it's type-safe
  int16_t count = 0;  // No need to use static
  const int16_t numLeds = 0;  // Still declared const.

  class thisClassHasACommonName {
    ...
  };
}

Everything’s contained within the anonymous namespace at the beginning of the file. The compiler won’t get confused if there’s another SOME_VAR, count or numLeds defined in a different file. And unlike static, classes declared in an anonymous namespace are local to the file as well.

Automatic for the people

The auto keyword, added in C++11, allows you to define a variable without knowing its type. Once defined, though, like other variables, it’s type can’t be changed, just like regular C++ variables. The C++ compiler uses deduction figure out the variable’s type.

auto i = 5;          // i is of type int
auto j = 7.5f;       // j is of type float
auto k = GetResult();  // k is of whatever type GetResult() returns 

in order for you to specify that you want a reference, you can do this:

auto&amp; temp1 = myValue; 
const auto&amp; temp2 = myValue;

The proper type is deduced for pointers as well:

 
int myValue = 5; 
auto myValuePtr = &amp;myValue; // myValuePtr is a pointer to an int.

Auto is a great shorthand for those especially long, complicated types.  It works great for defining iterator instances!

Using using

There have been a couple of ways to create aliases in C++: The lowly (and dangerous) #define and the less dangerous typedef. The use of typedef is preferred to a #define, but they do have a couple of issues. The first is readability. Consider the following typedef:

typedef void(*fp)(int, const char*);

At first glance, especially if you don’t use C++ a lot, it can be difficult to determine that this is creating an alias, fp, that is a pointer to a function that returns void and takes an int and a string parameter.  Now, let’s see the C++11 way:

using fp = void(*)(int, const char*);

We can now see that fp is an alias to something, and it’s a bit easier to determine that it’s a function pointer that it’s an alias of.

The second difference is a bit more esoteric, at least in Arduino programming. Usings can be templatized while typedefs cannot.

Nullptr

In C and C++98, NULL is actually defined as 0. To be backwards compatible, the C++ compiler will allow you to initialize a pointer variable using 0 or NULL.

When you start using auto, though, you’ll start seeing code like this:


auto result = GetResult(...);

if (result == 0) { ... }

Just from looking at this, you can’t tell if GetResult returns a pointer or an integer. However, even among those who still use C, not many will check if result == 0, they’ll check if result == NULL.

auto result = GetResult(...);

if (result == NULL) { ... }

If, later on, you change GetResult to return an int, there’s no compiler error – the statement is still valid, although it still looks like it should be a pointer.

C++11 changes this by introducing nullptr, which is an actual pointer type. Now, you type:

auto result = GetResult(...);

if (result == nullptr) { ... }

Now you know that GetResult can only return a pointer. And if someone changes it on you, then they’ll get a compile error if they don’t also change the if statement on you. The introduction of nullptr also means that it’s safer to overload a method on an integral type and a pointer type. For example:

void SetValue(int i);     // (1)
void SetValue(Widget* w); // (2)
... 
SetValue(5);    // Calls 1 
SetValue(NULL); // Also calls 1 

Because NULL isn’t a pointer, the second call to SetValue calls the version that takes an integer parameter. We can now call the second SetValue properly by using nullptr:

SetValue(nullptr);  // Calls 2

This is why it’s usually considered dangerous to overload a function or method based on an integer parameter and a pointer (to anything). It’s safer now, but still frowned upon.

Default Initialization

Considering the following class:

class Foo {
  Foo() : fooString(nullptr) { ... }
  Foo(const char* str) : fooString(nullptr) { ... }
  Foo(const Foo&amp; other) : fooString(nullptr) { ... }
  ...
  private:
    char* fooString;
};

We’ve initialized all the variable with nullptr, which is good. If another member variable is added to this class we now have to add three more initializations to the constructors. If your class has several variables, you have to add initializers for all of them in all the constructors. C++11 gives you the option to initialize variables inline with the declaration.

... 
private:
  char* fooString = nullptr;

With C++11, we can specify a default initial value – we can still override this in each constructor if we need to, but, if we don’t, it doesn’t matter how many constructors we add, we only need to set the value in one place. If we’ve separated our class out in to a ‘.h’ file and a ‘.cpp’ file, then an added benefit is that we only have to open the ‘.h’ file to add and initialize a variable.

Scoping Your Enums

One of the things that C++ tries to do is allow the programmer to encapsulate things so that, for example, when you name a variable, you’re not accidentally naming your variable the same as something else with the same name. C++ gives you tools to allow you to do this, such as namespaces and classes. The lowly enum, however, leaks its entries into the surrounding scope:


enum Colour {
  white,
  blue,
  yellow
};
// Doesn't compile.  There's already something in this scope with the name 'white'
auto white = 5;  

The variable ‘white’ can’t be defined because ‘white’ is part of an enum, and that enum leaks it’s entries into the surrounding scope. C++11 introduces scoped enums which allow a couple of things that C++98 didn’t allow. The first, as the name implies, is that the enums are fully scoped. The way to create a scoped enum is with the use of the ‘class’ keyword:


enum class Colour {
  white,
  blue,
  yellow
};

auto white = 5; // This now works.

Colour c = white; // Error, nothing in this scope has been defined called white.
Colour c = Colour::white; // Correct.

By default, scoped enums have an underlying type: int, so whatever size an int is on your platform, that’s the size that sizeof will return for your enum. Before C++11, unscoped enums also had an underlying type, but the compiler tried to be smart about it, so it would determine what the underlying type was and not tell us – it could optimize it for size and create an underlying type that was the smallest that could fit the number of entries. Or it could optimize for speed and create a type that was the fastest for the platform. All this means is that the compiler knew what type an enum was, but we didn’t, so we couldn’t forward declare the enum in a different file. For example,


file1.h:

enum Colour {
  white,
  blue,
  yellow
};

file2.h:

enum Colour; // Error, in this file, the compiler doesn't know what the type of Colour is.

void SomeFunction(Colour c);

The only way is to #include header1.h in header2.h. For small projects, this is fine, but in bigger projects, adding an entry to the Colour enum will mean a recompilation of anything that includes header1.h or header2.h. Scoped enums have a default size: int, so the compiler always knows what size they are. And, you can change the size if you wish:


file1.h:

enum class Colour: std::int8_t {
  white,
  blue,
  yellow
};

file2.h:

enum class Colour: std::int8_t;

void SomeFunction(Colour c);

Now any file that includes file2.h doesn’t necessarily have to be recompiled (file2.cpp will have to, because you’ll have to #include file1.h in it in order get it to compile properly.)

In Conclusion

You do have choice when moving forward programming your microcontroller. There’s no reason to use any of these if you don’t want to. I’ve found, however, that they help clean up my code and help me catch bugs at compile time rather than at run time. Many are just syntactic sugar, and you may not like the change. Some are just niceties that make working in a complicated language a bit nicer. What’s holding you back from trying them out?

99 thoughts on “Using Modern C++ Techniques With Arduino

        1. I agree. But I am a software developer by trade. So I pay for their all products pack yearly and get clion, idea, you gogsland, all things I use on the daily. PlatformIO has good integration with atom. But I feel that program is slow and buggy

          1. I used PHP Storm, which include a lot of the features for other languages. Even though I’m not a “pro” developer anymore, I still use it, because it works and works very well and will pay for it, since it blows Netbeans out of the water. I do have it tuned to use the Netbeans short cuts (since I used NB for a long time).

          1. Does not work. Downloaded, did chmod +x, tried to run but I keep getting “run-detectors: unable to find an interpreter for ./vs_Community.exe”. I don’t expect Microsoft is releasing such crappy software so perhaps some other thing is wrong?

    1. While you can do crazy complex unreadable things in C++11 (template meta programming is one of those). Which are hard to debug when they break.

      But it is an extremely useful language, and way less error prone then C in many ways.

      C++11 adds so many pretty useful things, like lambas, the whole library, finally standardized threading support. But it’s not a language for anyone, as it can bite you in the ass in unexpected ways.

      Nothing beats the portability, speed and size of C++ as far as I know.

      (I do 90% of my professional work in python, rest is C++. 90% of my hobby stuff in C++)

        1. you don’t have to read an anti-c++ gospel. just don’t understand the whole instance and inheritance stuff, combined with the horrendous way people write C++ libraries and code and you can count me out. I really tried to learn C++, but I get confused very quickly. Just accept that a lot of people are not that fond of the oo type of programming.

          1. I’m able to learn a lot of things from books, but I was not able to learn OO thinking from a book. I didn’t really grok it until I took a class.

            Now, it comes very naturally to me, and I consider it well worthwhile.

          2. im glad i learned c++ early before all the java/dot net fanbois put a seed of doubt in the minds of would be c++ programmers. if you dont want to throw most of your performance away to a vm, you are going to have to put more thought into your code.

          3. OO is such a natural fit for EE’s that it only took me a single glance at an image in a MSJ magazine to get it. I haven’t looked back since.

            And by using templates I can often make my embedded code compile smaller than the canonical C code.

          4. You can use C++ without using object-oriented programming. In fact, most of the features mentioned in the article are not specifically for OOP stuff. But some very basic OOP is great without being even slightly complicated (e.g. classes with no inheritance).

    2. Maybe you should learn it before knocking it. If you had you’ll not be making that comment. C++ helps to improve code cohesion and helps to maintain an understandable and manageable coupling between related objects.

      1. If used well it can do that. Otoh when language features are used for their own sake, C++ can make uniquely horrific messes. But it’s not the fault of the language when that happens, that’s just the tail wagging the dog. My point being that it doesn’t *automatically* confer those benefits once you’ve mastered the mechanics of the language, there also needs to be an awareness of how to do good OO design to achieve those ends.

        1. I really think it’s your own responsibility to answer those questions by reading a primer on the subject before diving into a conversation on the pros and cons of an object-oriented language.

        2. An object is a variable type, made out of multiple variables grouped together, and functions that operate on it.
          Example:

          struct position2d {
          float x;
          float y;

          void scale(float factor) {
          x = x * factor;
          y = y * factor;
          }
          };

          Generally, by looking at which object contain which other objects, you can get a pretty good idea of the general layout of a program. And it makes sense in practice to group functions that modify your object together – you don’t HAVE to, in theory, but it gives a pretty good overall code organization overall.

  1. “With C++11, we can specify a default initial value – we can still override this in each constructor if we need to, but, if we don’t, it doesn’t matter how many constructors we add, we only need to set the value in one place.”

    I’m curious how this is actually implemented by compilers – if you’ve got a global class (like, say, the Serial object in Arduino), the constructor gets called at the beginning of the program and everything gets initialized in the constructor. This is space inefficient, but even with space optimization turned all the way up, on certain compilers (the GCC port to MSP430, for instance) they’ll still do it this way.

    If, instead, you have a struct, and initialize the struct, those values get shoved in .data, and the runtime can be smart and store it in a compressed manner and efficiently load it at startup. So it’d be interesting if C++11 treats these two cases differently.

    1. Usually it’s implemented by putting all objects that required none “0” initialization in 1 area. All other objects that require anything else as basic initial values go into another group that is copied as big block. (difference between .bss and .data)
      This has been normal even in plain C.

      Anything that is more complex then static data initialization is just called as constructors. And I do not know how well different compilers handling constructor initialization that is actually just static data.
      If you want to make a 100% sure it’s just static quick copy initialization, you’ll most likely have to make sure your data is POD http://en.cppreference.com/w/cpp/concept/PODType

  2. Throwing my $0.02 in here: by far my favorite C++11 feature is the “auto” keyword, primarily for use with the STL. The examples given mention iterators but don’t really show it off. Imagine some complicated data type…

    std::map<std::string,std::map<int,vector>> datatype;

    Looping used to go like this
    for (std::map<std::string,std::map<int,vector>>::iterator it = datatype.begin(); it != datatype.end; it++)

    Chuck all that out and go with
    for (auto it = datatype.begin(); it != datatype.end(); it ++)

    1. The idea of doing this on an 8-bit microcontroller where you need to account for every byte of memory and every cycle of operation is comical.

      The user of those templates will have no idea what’s going on, and no way to solve the problem when one line of code uses the entirety of program storage.

      You can do great things with C++11, but what most people do with it is make the developer’s life easier by freeing them from the need to understand what’s actually executing at the expense of all else.

      The complicated datatype might look bad. But it’s a million times better than ‘auto’. At least you don’t need an IDE to figure out what interfaces your reference might have, and you have a shot at understanding what’s going on.

  3. C++11 (& C++14 & C++17 e.t.c) have been adding some really useful stuff to the language like smart pointers lambdas and all of this other great stuff. The problem with the C++ language though is it was already quite complicated and feature rich to start with. Adding more features, even if they’re useful, makes it quite an overwhelming language to learn and practice, especially for beginners. I didn’t see this in the past (as I’ve been using C++ for a long time) but i think i see it now. It makes sense to me why programmers in general prefer to program in Java, C, Python, Javascript, Go e.t.c than C++. The simplicity of the language itself and its constructs in some cases is very important.

    Still like C++ though!

    1. Also, in an education environment, access to newer language features can be hit or miss (mostly miss). EG: at my school all programs must compile and run on the old Redhat (6?) machine Empress the department provides. So we’re limited to the libraries and compiler version available on the testing server. On the other hand in software engineering we got to choose the environment and no sane individual keeps an out-of-date JVM installed so we all just standardized on Java and Eclipse as a development platform (and got access to JDBC and standard GUI APIs with it).

    2. Because it’s there doesn’t mean you _have_ to use it. Use the features that make your code more readable and perform as you wish and skip the rest.

      Arguing that there’s too many features in the language is what’s been keeping C++ stagnant for a decade, and is almost what killed it. To remove all thos old hacks (aka “features”) they have to add new and improved methods befor they can deprecate and improve the language gradually by providing means of using better syntax and expressions.

  4. Why would anyone use C++ …. for anything? Don’t forget this is the octopus created by nailing legs on a dog.
    And while we are throwing stones, why an IDE for that matter?
    All these are crutches for the incompetent.

    1. Personally I find C++ to be good for situations where (1) you have a problem that fits neatly into an object heirarchy and (2) it needs to be fast without a lot of effort. For example, implementing collision detection for a physics engine with a large set of object types.

      Sure, you could implement these types of things in C if you really wanted to, but for my particular niches it seems to me that modern C++ results in a more concise design that’s easier to maintain. Of course, I always try not to use any more of the “octopus legs” than I absolutely have to, for the sake of maintainability.

      As for C++ and IDEs being, “crutches for the incompetent,” some of us incompetents just need to get some work done sometimes. IDEs make my life a *lot* easier when trying to rescue a giant, sprawling mass of code that nobody has ever properly designed or maintained, and when I’m not getting paid by the hour, I don’t care who thinks my productivity tool is a crutch.

      1. “it needs to be fast without a lot of effort. For example, implementing collision detection for a physics engine with a large set of object types.”

        Okay, back to python and perl. They do what I need without all the BS. Thanks for playing.
        I don’t do physics engines, I’ve no idea what one of those is, not do I care.
        I do simple stuff like pulling messages off message queues, processing said messages and dispatching appropriately.
        Can C++ help me with that? If yes, tell me now. If not, I’ll go away and not waste more of anyone’s time.

        Make me want to try the language before before pissing me off with all the OO shit and throwing in jargon like ‘physics engines,” fer chrissakes.

        I’ve also noticed that the diagnostic messages from various programs written in C++ are just as, or more incomprehensible than those written in Java. I tip my hat to those sadistic pricks!

        1. You sound very dismissive of C++.

          Remember that everything in C++ is there for a reason. They didn’t add features to C just because they were bored; they did it because there were things real-life programmers wanted to do and the new features made it possible. By rejecting C++, you’re missing out on those genuinely useful capabilities.

          Remember also that C++ is a successful and widely-used language. It’s not as if we’re trying to sell you on Ada here. People are using it because it’s good at solving real-world problems. If it’s not well suited to YOUR current applications (as your reference to Python and Perl would suggest), that’s fine. But it would make no sense to angrily dismiss it as a possibility for future applications you might have.

          I’m puzzled by your reference to “OO shit”. Object oriented programming is the norm these days. You mention Python and Perl. Those support OOP. If you’re going to diss C++ for being OO, why would you let Python and Perl off the hook?

          And regarding Python and Perl: they’re great and useful languages. But, being interpreted, they’re not well-suited for microcontrollers, which IS the topic of this article.

          Finally, I no idea what “BS” you think C++ is burdening you with. It’s easy to hurl unsupported insults – even presidents can do it. Care to back it up with specifics?

      1. I don’t think that there is an ultimate programming language that is better than all the rest…or a ‘worst’ programming language (maybe php or javascript ? just kidding :) ). Every language has pros and cons and are usually good at solving a set of particular applications. It’d be great if there would be a ‘one programming language to rule them all’. But there isn’t.

        As far as IDEs go, I think that every programmer should at least be familiar with make, autotools, and CMake. Beyond that whether they build their ‘own’ build setup via these tools + code editor or go with a full fledged IDE is up to them….whatever works for them.

    2. Gosh, I feel the opposite. Why would anyone use C when they can use C++? C++ can do everything C can do, just as efficiently, but it gives you all these great tools for organizing your code.

      I don’t see any downside to choosing C++ over C.

        1. That is not necessarily true. But here’s the thing: the way C++ is advocated to be used here is not actually C++ but C with a little bit of added sugar hence the similarity to C in the generated binaries. As soon as you start using the original C++ functionality the real ugliness rears its head and will bite you in the ass often and hard…

          NB: NULL is not 0 but zero typecast to the native pointer size, i.e. 0L or 0LL. Anyone using 0 and NULL interchangeably deserves to experience excruciating pain and any halfway decent compiler will warn about this, exactly like with nullptr.

          1. What in the hell are you talking about? Do you even code?
            Your first paragraph is just plain fear mongering about nothing. Original C++ functionality? You mean C? Unless you have made some error that generates many templates or piled in a bunch extraneous code that should have been stripped there is literally no ugliness to worry about.

            NULL is a win32 relic implemented as a macro. It might be implemented in some other libraries or tool chains too, I don’t know. It is actually advisable to not use it. In any case it evaluates to 0 and no compiler I have ever used warns or mishandles the use of 0.
            Now their is nullptr so it is a null issue.

    3. IDEs are crutches for the incompetent, just like GUIs and mice are crutches for people incompetent with the command line. You more than make up in snobby elitism for all the insight you lack.

    4. There are 3 major groups of languages, speed-wise:
      (1) Fast: C/C++, Assembly, Fortran, Objective-C, Delphi…
      (2) Medium (garbage collected): C#, Java, Go…
      (3) Slow (dynamic typed): Perl, Python, Javascript, Lua, Ruby, PHP…

      Dynamic typed languages (group 3) can never ever catch up to static typed (groups 1 & 2) in speed: even on something as simple as an addition (+), the engine can’t just add the two numbers because it doesn’t KNOW that they’re numbers, it has to check. Sure, something like the Javascript V8 engine does an admirable job at caching types, but there’s a hard limit to how far that will take you.

      Garbage collected languages (group 2) are relatively close in speed to languages with manual memory management (group 1) and the difference is not as much in average speed as in the maximum amount of time that doing something will take (and in startup speed). This is A-OK for server work (which is why C# and Java are popular there), but the cost is that when running something like a Java program, the garbage collector can trigger at any time and lock up every thread for upwards of 100ms and there’s literally nothing you can do. Large data sets seem to compound this problem.

      C++ is popular simply because it’s still the nicest, most convenient group-1 language even after all those years, and the other group-1 alternatives aren’t really any better. I’m sure there are nice languages that don’t have the somewhat hectic history of C++ in group 2 and 3, but they’re not competing against C++, because you typically can’t really use those languages on projects that require a group-1 language.

  5. Smart pointers are another big thing. They’re (i think?) a library feature meant to give the flexible ownership of Java without any actual garbage collection through a class that does it’s own reference counting.

    Also a lot of the more useful things from the “boost” libraries have gradually been migrating into standard libraries. But I don’t expect complicated data structures and file IO improvements to fit onto a microcontroller.

      1. Why on earth would you do that? Always use make_shared directly, otherwise you’ll be easily leaking memory (RAII). And use shared_ptrs correctly.
        Don’t use std::enable_shared_from_this unless you really know what you’re doing. Then think it over and design your class so you don’t need it.

        #include
        #include
        using namespace std;

        class A
        {
        public:
        A(int i) : m_i(i) {}
        int geti() { return m_i; }

        private:
        int m_i;
        };

        int main() {
        auto a = std::make_shared(123);
        auto b = a;
        std::cout <
        geti() << std::endl;
        std::cout <geti() << std::endl;
        b.reset();
        std::cout <
        geti() << std::endl; // This works, a is still holding the object
        //std::cout <geti() << std::endl; // This will crash, b is a nullptr now
        return 0;
        }

        IdeOne: http://ideone.com/GHvxx1

      2. Bah, they need to move the report button elsewhere. I swear, I hit it every time I try to reply.

        I’m staring at your code snippet, and I’m just not seeing it. Should work just fine. enable_shared_from_this is intended to enable (std::shared_ptr x = std::make_shared(this)). If that’s not what you’re using it for, then you’re probably doing something wrong.

        I also see most assignments of null (or calls to reset(), which is better IMO) to a shared pointer to be poor style. These are most useful and sensible if you let them destroy themselves when they go out of scope.

        If you’re calling test() on a null pointer, it damn well *should* crash.

        1. HaD completely eat my formatting of that code… there was a lot more code then you can actually see… test was called on the “c” pointer.

          The whole problem I have with the shared pointer, is that there is no simple way to guarantee that the user of your class only creates it in the intended way. Yes, you can do a factory, but the whole reason for these new features is writing less code, not more.

          1. You can’t stop a user from misusing your class. Just create useful documentation.
            While I sometimes return smart pointers to handle resources, I think it too is probably a poor design decision.

            I think the best practice is just give raw pointers/references and document it and let the user worry about handling it. The tools all exist to move it to a smart pointer.

            As stated above let user of the class worry about lifetime, because the proper way in this era is to let fall out of scope. In some cases especially in dealing in hardware or OS resources it’s probably best to not assume or try to control anything about lifetime because your class may be but a small part in a much larger machine.

            TL;DR of it all is while smart pointers are incredibly useful they can also be a hindrance especially for class and framework creators.

  6. The only things that look useful out of all of those features are the ones relating to enums. Being able to specify the size of enums and have their members better scoped would be a welcome addition to C.

    The rest of the features look like they’re just one more way to do the same old crap. Because, you know, C++ doesn’t already have too many ways to do any given task, it needs one more.

    1. The int sizes where always there, they are just now standard, and thus guaranteed. And one of the best things for memory constrained code like AVR.

      auto is one of the best things ever. It saves a lot of typedefs, and just allows you to write working code faster.

      It’s a shame they didn’t mention constexpr. Which allows you better to ensure that things are optimized at compile time.

      Good thing they didn’t mention variadic templates.

      Yes, all this, you could do without. You could do with just butterflies (Insert XKCD). But the whole thing is doing it more efficient, faster, in a standardized way.

  7. Don’t forget about the natural ally of the “auto” type: range-based “for” loops!

    Instead of having to type out:
    for (SomeArray::iterator iter = SomeArray.begin(); iter != SomeArray.end(); iter++)
    {
    doFunction(*iter);
    }

    we can eliminate a lot of boilerplate and instead write this:
    for (auto& arrayValue : SomeArray)
    {
    doFunction(arrayValue);
    }

  8. Awesome. Programmed c++ heavily about a decade ago and lovely to see the ‘auto’ declaration and other new features. For iterators on vectors it’s indeed way better! And yeah for anything with serious data where you got vectors,maps, graphs or trees of +1 million entries that need to processed fast, c++ is the way to go. If done right, it can crush python and java with regards to cpu and memory usage. But indeed the price you pay is it also needs only one bad coder to ruin the whole project and cause a segfault or coredump;). Even with smart pointers I’ve seen it happen, somebody just decides to call delete somewhere and a week or so later that change causes a random segfault and you’ll be gdb’ing + valgrinding for hours to find it ;)

    1. I do hear that argument about C++ vs. Java often, but seriously: If you’re not proficient coding in a language your code is going to suck no matter what… Also ironically “java.lang.NullPointerException” is the most common Java error. Go figure…

      1. This.

        A lot of people blame the language; far more often, the correct answer is to blame the programmer. Unless you’re a secretary writing a macro in an excel spreadsheet… that should be fairly foolproof. :)

      2. Ever debugged an application where a buffer was 1 byte too small and would override 1 byte of the return pointer. Ruining your stacktrace.

        Or, usage of uninitialized pointer, which, due to how the application was allocating memory, was a valid pointer somewhere into memory, overriding other data, and not providing the proper data.

        All in the line of things I’ve debugged. Yes, code can suck anywhere, mistakes are made anywhere, C++ just makes you pay harder for it when you do.

        1. All those example of bugs are common in C too. If anything, C++ provides tools for avoiding them. E.g. using a string class will ensure your string buffer is never 1 byte too short. Using smart pointers will ensure you never try to access freed memory.

    1. There’s no limitations in the language regarding integer length (unfortunately), so it is completly upt to the compiler to decidede wheter a short should be 16 or 12 bits long.

      1. Huh, I didn’t know about that. Ya got me.

        Well, there must be another reason C++ is a crappy language. Just give me a minute to think one up…

        (Seriously, though, thanks. I’ve learned something new today.)

    2. While we are talking about other interesting parts of stdint, it bears mentioning the int_fastN_t which gives you the fastest type that is at least as many bits as you requested. This may or may not be the smallest possible size because some processors don’t have good ways of addressing sub word sized variables.

      Also, if you are using pointers, make sure that you store your pointer math in a ptrdiff_t sized variable. It isn’t always obvious how big of a variable that you need to store pointer math.

    1. I always put premium in the lawn mower. I find the ethanol in low grade fuel attacks the carb seals. I’d rather pay an extra couple cents for fuel than replace seals every couple years.

  9. “The following are some techniques using new features of C++ that don’t add memory overhead, reduce speed, or increase size”

    What I’d like to know is what DOES add overhead. Try..catch, I assume? when does constructors do? using the heap is always explicit, right (except in weird implementations of libraries)?

    Also, totally unrelated but how (not why) do you cleanly use inline assembly in C or C++? gcc have always been telling me I’m overwriting registers (because I am).

    1. Most of the std:: library uses the heap. std::array might not, as it’s a wrapper around a static array. But most of the library is just not designed to be used on a super memory constrained setup like the AVR.

      The overhead of having a try{}catch block without an exception throw is low. And could even be zero in recent compilers.

      Constructors are just functions. The compiler can sometimes optimize them away.

      But the reality is, you don’t always know what the compiler will do unless you look at the final listing. So use avr-objdump to generate an assembly listing, and inspect that. Compilers can do surprising things. For example 8 bit shift on a 16 bit integer should be a simple “pick the proper register”, instead it does all kinds of complex shifts.

      1. Yes, much of the std library uses the heap. But if you need a dynamic array or linked list in your embedded application, you’ll be using the heap whether you’re writing in C++ or C. So you can roll your own or use an implementation (the std library) that has been QAed, optimized, and tested in the real world for millions of hours.

      1. I find that to be quite untrue. The only overhead is memory/character accounting. Mostly void pointers are passed around. The only possible show stopper on low memory devices is in later standards std::string memory has to be contiguous.
        On slow memory devices you aren’t using stl anything. All of them do a lot of reallocating.

        I won’t disagree that using stl containers on most micro-controllers will end in cursing or hair pulling out, but it isn’t related to overhead.

        Heap use or resizing as it were can be controlled if I remember right (ex. instead of doubling which is standard you can set it to a percentage.). I could just be making this all up though, but I could have sworn I changed this before.

    2. RTTI for dynamic typing, I think, adds some amount of overhead behind the scenes. This is the case where you have:

      class a,
      class b inherited from a,

      a function() {
      return new b;
      }

      // and finally in your main code
      class_a instanceVar = function();

      Now you have a variable “instanceVar” which is base class_a type, but really is a fully formed class_b. If you later call dynamic_cast(instanceVar), then AT RUNTIME the true type of instanceVar is checked and this will succeed or fail depending on what instanceVar actually is. The purpose is trying to dynamic_cast, and then if successful, using some method or property of class_b that isn’t present in a class_a.

      I’m probably really butchering the explanation here… the gist is, at RUN-TIME, trying to “cast” a more generic object into a more specific version. It’s for cases where you can’t know this at compile-time whether everything will successfully cast, so instead the objects carry additional info about real-types behind the scenes for when you call dynamic_cast on them.

      …Here, just read the Wikipedia article.
      https://en.wikipedia.org/wiki/Run-time_type_information

      1. Yes, RTTI takes space. I believe it requires an extra word per class (NOT an extra word per object, because all objects of the same class point to the same instance of the class’s virtual table), If you were trying to do the same thing in C, you’d use at least the same amount of memory, probably more (because most people would use one word per instance of the struct instead of one word per type of struct).

        And this brings up an important point about C++: the issue is NOT whether a feature uses time and/or space. The issue is whether it uses more time and/or space than the equivalent functionality implemented in C would. And it almost never does.

          1. RTTI only works for classes that have virtual functions. So your object already has a pointer to the virtual table, and RTTI increases the size of that table by one word. So the incremental memory usage of RTTI is one word per class.

            To be fair, if you turn on RTTI in the compiler, it adds that one word to EVERY virtual table, whether you’re planning to use RTTI on that class or not.

    3. “Also, totally unrelated but how (not why) do you cleanly use inline assembly in C or C++? gcc have always been telling me I’m overwriting registers (because I am).”

      Don’t.

  10. I do most of my embedded programming in C and assembler. I did many years of C++ programming in the early (pre-stl) days, and find well-written C (or even asm) easier to understand and maintain than most C++. And I still have a bad taste in my mouth from the mess you get with C++ when people try to use const references without fully considering which methods need to be const…
    What surprises me the most is that after decades of compiler improvements, I can still write better optimized asm code (at least for AVR) than the compiler can generate.

    1. Right. No one would ever fight about Star Wars vs. Star Trek. Or Yankees vs. Red Sox. Or Brad vs. Angelina. Or (and I’m dating myself here) Sean Connery vs. Roger Moore.

Leave a Reply to bimoverbohmCancel reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

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