patterncppMinor
C++ maybe pointer type implementation
Viewed 0 times
maybepointerimplementationtype
Problem
Motivation
This is intended to implement a C++
Traditionally, these kinds of errors are prevented by the user having to guard pointer dereferencing by explicitly checking
The obvious danger is that the user forgets to check for
In contrast,
Usage
Before getting to the implementation, let's see how
We can easily assign raw pointers to initialize
Note that
The only way to access the pointer is through the
```
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
});
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
Why return bool (or throw)?
The usefulness of an maybe type is more limited when you don't return a
Instead of your
Instead it is
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
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 CallablesCode 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.