C++ Core Guideline: The Guideline Support Library

Contents[Show]

The guidelines support library (GSL) is a small library for supporting the guidelines of the C++ core guidelines. It should help to write better C++ programs. Therefore, its key concerns are memory safety and type safety. They are already a few implementations of the GSL available.

The GSL is a header only library, hence, you can use the functions and types of the library quite easily. The best-known implementation is the one from Microsoft, hosted at Github: Microsoft/GSL. The Microsoft version requires C++14 support and runs on various platforms. Here are the main platforms.

  • Windows using Visual Studio 2015
  • Windows using Visual Studio 2017
  • GNU/Linux using Clang/LLVM 3.6
  • GNU/Linux using GCC 5.1

But that is not all, there exist more implementations on GitHub. I want explicitly emphasise the GSL-lite implementation of Martin Moene. His implementation even works with C++98 and C++03.

Before I dive into the details, there is one issue which makes my writing difficult: the lack of good documentation or tutorials. To get an idea, what the function and types should support, you have to install the library and analyse the unit-test. That is not the kind of documentation, I expect. In contrast, the installation and usage of Microsoft implementation  of the GSL were quite easy on Windows and Linux.

 

support

So, let me dive into the details. The GSL consists of five components. Here is a first overview:

You may be wondering that the GSL has its own smart pointer gsl::unique_ptr and gsl::shared_ptr because the C++11 standard has std::unique_ptr and std::shared_ptr. The idea is quite simple: you can use the GSL with a compiler that does not support C++11. A lot of the functions and types that the GSL support may become part of C++20. That holds at least for the concepts and the assertions. Additionally, it is quite probable that the remaining parts will also become part of upcoming C++ standards.

The components

Let's have a look at the Views,

GSL.view: Views

A view is never an owner. In the case of a gsl::span<T> it represents a non-owning range of continguous memory. This can be an array, a pointer with a size, or a std::vector. The same holds for gsl::string_span<T> or a zero terminated C strings: gsl::(cw)zstring.The main reason for having a gsl::span<T> is that a plain array will be decayed to a pointer if passed to a function; therefore the size is lost.

gsl::span<T> automatically deduces the size of the plain array or the std::vector. If you use a pointer, you have to provide the size.

template <typename T>
void copy_n(const T* p, T* q, int n){}

template <typename T>
void copy(gsl::span<const T> src, gsl::span<T> des){}

int main(){
    
  int arr1[] = {1, 2, 3};
  int arr2[] = {3, 4, 5};
  
  copy_n(arr1, arr2, 3);         // (1)
  copy(arr1, arr2);              // (2)
    
}

 

In contrast to the function copy_n (1), you have not to provide the number of elements for the function copy (2). Hence, a common cause of errors is gone with gsl::span<T>.

There are various kinds of owners in the GSL.

GSL.owner: Ownership pointers

I assume, you know std::unique_ptr and std::shared_ptr and, therefore, you know gsl::unique_ptr and gsl::shared_ptr. If not, here are my posts about smart pointers.

gsl::owner<T*> marks a pointer that has ownership of the referenced object. You should use gsl::owner<T> if you can not use resource handles such as smart pointers or containers. The key point about the owner is that you have to free the resource explicitly. Raw pointers that are not marked as gsl::owner<T*> are considered to be non-owning in the C++ core guidelines. Therefore, you have not to free the resource.

gsl::dyn_array<T> and gsl::stack_array<T> are two new array types.

  • gsl::dyn_array<T> is a heap-allocated array with a fixed size of elements that is specified at run-time.
  • gsl::stack_array<T> is a stack-allocated array with a fixed size of elements that is specified at run-time.

GSL.assert: Assertions

Thanks to Expects() and Ensures(), you can state preconditions and postconditions for your functions. Currently, you have to place them in the function body but this will later be moved to the function declaration. Both functions are part of the contract proposal.

Here is a simple example using Expects() and Ensures().

int area(int height, int width)
{
    Expects(height > 0);            
    auto res = height * width;
    Ensures(res > 0);
    return res;
}

 

GSL.util: Utilities

gsl::narrow_cast<T> and gsl::narrow are two new casts.

  • gsl::narrow_cast<T> is a static_cast<T> that only expresses its intent. A narrowing conversion may happen.
  • gsl::narrow is a static_cast<T> that throws a narrowing_error exception if static_cast<T>(x) != x.

gsl::not_null<T*> models a pointer that never should be a nullptr. If you set a gsl::not_null<T*> pointer to a nullptr, you will get a compiler error. You can even put a smart pointer such as std::unique_ptr or std::shared_ptr into a gsl::not_null<T*>. Typically, you use gsl::not_null<T*> for function parameters and their return type. Therefore, you can not forget to check if a pointer holds a value.

int getLength(gsl::not_null<const char*> p); // p cannot be a nullptr

int getLength(const char* p);                // p can be a nullptr

 

Both functions state their intent explicitly. The second one can accept a nullptr.

finally allows you to register a callable that will run at the end of the scope.

void f(int n)
{
    void* p = malloc(1, n);
    auto _ = finally([p] { free(p); });
    ...
}
    

 

At the end of the function f, the lambda function [p] { free(p); } will be invoked automatically.

According to the C++ core guidelines, you should consider finally as a last resort, if you can not use proper resource management such as smart pointers or STL containers.

GSL.concept: Concepts

I make it short because most of the concepts are defined in the Ranges TS.  Here are my posts to concepts.

My last words

I'm impressed by the guideline support library. What I like in particular, is that it requires no C++11 conformant compiler. You can even use it in legacy code and make it a lot more memory safe and type safe.  I forget to mention: the GSL "aim for zero-overhead when compared to equivalent hand-written checks.". That's a promise.

What's next?

After my short detour to the GSL I will return to the rules of the C++ core guidelines. The next post is about functions in general, the parameters of functions, and their return value in particular.

 

 

Thanks a lot to my Patreon Supporter: Eric Pederson.

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Get your e-book. Support my blog.

 

Add comment


My Newest E-Books

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 56

All 506522

Currently are 197 guests and no members online