800px Head Platon Glyptothek Munich 548

C++ Core Guidelines: The Philosophy

Today, I will dig deeper into the C++ Core Guidelines. I wrote about the Introduction section in my last post about the C++ Core Guidelines. Today I write about the section that is “primarily for humans”. This is the most general section and is called Philosophy. The rules are so general that you can apply them to each programming language.

 

Only to remind you. The C++ Core Guidelines consist of 350 rules. They a grouped in the following sections.

Let’s look at each of the 13 philosophy rules. They should provide the rationale for all 350 rules.

Philosophy

 800px Head Platon Glyptothek Munich 548

The following 13 rules cannot be checked thoroughly. Here are they.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

Be part of my mentoring programs:

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (starts March 2024)
  • Do you want to stay informed: Subscribe.

     

    I will provide examples from the C++ Core Guidelines for each rule if possible.

    Express ideas directly in code

    A method should express its intent. Have a look here.

    class Date {
        // ...
    public:
        Month month() const;  // do    
        int month();          // don't
        // ...
    };
    

     

    Neither does the second method month express that it will not change the instance nor return a Month object.

    The same reasoning often holds for explicit loops versus algorithms of the Standard Template Library (STL).

    int index = -1;                    // bad
    for (int i = 0; i < v.size(); ++i) {
        if (v[i] == val) {
          index = i;
          break;
        }
    }
    
    auto p = find(begin(v), end(v), val);  // better
    

     

    The meta-rule is obvious. You should know and use the algorithms of the STL.

    Write in ISO Standard C++

    This rule is simple and has a straightforward enforcement statement: “Use an up-to-date C++ compiler (currently C++11 or C++14) with a set of options that do not accept extensions.”

    Express intent

    Your code should express its intent. What can we deduce from the three explicit and implicit loops?

    for (const auto& v: vec) { ... }       (1)
    for (auto& v: vec){ ... }              (2)
    for_each(par, vec, [](auto v){ ... }); (3)
    

     

    The elements of the container vec will not be modified in (1). On the contrary, the elements in expression (2) will be modified. The algorithm for_each is executed with the parallel execution policy (par). That means the order of iteration does not matter.

    Ideally, a program should be statically type-safe

    You should strive for statically type-safe programs. Of course, that is impossible because there are problem areas in C++. The C++ Core Guidelines name the problem areas and possible solutions.

    • use std::variant (new with C++17) instead of unions
    • minimize the use of casts; use templates if possible
    • use gsl::span against array decay (if you pass an array to a function, it will implicitly decay to a pointer) and range errors
    • minimize narrowing conversions (narrowing conversion is an implicit conversion including the loss of data accuracy; for example, a double becomes an int implicitly)

    gsl stands for Guideline support library GSL. GSL is a small library to support the set of guidelines from the C++ Core Guidelines. I will write about the GSL in an upcoming post.

    Prefer compile-time checking to run-time checking

    All that can be done at compile time must not be done at run time. Since C++11, we have the function static_assert and the type-traits library. static_assert will check a predicate such as static_assert(sizeof(int) >= 4) at compile time. Thanks to the type-traits library, we can state powerful conditions about a type T at compile time: static_assert(std::is_integral<T>::value). Of course, if the check fails at compile-time, the compilation will fail with a readable error message. I already wrote about static_assert.

    What cannot be checked at compile-time should be checkable at run time

    This rule talks about hard-to-detect errors that should be avoided. The examples are about dynamically allocated arrays.

    extern void f(int* p);
    extern void f2(int* p, int n);
    extern void f3(unique_ptr<int[]> uniq, int n);
    
    f(new int[n]);                  (1)
    f2(new int[n], m);              (2)
    f3(make_unique<int[]>(n), m);   (3)
    

     

    What’s wrong with the example? The call (1) does not pass the number of elements. (2) makes passing the wrong number of elements possible, and (3) passes the ownership and the size separately. By bypassing a reference or a view (part of the gsl) you can overcome these issues.

    Catch run-time errors early

    Here are the enforcements for this rule:

    • Look at pointers and arrays: Do range-checking early and not repeatedly
    • Look at conversions: Eliminate or mark narrowing conversions
    • Look for unchecked values coming from the input
    • Look for structured data (objects of classes with invariants) being converted into strings

    Don’t leak any resource

    Leaking resources are, in particular, critical for long-running programs. Resources may be memory but also file handles or sockets. The idiomatic way to solve this issue is RAII. The idea of the RAII idiom is quite simple. You bind a resource’s acquisition and release to the lifetime of a local object. Therefore, the resource will automatically be initialized in the constructor and released in the destructor. The acronym RAII stands for Resource Acquisition Is Initialization. Smart pointers and locks are based on this technique. Here are more details of RAII.

    Don’t waste time or space

    The reason for this rule is quite promising: “This is C++”. One example of the rule is a riddle.

    What’s wasted here?

    void lower(string s)
    {
        for (int i = 0; i < strlen(s); ++i) s[i] = tolower(s[i]);
    }
    

     

    Prefer immutable data to mutable data

    There are a lot of reasons that speak for immutable data:

    • It’s easier to reason about constants than about variables.
    • Constant has more optimization potential.
    • Constants are free of data races.

    Encapsulate messy constructs rather than spreading through the code

    Messy code is prone to bugs and harder to write. Therefore, you should encapsulate the low-level code in a function or a method and put a good interface around it.

    Use supporting tools as appropriate

    Computers are better than humans at doing boring and repetitive tasks. That means you should use static analysis, concurrency, and testing tools to automate these verifying steps.

    Use support libraries as appropriate

    That is quite easy to explain. It would be best to go for well-designed, well-documented, and well-supported libraries. Therefore, you will get a well-tested and nearly error-free library and highly optimized algorithms from domain experts. Two outstanding examples are the C++ standard library and the Guidelines Support Library.

    What’s next?

    An interface is a contract between the service provider and the service user. There are 20 rules about interfaces in the C++ Core Guidelines. In the next post, I will have a closer look at interfaces.

     

     

     

     

    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, Kris Kafka, 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, Dmitry Farberov, 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, moon, Philipp Lenk, Hobsbawm, and Charles-Jianye Chen.

    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

    Seminars

    I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

    Standard Seminars (English/German)

    Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

    • C++ – The Core Language
    • C++ – The Standard Library
    • C++ – Compact
    • C++11 and C++14
    • Concurrency with Modern C++
    • Design Pattern and Architectural Pattern with C++
    • Embedded Programming with Modern C++
    • Generic Programming (Templates) with C++
    • Clean Code with Modern C++
    • C++20

    Online Seminars (German)

    Contact Me

    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 *