checklist 911840 1280

C++ Core Guidelines: Profiles

Informally, profiles are a subset of rules of the C++ core guidelines for specific concerns such as type safety, bounds safety, and lifetime safety. Thanks to the guideline support library, they can be checked

checklist 911840 1280

 There are two main reasons for the profiles:

  1. You have to deal with legacy code, and you can not apply all rules of the C++ core guidelines in one step. You have to apply the rules step by step and use them; therefore, some rules first and others later.
  2. Some related rules may be more critical to your codebase than others. They aim for a specific goal, such as the “avoidance of bounds error” or the “correct usage of types”. These related rules are called profiles.

Here is the formal definition of profiles from the C++ core guidelines:

  • Profile: A “profile” is a set of deterministic and portably enforceable subset rules (i.e., restrictions) that are designed to achieve a specific guarantee. “

Two terms in the definition are pretty interesting:

  • Deterministic: Local analysis can analyze the rule a compiler can implement it.
  • Portable enforceable: Different tools on different platforms give you the same answer.

Now, the question is: When does your code conform to a profile? The answer is simple: It has to be warning-free regarding a profile. There exist three profiles:

According to the C++ core guidelines, profiles may follow in the future regarding topics such as undefined and unspecified behavior. Before I continue in my next post with the three existing profiles, I have to explain the difference between undefined and unspecified behavior. Let’s make a detour.

Undefined and unspecified behavior

Here are the definitions from the current C++20 draft. The draft is written in American English.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Generic Programming (Templates) with C++": October 2024
  • "Embedded Programming with Modern C++": October 2024
  • "Clean Code: Best Practices for Modern C++": March 2025
  • Do you want to stay informed: Subscribe.

     

    • Undefined behavior (3.27): behaviour for which this document imposes no requirements.
    • Unspecified behavior (3.28): behaviour for a well-formed program, construct and correct data, that depends on the implementation.

    Okay, let me give you more explanation and a few examples:

    Undefined behaviour

    Informally, when your program has undefined behavior, you can not give any guarantees about your program. The result may be the expected result, you may get the wrong result, the compilation may break, or you get a runtime error. The behavior may depend on the platform, the compiler, the compiler version, the optimization level, or the state of the used computer. I could extend this list forever, but I will stop here. The action you have to take is quite simple: Fix the undefined behavior!

    To make it more concrete, here are a few examples of undefined behavior.

    • Access to containers such as C-arrays or STL-containers out of bounds
    • Use of an uninitialized variable
    • Dereferencing a null pointer
    • Division by zero
    • Undefined order of evaluation

    Besides the last point, the listed undefined behaviors should be obvious.

    Informally said, undefined order of evaluation of a subexpression A followed by a subexpression B means that the compiler can evaluate the subexpression A and B in any order it wants. Now we need guarantees that A is sequenced_before B because if A is sequenced_before B, A is evaluated before B, and we have these guarantees. For example, logical operators, full expression (a = c;), invoking or returning from a function, or the end of an initialization establish sequenced_before relations. Admittedly, this was a simplification of the order of evaluation. Read the details on cppreference.com

    Maybe a tiny program helps your understanding. When I execute the following program with C++14, I get three warnings.

    // undefinedBehaviour.cpp
    
    #include <array>
    #include <iostream>
    
    int main(){
    
        std::cout << std::endl;
        
        std::array<int, 1> myArr{};                      // (0)
    
        int i{};                                         // (0)
    
        myArr[i] = i++;                                  // (1) 
        
        std::cout << i << "  " << i++ << std::endl;      // (1)
        
        std::cout << std::endl;
    
        int n = ++i + i;                                 // (2)
    
        std::cout << "n: " << n << std::endl;  
    
        std::cout << std::endl;
    
    }
    

     

    I use the line (0) curly braces to initialize both data types. The three expressions inline (1) and line (2) have undefined behaviour with C++14. The reason is that each expression is thoroughly evaluated at the end of the expression. The semicolon is, in these cases, the end of the expression. The Clang compiler gives self-explanatory warnings.

     undefinedBehaviour

    Unsequenced evaluation means that the operations may happen in any order and overlap. This is possible in a single-threaded execution because the underlying assembler instruction may interleave. Sorry, this was not the complete truth. Lines (1) have undefined behaviour in C++14 but unspecified behaviour in C++17.

    Unspecified behavior

    Unspecified behaviour means the implementation is not required to document which behaviour occurs. For example, it is unspecified in which order the arguments of a function are evaluated.

    Here is an interesting example: undefined behaviour in C++14 but unspecified behaviour in C++17.

    #include <iostream>
    
    void func(int fir, int sec){
        std::cout << "(" << fir << "," << sec << ")" << std::endl;
    }
    
    int main(){
        int i = 0;
        func(i++, i++);
    }
    

     

    When I execute the program, the output differs between GCC and Clang. You neither get the same result nor are the arguments evaluated left to right.

     

    GCC

    gcc

     

     

     

    Clang

    clang

     

     
     

     

    Unspecified behavior in C++17 guarantees that each argument is thoroughly evaluated before the other argument is evaluated. But you still have no guarantee in which order it is done.

    What’s next?

    After this necessary detour to undefined and unspecified behavior, I will write in my next post about the three profiles: safety, bounds safety, and lifetime safety.

    The election of the pdf packages is made:

    You can see the details of the election in the provided links. The pdf packages will be available in 1 – 2 weeks.

     
     

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *