C++ template metaprogramming for AVR microcontrollers

October 19, 2016

father_mckenzie-October 19, 2016

The AVR
The AVR 8-bit microcontroller's modified Harvard architecture was introduced in 1997. These Atmel microcontrollers are popular among a reasonably large number of developers because they provide decent performance coupled with low power consumption. It's also fair to say that a large part of the AVR's popularity comes from the fact that these microcontrollers are used in many Arduino systems.

The Arduino
Introduced in 2005, the Arduino is an open-source prototyping platform that was originally targeted at beginners, students, and non-engineers (including artists). The Arduino soon found itself at the forefront of the flourishing Maker Movement. The Arduino environment has evolved since its inception, and there now exist a wide variety of Arduino boards and supplementary devices. A simple-to-learn subset of the C programming language -- coupled with a diversity of libraries written by enthusiasts from all over the world -- make it possible to create Arduino applications for virtually any problem solution. This facilitates the ability of both beginners and professionals to use the Arduino to check ideas and create new device prototypes.

Having said all this, it is unlikely that one would use Arduino software for real, commercial projects. The main reason for this is the inefficiency of the resulting code[8]. The simplicity and universalism of the Arduino tools limits their ability to take full advantage of the AVR microcontroller’s potential, its performance, and inherent parallelism.

Software development approaches
For the purposes of these discussions, software development approaches may be considered to fall into two main camps: old school and new school.

Old School: These programmers are true hardware and software experts. Their tools are assembly language and the C programming language. Their main goal is to squeeze everything they can out of every byte -- to achieve ultimate code performance while minimizing power and memory consumption. However, code written by these programmers is not always easy to understand, which can make ongoing development and maintenance difficult.

New School: People brought up in the era of C++ and objects tend to see objects in everything. Classes are an excellent example of code reuse. The use of classes encourages developers to achieve better code structure and well-thought-out assignments of responsibilities among components. Properly written object-oriented code is easy to understand and support. However, using the object-oriented capabilities of C++ comes at a cost. One oft-cited drawback of using C++ is it’s perceived lack of efficiency. The automatic generation of class methods and the implicit creation of temporary objects can lead to a substantial reduction in code performance[7]. As a result, developing efficient C++ code is something of an art.

C++ templates
One of the strengths of C++ is its templates mechanism. The main idea is the ability to create a generalized definition of code behavior without explicitly specifying the entities to use. An obvious example of template usage is the standard templates library (STL), which presents three main types of entities: containers, algorithms, and iterators.

Generalized containers can be specified with required data types at the point of use. Algorithms know nothing about containers, and cooperation between algorithms and containers is achieved via the iterators mechanism. This allows the STL to demonstrate an amazing flexibility and to provide solutions for an unlimited number of practical problems.

An obvious advantage of a class templates is the fact that the compiler only instantiates those class members that are actually used in the code, while any remaining code receives only a syntax check. This eliminates unused code and reduces program memory consumption. The specialization mechanism allows fine-tuning of the code's behavior depending on template parameters and provides an excellent opportunity for code optimization. At the same time, the templates syntax is not particularly convenient and the compiler is not friendly to the template code. This makes development quite difficult and is probably the main disadvantage of using templates.

The easiest way to illustrate the concept of templates is by means of an example. Let's suppose we wish to create a min() function for use with integer values. An obvious solution for a C programmer would be to implement something like the following:

   ​int min (int a, int b)

   {

       return (a < b) ? a : b;

   }

 

If a similar capability is required for use with floating-point values, then an additional function needs to be defined as follows:

   ​float min (float a, float b)

   ​{

       return (a < b) ? a : b;

   }

 

That is, for every data type for which we wish to perform this task, we need to create a unique function. By comparison, the solution for a C++ programmer can be as simple as follows:

   ​template<typename T>

   T min (T a, T b)

   ​{

       return (a < b) ? a : b;

   }

 

In this case, the types of the values are not explicitly specified; instead, we use T notation that appears in the template definition with the typename keyword. For function (and class method) templates, the compiler is able to deduce the required parameter type based on the types provided by the user.

Not unreasonably, the compiler will report a problem if such a function is called with a pair of different data types. If this is done intentionally, however, then the solution is simple: all that is necessary is to explicitly specify the required type during the function call as illustrated below:

   float float_variable = 3.141;

   int   integer_variable = 3;

   int   result = min<int>(float_variable, integer_variable);

 

Or, depending on the programmer's requirement:

float result = min<float>(float_variable, integer_variable);

 

 

When instantiated in this way, the function can work with arbitrary types of data, the only requirement is the '<' (less than) operation defined for the type used. Such behavior is very similar to dynamically typed languages; however, there is a fundamental difference. For languages like Python, a single instance of the function is sufficient. As a statically typed language, C++ requires a distinct instance for each type with which the function is used. Here, we completely rely on the compiler to perform all that necessary work for us. However, although this is convenient, this is also the sort of thing that can lead to code bloat.

Of course, this particular function is an obvious candidate for inlining due to its small size, so this wouldn’t be a problem. However, a bunch of differently parameterized class template instances having bulky methods can lead a significant increase in the size of the code. Todd Veldhuizen gives recommendations that can help avoid this[5].

Template metaprogramming
In 1994, during a meeting of the C++ standardization committee, Erwin Unruh first demonstrated an ability of templates to execute computations at compile time. The code he presented produced a series of diagnostic messages containing prime numbers. As further investigations revealed, this ability to perform compile-time calculations has computational completeness[6]; indeed, it is possible to perform arithmetic operations, create loops via recursion, and branching via specializations.

Also noticed was a similarity between templates and conventional runtime functions[3]. Template parameters are similar to ordinary function parameters, while nested types and enumeration constants mimic return values. The very same entities used as template parameters can be used as metafunction parameters and returning values as follows:

  • Constant values of integral and enumerated types

  • Types

  • Pointers

Continue to page 2 >>

 

< Previous
Page 1 of 4
Next >

Loading comments...