C++ Core Guidelines: Type Safety by Design

What does that mean: type safety by design. Type safety by design just means, that you always initialise your variables, use std::variant instead of a union, or prefer variadic templates and fold expressions to va_arg's.

abseiling 1842180 1280

As in my first post to type safety C++ Core Guidelines: Type Safety, I will name the four missing types of type safety and add additional information, if necessary. Here we are:

Type Safety

Type.5: Don’t use a variable before it has been initialized

The rules which object will be initialised or not are quite difficult to get right in C++. Here is a simple example.

struct T1 {};
class T2{
 public:
    T2() {} 
};

int n;                 // OK

int main(){
  int n2;              // ERROR
  std::string s;       // OK
  T1 t1;               // OK
  T2 t2;               // OK                  
}

 

n is a global variable; therefore, it is initialized to 0. This initialisation will not hold for n2, because it is a local variable and is, therefore, not initialised. But if you use a user-defined type such as std::string, T1, or T2 in a global or in local scope they are initialised.

If that is too difficult for you, I have a simple fix. Use auto. The c findompiler can not guess from an expression auto a of what type a has to be. Now, you can not forget to initialise the variable. You are forced to initialise your variables.

struct T1 {};
class T2{
 public:
    T2() {}
};

auto n = 0;

int main(){
  auto n2 = 0;
  auto s = ""s;      
  auto t1 = T1();               
  auto t2 = T2();
}

Type.6: Always initialize a member variable

In this case, I can make it short. I have already written in my post C++ Core Guidelines: More Non-Rules and Myths about the initialisation of member variables.

Type.7: Avoid naked union

First of all: What is a union? A union is a user-defined type that can hold one of its members at a time.

"Naked" unions are very error-prone because you have to keep track of the underlying type.

// nakedUnion.cpp

#include <iostream>

union Value {
    int i;
    double d;
};

int main(){
  
  std::cout << std::endl;

  Value v;
  v.d = 987.654;  // v holds a double
  std::cout << "v.d: " << v.d << std::endl;     
  std::cout << "v.i: " << v.i << std::endl;      // (1)

  std::cout << std::endl;

  v.i = 123;     // v holds an int
  std::cout << "v.i: " << v.i << std::endl;
  std::cout << "v.d: " << v.d << std::endl;      // (2)
  
  std::cout << std::endl;

}

 

 The union holds int the first iteration a double and in the second iteration an int value. If you read a double as an int (1) or an int as a double (2), you get undefined behaviour.

nakedUnion

std::variant

A std::variant is in contrast a type-safe union. We have it since C++17. An instance of std::variant has a value from one of its types. The type must not be a reference, arrayIts or void. A default-initialized std::variant will be initialized with its first type. In this case, the first type must have a default constructor. Here is a simple example based on cppreference.com.

// variant.cpp

#include <variant>
#include <string>
 
int main(){

  std::variant<int, float> v, w;       // (1)
  v = 12;                    
  int i = std::get<int>(v);
  w = std::get<int>(v);                // (2)
  w = std::get<0>(v);                  // (2)
  w = v;                               // (2)
 
  //  std::get<double>(v);   // error: no double in [int, float] (3)
  //  std::get<3>(v);        // error: valid index values are 0 and 1 (4)
 
  try{
    std::get<float>(w);                // (5)   
  }
  catch (std::bad_variant_access&) {}
 
  std::variant<std::string> v2("abc"); // (6)
  v2 = "def";                          // (7)

}

I define in line (1) both variants v and w. Both can have an int and a float value. std::get<int>(v) returns the value. In lines (2) you see three possibilities to assign the variant v to the variant w. But you have to keep a few rules in mind. You can ask for the value of a variant by type (line 3) or by index (line 4). The type must be unique and the index valid. On line (5), the variant w holds an int value. Therefore, I get a std::bad_variant_access exception. If the constructor call or assignment call is unambiguous, a conversion can take place. That is the reason that it's possible to construct a std::variant<std::string> in line (6) with a C-string or assign a new C-string to the variant (line 7).

Type.8: Avoid varargs

Variadic functions are functions such as std::printf which can take an abitrary number of arguments. The issue is that you have to assume that the correct types were passed. Of course, this  assumption is very error-prone and relays on the discipline of the programmer. 

To understand the implicit danger of variadic functions, here is a small example.

// vararg.cpp

#include <iostream>
#include <cstdarg>

int sum(int num, ... ){
  
    int sum{};
      
    va_list argPointer;
    va_start(argPointer, num );
    for( int i = 0; i < num; i++ )
        sum += va_arg(argPointer, int );
    va_end(argPointer);
      
    return sum;
}
 
int main(){
    
    std::cout << "sum(1, 5): " << sum(1, 5) << std::endl;
    std::cout << "sum(3, 1, 2, 3): " << sum(3, 1, 2, 3) << std::endl;
    std::cout << "sum(3, 1, 2, 3, 4): " << sum(3, 1, 2, 3, 4)  << std::endl; // (1)
    std::cout << "sum(3, 1, 2, 3.5): " << sum(3, 1, 2, 3.5) << std::endl;    // (2)

}

sum is a variadic function. It's first argument is the number of arguments which should be summed up. I will only provide so much info to the varargs macros that you can understand the program. For more information, read cppreference.com.

  • va_list: holds the necessary information for the following macros
  • va_start: enables the access to the variadic function arguments
  • va_arg: accesses the next variadic function argument
  • va_end: end the access of the varadic function arguments

In line (1) and line (2) I had a bad day. First, the number of the arguments num is wrong; second, I provided a double instead of an int. The output shows both issues. The last element in line (1) is missing and the double is interpreted as int (line 2).

vararg

This issue can be easily overcome with fold expressions in C++17:

 

// foldExpressions.cpp

#include <iostream>

template<class ...Args>
auto sum(Args... args) { 
    return (... + args); 
}
 
int main(){
    
    std::cout << "sum(5): " << sum(5) << std::endl;
    std::cout << "sum(1, 2, 3): " << sum(1, 2, 3) << std::endl;
    std::cout << "sum(1, 2, 3, 4): " << sum(1, 2, 3, 4)  << std::endl; 
    std::cout << "sum(1, 2, 3.5): " << sum(1, 2, 3.5) << std::endl;    

}

Okay, the function sum may look terrifying to you. C++11 supports variadic templates. These are templates that can accept an arbitrary number of arguments. The arbitrary number is held by a parameter pack denote by an ellipse (...). Additionally, with C++17 you can directly reduce a parameter pack with a binary operator. This addition, based on variadic templates, is called fold expressions. In the case of the sum function, the binary + operator (...+ args) is applied. If you want to know more about fold expressions in C++17, here is my previous post to it. 

The output of the program is as expected:

foldExpressions

Additionally to variadic templates and fold expression, there is another comfortable way for a function to accept an arbitrary number of arguments of a specific type: use a container of the STL such as std::vector as argument.

 

// vectorSum.cpp

 

#include <iostream>
#include <numeric>
#include <vector>

auto sum(std::vector<int> myVec){
     return std::accumulate(myVec.begin(), myVec.end(), 0);
}

int main(){

std::cout << "sum({5}): " << sum({5}) << std::endl;
std::cout << "sum({1, 2, 3}): " << sum({1, 2, 3}) << std::endl;
std::cout << "sum({1, 2, 3, 4}): " << sum({1, 2, 3, 4}) << std::endl;

}

 

In this case, a std::initializer_list<int> is used as argument of the function sum. A std::initializer_list can directly initialise a std::vector. In contrast, to fold expressions, std::accumulate is performed at runtime.

vectorSum

What's next?

Next time, I continue with the profile to Bounds Safety. This profile has four rules.

 

 

 

Thanks a lot to my Patreon Supporters: Paul Baxter,  Meeting C++, Matt Braun, Roman Postanciuc, Venkata Ramesh Gudpati, Tobias Zindl, Marko, Ramesh Jangama, G Prvulovic, Reiner Eiteljörge, Benjamin Huth, Reinhold Dröge, Abernitzke, Richard Ohnemus , Frank Grimm, Sakib, Broeserl, and António Pina.  

 

Thanks in particular to:
 
crp4

 

   

Get your e-book at Leanpub:

The C++ Standard Library

 

Concurrency With Modern C++

 

Get Both as one Bundle

cover   ConcurrencyCoverFrame   bundle
With C++11, C++14, and C++17 we got a lot of new C++ libraries. In addition, the existing ones are greatly improved. The key idea of my book is to give you the necessary information to the current C++ libraries in about 200 pages.  

C++11 is the first C++ standard that deals with concurrency. The story goes on with C++17 and will continue with C++20.

I'll give you a detailed insight in the current and the upcoming concurrency in C++. This insight includes the theory and a lot of practice with more the 100 source files.

 

Get my books "The C++ Standard Library" (including C++17) and "Concurrency with Modern C++" in a bundle.

In sum, you get more than 600 pages full of modern C++ and more than 100 source files presenting concurrency in practice.

 

Get your interactive course

 

Modern C++ Concurrency in Practice

C++ Standard Library including C++14 & C++17

educative CLibrary

Based on my book "Concurrency with Modern C++" educative.io created an interactive course.

What's Inside?

  • 140 lessons
  • 110 code playgrounds => Runs in the browser
  • 78 code snippets
  • 55 illustrations

Based on my book "The C++ Standard Library" educative.io created an interactive course.

What's Inside?

  • 149 lessons
  • 111 code playgrounds => Runs in the browser
  • 164 code snippets
  • 25 illustrations

Add comment


My Newest E-Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 2170

All 2689703

Currently are 244 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments