Lego dimensions.svg

C++ Core Guidelines: Interfaces II

Interfaces are a contract between a service provider and a service consumer. The C++ Core Guidelines have 20 rules to make them suitable because “interfaces is probably the most important single aspect of code organization”.

Lego dimensions.svg

I wrote in my last post about the first ten rules. Today I will finish my job and write about the remaining ten rules.

Let’s dive directly into the details.

I.11: Never transfer ownership by a raw pointer (T*)

There is a conceptual issue with this code.

X* compute(args)    // don't
{
    X* res = new X{};
    // ...
    return res;
}

 

Who deletes the pointer X? There are at least three alternatives to deal with the ownership problem:

I.12: Declare a pointer that must not be null as not_null

What is the semantic difference between the three variations of the following function length?

int length(const char* p);            // it is not clear whether length(nullptr) is valid

int length(not_null<const char*> p);  // better: we can assume that p cannot be nullptr

int length(const char* p);            // we must assume that p can be nullptr

 

 

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.

     

    The intention of variations two and three of length is quite apparent. The second variation accepts only a non-null pointer; the third accepts a nullptr. You may have already guessed it. not_null if from the GSL.

    I.13: Do not pass an array as a single pointer

    Passing arrays as a single pointer is quite error-prone.

    void copy_n(const T* p, T* q, int n); // copy from [p:p+n) to [q:q+n)
    

     

    What will happen if n is too big? Right: undefined behavior. The GSL offers a solution called spans.

    void copy(span<const T> r, span<T> r2); // copy r to r2
    

     

    Spans deduce their number of arguments.

    I.22: Avoid complex initialization of global objects

    Global objects provide a lot of fun. For example, their initialization order is not defined if they are in different translation units. The following code snippet has undefined behavior.

    // file1.c
    
    extern const X x;
    
    const Y y = f(x);   // read x; write y
    
    // file2.c
    
    extern const Y y;
    
    const X x = g(y);   // read y; write x
    

    I.23: Keep the number of function arguments low

    There is a simple rule: one function should do exactly one job. If that is the case, the number of function arguments automatically becomes low, making the function easy to use.

    The New Parallel Algorithms of Standard Template Library, such as std::transform_reduce often break this rule.

    I.24: Avoid adjacent unrelated parameters of the same type

    What are the source and the destination of the following copy_n function? Any educated guess?

    void copy_n(T* p, T* q, int n); 
    

     

    I often have to look for documentation.

    I.25: Prefer abstract classes as interfaces to class hierarchies

    Of course, that is an evident and long-established rule for object-oriented design. The guidelines provide two reasons for this rule.

    • abstract classes are more likely to be stable than base classes
    • bases classes with state and non-abstract methods put more constraints on derived classes

    I.26: If you want a cross-compiler ABI, use a C-style subset

    ABI stands for Application Binary Interface.

    This is a strange rule in C++ guidelines. The reason is that “Different compilers implement different binary layouts for classes, exception handling, function names, and other implementation details.”. On some platforms, common ABIs are emerging. Using a single compiler, you can stick to the full C++ interface. In this case, you have to recompile the code.

    I.27: For stable library ABI, consider the Pimpl idiom

    Pimpl stands for a pointer to implementation and is the C++ variation of the bridge pattern. The idea is that a non-polymorphic interface holds the pointer to its implementation. Therefore, modification of the implementation doesn’t require recompilation of the interface.

    Here is an example from the C++ Core Guidelines:

    interface (widget.h)
    class widget {
        class impl;
        std::unique_ptr<impl> pimpl;
    public:
        void draw(); // public API that will be forwarded to the implementation
        widget(int); // defined in the implementation file
        ~widget();   // defined in the implementation file, where impl is a complete type
        widget(widget&&) = default;
        widget(const widget&) = delete;
        widget& operator=(widget&&); // defined in the implementation file
        widget& operator=(const widget&) = delete;
    };
    
    implementation (widget.cpp)
    
    class widget::impl {
        int n; // private data
    public:
        void draw(const widget& w) { /* ... */ }
        impl(int n) : n(n) {}
    };
    void widget::draw() { pimpl->draw(*this); }
    widget::widget(int n) : pimpl{std::make_unique<impl>(n)} {}
    widget::~widget() = default;
    widget& widget::operator=(widget&&) = default;
    

     

    The pimpl is the pointer that holds the handle to the implementation.

    For an in-depth discussion of this C++ idiom, read the GOTW #100 article by Herb Sutter. GotW stands für Guro of the Week.

    I.30: Encapsulate rule violations

    Sometimes code is ugly, unsafe, or error-prone because of various reasons. Put the code in one place and encapsulate it with an easy-to-use interface. That is called abstraction, which you sometimes have to do. I have no problem with that code if the internal code is stable and the interface only lets you use it correctly.

    What’s next?

    I often mentioned the guideline support library in the last posts, including the current one. Now it’s time to look at insight, and I will write about it in the next post.

     

     

    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,and Matt Godbolt.

    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 *