C++ Core Guidelines: Type Safety

Contents[Show]

The C++ core guidelines support three profiles: type safety, bounds safety, and lifetime safety. Thanks to the guideline support library (GSL), you can check your code against these profiles. Today, I start with the profile type safety.

 checklist 1295319 1280

 

If you don't know what a profile is, read my last post: C++ Core Guidelines: Profiles. Although the idea of a profile is it to target a specific goal, it needs assistance from the other profiles. This said, the type safety profile needs, for example, assistance from the bounds safety, and lifetime safety profile. Now, let's dive into the type safety.

Type Safety

Type safety means that you use the types correctly and, therefore, avoid unsafe casts and unions. Type safety consists of eight rules which are called type. The rules start with don't, always, or avoid and refer to existing rules. If necessary, I will add content to the rules.

Type 1:

  • Don’t use reinterpret_cast

  • Don’t use static_cast for arithmetic types

  • Don’t cast between pointer types where the source type and the target type are the same

  • Don’t cast between pointer types when the conversion could be implicit

The answer to this don't boil down to two points:

Avoid  in particular C-casts and prefer if necessary named casts:

Avoid casts:

Let's see what will happen if I screw up the type system.


// casts.cpp

 

#include <iostream>

int main(){

    double d = 2;
    auto p = (long*)&d;
    auto q = (long long*)&d;
    std::cout << d << ' ' << *p << ' ' << *q << '\n';

}

 

Neither the result with the Visual Studio compiler

castsWin

nor the result with the GCC or the Clang compiler is promising.

castsGccClang

What is so bad about the C-cast? You don't see which cast is performed. If you use a C-cast, a combination of casts will be applied if necessary. Roughly speaking, a C-cast starts with a static_cast, continues with a const_cast, and finally performs a reinterpret_cast.

There is another issue I don't like about a C-cast. It's challenging to find C-casts in your source code. This remark does not hold for C++-casts such as dynamic_cast, const_cast, static_cast, or reinterpret_cast.

Of course, you know how I will continue: explicit is better than implicit.

Prefer named C++-casts

Including the GSL, C++ offers eight different named casts.

Here are they including a short description:

    • static_cast: conversion between similar types such as pointer types or numeric types
    • const_cast: adds or removes const or volatile
    • reinterpret_cast: converts between pointers or between integral types and pointers
    • dynamic_ cast: converts between polymorph pointers or references in the same class hierarchy
    • std::move: converts to an rvalue reference
    • std::forward: converts to an rvalue reference
    • gsl::narrow_cast: applies a static_cast
    • gsl::narrow: applies a static_cast

I know, the description is concise. I want to make a least two remarks:

  1. GSL stands for guideline support library and is a header only library using the namespace gsl.  The GSL can be used to automatically check the rules of the C++ core guidelines and, in particular, the profiles. I will write about this check in a future post. I have already written a post to the GSL: C++ Core Guidelines: The Guidelines Support Library.
  2. What? std::move and std::forward are casts? Let's have a closer look at the internals of std::move: static_cast<std::remove_reference<decltype(arg)>::type&&>(arg). First the type of the argument arg is determined by decltype(arg). Then all references are removed and two new references added. The function std::remove_reference is from the type-traits library. I have already written a few posts to the type-traits library.  In the end, each argument arg to std::move comes out as rvalue reference.

Type 2:

  • Don’t use static_cast to downcast

I can give you a short answer: Use dynamic_cast instead. I have already written about this topic in my post C++ Core Guidelines: Accessing Objects in a Hierarchy.

Type 3:

  • Don’t use const_cast to cast away const

Let me be more specific. Casting away const is undefined behaviour if the underlying object such as constInt is not mutable.

 

const int constInt = 10;
const int* pToConstInt = &constInt;
 
int* pToInt = const_cast<int*>(pToConstInt);
*pToInt = 12;          // undefined behaviour

 

If you don't believe me, there is a footnote in the C standard [ISO/IEC 9899:2011] (subclause 6.7.3, paragraph 4) which is also relevant for the C++ standard: The implementation may place a const object that is not volatile in a read-only region of storage. Moreover, the implementation need not allocate storage for such an object if its address is never used.  This means, that a modification of an originaly constant object may have no result.

Type 4:

  • Don’t use C-style (T)expression or functional T(expression) casts

The first part of this don't is quite easy to answer: prefer named casts such as in type 1.

The functional T(e) cast is used to construct T from the expression e. Let me show what can happen if you use the functional cast in the wrong way.

// functionalCast.cpp

void f(int x, long y, double d, long long l){
    char c1(x);   
    char c2(y);   
    char c3(d);   
    char c4(l);
}

int main(){  
  f(3, 3l, 3.0, 3ll);
}

 

The function f takes four arguments and use these arguments for initialising char's. You get what you deserve and can only hope for a warning. C++ Insights shows explicitly, how your code is transformed. A static_cast is applied to each argument.

 cppInsightFunctionalCast

This process is called narrowing conversion and should be detected by the compiler. Using curly braces instead of round braces checks if a narrowing conversion happens. The C++ compiler must write a warning but typically throws an error when this happens. If you want to be sure that narrowing conversion always triggers an error, treat narrowing warnings wiht GCC and Clang as an error with -Werror=narrowing. Here is the modified program using curly braces for initialisation.

// functionalCastCurlyBraces.cpp

void f(int x, long y, double d, long long l){
    char c1{x};   
    char c2{y};   
    char c3{d};   
    char c4{l};
}

int main(){  
  f(3, 3l, 3.0, 3ll);
}

 

Now, the compiler detects what is going wrong.

 

narrowingConversionError

What's next?

In the next post, I finish the rules to type safety. They are about initialisation, unions, and varargs. Now, I have to prepare for a very exciting week in Aurora. I give a two days workshop to concurrency, a back to basics talk to concurrency, and a presentation to concepts at the CppCon.

 

 

 

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, Timo, 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 5890

All 2701907

Currently are 139 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments