HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

C++ maybe pointer type implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
maybepointerimplementationtype

Problem

Motivation

This is intended to implement a C++ maybe_ptr type that can either hold a pointer to an underlying type T or a nullptr. The idea is to guarantee at compile-time the absence of null pointer dereferencing errors in code that uses raw pointers.

Traditionally, these kinds of errors are prevented by the user having to guard pointer dereferencing by explicitly checking nullptr. For example:

void foo(int* n)
{
    if(n != nullptr)
    {
        bar(*n);
    }
}


The obvious danger is that the user forgets to check for nullptr, in which case it is undefined behavior that is not detected by the compiler and may not even be easily detected at run-time.

In contrast, maybe_ptr guarantees nullptr checking before the user is able to deference the raw pointer.

Usage

Before getting to the implementation, let's see how maybe_ptr is used (leveraging C++11 lambdas):

void foo(const maybe_ptr& ptr)
{
    bool ran = maybe_if(ptr, [](int* n)
    {
        bar(*n);  // never reached if n is null
    });
    // ran is true if ptr did not contain null (i.e. the lambda was run)
}


We can easily assign raw pointers to initialize maybe_ptr:

int n;
maybe_ptr ptr = &n;


Note that maybe_ptr does not allow any direct access to the raw pointer once it is assigned:

maybe_ptr ptr;
std::string* s = *ptr; // compiler error: no "*" operator defined
ptr->length(); // compiler error: no "->" operator defined
ptr->get(); // compiler error: "get" is inaccessible (private method)


The only way to access the pointer is through the maybe_if function, with a function pointer, lambda function, or functor. A couple overloaded maybe_if functions are provided for convenient, simultaneous access to multiple pointers within the same lambda function:

```
void foo(const maybe_ptr& ptr1, const maybe_ptr& ptr2)
{
bool ran = maybe_if(ptr1, ptr2, [](int n, float x)
{
bar(n, x); // never reached if n or x are null
});

Solution

Variadic expansion

Until we get fold expressions, we have to use a workaround to check all the pointers

template
bool maybe_if(const maybe_ptr &... args, Func function)
{
  bool result = true;
  (void)std::initializer_list{ (result = result && args, 0)... };

    if (result)
    {
      function(args.get()...);
    }
    return result;
}


Why return bool (or throw)?

The usefulness of an maybe type is more limited when you don't return a maybe.

Instead of your

bool ran = maybe_if(ptr, bar);
if (ran) 
{ 
    bar happened, do other stuff
    /* why didn't we just if (ptr) { bar(*ptr) } here? */ 
}


Instead it is

auto bar_res = maybe_if(ptr, bar);
auto baz_res = maybe_if(bar_res, baz);
// etc...
bool last_res = maybe_if(baz_res, last);
return last_res;


Doing that would resolve points 3-5, however you are explicitly non-owning

Don't use std::function

Your worry that extra overhead will occur from ifs is misplaced, you should be much more worried about hiding functions in std::function<>, which is a very heavyweight construction, which is much more suitable for storing Callables

Code Snippets

template<typename ... Args, typename Func>
bool maybe_if(const maybe_ptr<Args> &... args, Func function)
{
  bool result = true;
  (void)std::initializer_list<int>{ (result = result && args, 0)... };

    if (result)
    {
      function(args.get()...);
    }
    return result;
}
bool ran = maybe_if(ptr, bar);
if (ran) 
{ 
    bar happened, do other stuff
    /* why didn't we just if (ptr) { bar(*ptr) } here? */ 
}
auto bar_res = maybe_if(ptr, bar);
auto baz_res = maybe_if(bar_res, baz);
// etc...
bool last_res = maybe_if(baz_res, last);
return last_res;

Context

StackExchange Code Review Q#154979, answer score: 3

Revisions (0)

No revisions yet.