C++ tips and tricks

Preface

So, what is point of this? I am CS student at University in Czech Republic, and as a hobby of mine I am willing to spent a lot of time on "coding theory". After spending huge ammount of time on this topic, I came into a land of C++, simultaneously with the urge to study and develop skillset to code programms that ... works and are bugfree. Well, as anybody who study this knows, any code that is complex enough to do something interesting is not bug free, but hey, we can approach it.

Whily my primary point of interesting are algorithms in robotics, my secondary focus is 'coding skills'. I've come to some beliefs about coding safe programs, that eventually crystalized into a set of rules I follow in my code. This document/wiki should be an overview of those and especially discussion about them. (And yeah, I have to note this because I start to forget why I do some things in first place and that is no go)

General ideas

This section covers general principles and notes

  • In my opinion is something that you should assert through this entire text. Also, I personally believe that this does not have to work for everyone, so this is not "the next way of coding that everybody should use".
  • I do belive that limiting the feature set that one can use is the way to go.
  • I do belive that solving problems during compilation with 'reasonable' tools is the way to go.

RAII

http://en.cppreference.com/w/cpp/language/raii

Principle used in C++, this binds a resource that has to be taken a care of, to a lifetime of object. Constructor of object handles acquistion of resource, methods handle safe access to the resource and destructor handles release of the resource.

This makes it possible to create a lot of mechanisms (noted later) that are safe to handle otherwise 'dangerous' things. Objects have pretty well defined lifetime and all mechanisms that affect it, so this is easy and intuitive to use I believe.

Object invariant

The idea is that each object has defined invariant - something that holds in all states the object can get in. The invariant should be usefull for the task that object should do, so invariant "at all times object is object" is not actually...

Let's consider object that contains data and should provide you with data. The invariant is simple: "Object X always provides valid data."

Wait, that is easy? well, depends.

Let's say you want to optimize that data container, you stop storing all information, becase you realize some of it can be calculated at runtime. Also, the calculation of some informations is costly, you introduce cache for the calculated data.

Invariant "Object X always provides valid data." No gets interesting. As in each public method, constructor and destructor, you now have to make sure that the invariant is not breached. Each method has to take the cache into account and correctly calculate information that should be calculated.

Some invariants are easy to verify at compile time, for example Monitors. In same cases you simply 'grep' the source code and make sure that first line of each method in monitor contains 'std::lock_guard'. In other cases you have to write a compiler plugin that verifies that invariant based on parsed code.

In case of cache + calculated data mentioned above, that is impossible in same cases. More attention should be put at those cases and other techniques should be used: Code review, full branch testing, random data testing. Cases like this should however be avoided.

Pick two

Well, in coding I believe you can have three things:

  • Code is safe
  • Code is fast
  • Code is easy and fast to write

But you can only have two, focus of this text is on having the first two things. So yeah, a lot of techniques and stuff is complex, time consuming, so this kind of criticizm is kind of pointless. However, pointlessly time consuming techniques should not be used.

Safety measures

Here is a collection of techniques in C++ for decreasing a chance of error or bug. This section provides rules to follow that decreases the chance to minimum.

Carefully with enforcing

As one friend said with IT security - First rule of security is to not to use security measures that would annoy the users.

Same goes with code safety, do not use techniques for code safety that would annoy your programmators. They than avoid using that techniques or bypass them (they can be quite creative with that).

My personal approach to that, is that all security techniques I like and describe, I do not enforce them during the entire development process. All of them have to be enforce only at the end of development cycle. As sometimes they can be tedious. You prototype feature, and once you are happy with it, you make sure all standarts are met.

This can be reasonably done, as a lot of the rules I propose can be verified with tools - only use those tools at the end of developer phase.

Also, I believe that for each rule, there may be situation where it is more reasonable to disable the rule-check for specific part of code (which also can be done in automatized way).

Foolproof API

My personal favourite is to design API of all things present in code, so they can't be used in a wrong way, or that makes it impossible to do certain sense of errors.

General problem are techniques "you should verify that property X holds before you use the function Y". That mechanism is doomed to fall.

In modern languages genreally, the way to avoid this are callbacks. Let's say you have resource that is optional and object that stores it.

Instead, of giving user a API that provides him the resource, rather create a API that takes callback and executes it in case there is resource. The method to do so is literally as simple as: void foo(Callback c){ if(resource) c(*resource);} This way, the condition 'if resource is present' is always present and it can't be misused.

Avoid direct memory manipulation

C++ has enough 'constructs' for memory manipulation, that you do not have to use 'free', 'malloc', 'new', 'delete. These are dangerous by definition, mainly because nothing controlls or checks that 'free' or 'delete' are called in all execution paths from the call of 'new' or 'malloc'. That is dangerous and notorious source of mistakes with memory management. Also a pointless one in C++ as there are various techniques and ways to makes this work - RAII.

Techniques: * std::unique_ptr/std::shared_ptr - I actually do not find them perfect, more about that later. * std::vector/std::list/std::map - And other data containers that are dynamic-sized in nature (hence require work with memory)

On problem with std::unique_ptr and std::shared_ptr

Well, I found one problem with these two kind of pointers, they are not as safe as I believe they should be. The desing correctly approaches management of the memory, and I believe that with actuall implementation in compilers there should not be errors. The memory is simply freed everytime the objects are destroyed.

Problem is with accessing the pointer stored by these objects. As the access

Advanced things

There may be cases where the tools provided by the language are not enough, what then? Simply create your own safe mchanism to handle what you need.

Idea is to isolate the dangerous mechnics into a struct/class that takes care only of that problem. Define a object invariant that represents that mechanics. Than:

  • With assertion that object holds that invariant, create an API that would imply that whenver your object is used, there can't be a way to use it incorrectly.
  • Use other means to verify that your object does hold it's invariant. (State model checking, klee, multiple person review, tests, math. checking tools)

Always isolate dangerous things and create an API that makes it impossible to use them incorrectly.

Ideas

Units of measurement

With robotics I handle a lot of calculations concerned physics, these involve various units of measurement: https://en.wikipedia.org/wiki/Units_of_measurement. Usually, these are stored in float/double datatype in the appropiate data structures. Well, we can do better as I do not consider this safe enough.

Point is, that type system can store much more information than 'float'/'double'. I have two interesting solutions in my mind, one is implemented and used, second is in making:

Version 1

Class stores inner variable, of type double. This class is templated and template argument 'Tag' is provided. Classes with different Tag are considered different data types by the compiler. Operators are then overloaded in specific way:

  • C + C = C - addition is possible only between same tags and result has same tag again.
  • C - C = C - dtto
  • C / d = C - division by double results in same type tagged type
  • C * d = C - dtto
  • C / C = d - division by same tag, results in double.
  • abs(C), min(C), max(C) = C - various math. functions are overloaded to use this class, they return same type
  • sin(C), cos(C) = d - in cases where it makes sense, only double is returned.
  • ** cmp ** - full set o comparasion methods is defined
  • operator<< - is overloaded too.

Each operator is defined to do exactly same, what would happend if we would divide/sum/ the inner double value in the objects. This is intuitive. This however has defined specific data-type level behavior that provides some properties:

  • Classes with different tag can't be summed/added together or divided/multipled by each other.
  • If argument of function expects class with specific tag, no other tag can be used.
  • One can manually write operator overloads, between specific tags, like: C<A> / C<B> = C<D>.

What this all means together?

You define datatype for Distance, Velocity and Time - ` typedef Distance C; typedef Velocity C; typedef Time C;` Given the rules above, distance can be only added to a distance etc...

Now, if you would use these types instead of double in your calculatons, compiler can warn you about some errors: * You sum distance and velocity - that does not make sense and you are warned about it. * You use velocity where distance is expected - one of possible errors, does not make sense.

But that may not be as flexible as possible, because sometimes you want the different units of measurement to interact. In this case, only way is to manually write the operators:

  • Distance / Time = Velocity
  • Velocity * Time = Distance
  • Distance / Velocity = Time

While this is exhausting when you start your project, once you have the basic class and enough conversions between specific tags, you have increased a safety of your physical computations. As now, only math. operations between units where it makes sense is possible. Also, given that you specify what datatype should functions return, this is also enforced that data-type was actually computed.

This of course does not mean that the computation itself is correct, that is different topic, it just makes much harder to make erros in computations. Also, you won't simply be able to use distance where velocity is expected etc...

P.S: This does not introduce any slowdown in the code.

Version 1b

After some time, I've made a patch that allows specifing a ratio as template argument (unit prefix - kilo,milli,micro...) and mechanics for easy manual conversion. (Method for ratio conversion) I believe that automatic conversion would not be intuitive - with operators you expect that only instruction for operator is executed. Not conversion.

Version 2

Previous verison require manual definition of relations between tags. At first, we drop the ratios from the experiment.

For the second version, I want to allow multiple tags to exist in the template arguments. C<DistanceTag,DistanceTag> simply means m^2. Each tag that represents a unit of measurement as we know in all physics formulas.

With that, we can define multiplication operator, that sums the tags from the multiplied classes: C<A> * C<B,D> = C<A,B,D>. This corresponds to 2m * 3m^2 = 6m^3. In case of division, we subtract the tags: C<A,D> / C<A> = C<D>, corresponds to: 3 Nm / 1 m = 3N.

Problematic are cases with divison where the unit does not exists in the dividend itself. We can use templates to help with that!. Template MI<Tag> - Multiplicative Invers, is used as solution. ` TimeTag -> MI ` corresponds to `s -> s^-1`.

This way, in case the tag in divider is not present in divident, the multiplicative inverse is inserted into result type instead: C<A> / C<B> = C<A, MI<B>> corresponds to 2 m / 1 s = 2 ms^-1.

Of course, in case type should have Tag and it's multiplitave inverse at same time, it should be automatically removed from the Tag list.

This can actually be fully implemented with C++ template system. However making this right will take some time. Anyway, this way you define: typedef Distance C<DistangeTag>; typedef Velocity C<DistanceTag, MI<TimeTag>>; typedef Time C<TimeTag>;

And you have all the /,* operators for free and without hussle.

Note: Debugging types with complex tag is going to be painfull.

Materials to investigate:

https://github.com/rigtorp/awesome-modern-cpp