Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Safe pointer dereferencing in C++

In our code base we have many constructions like this:

auto* pObj = getObjectThatMayVeryRarelyBeNull();
if (!pObj) throw std::runtime_error("Ooops!");
// Use pObj->(...)

In 99.99% of cases this check is not triggered. I'm thinking about the following solution:

auto& obj = deref_or_throw(getObjectThatMayVeryRarelyBeNull());
// Use obj.(...)

Where deref_or_throw is declared as follows:

template<class T> T& deref_or_throw(T* p) {
  if (p == nullptr) { throw std::invalid_argument("Argument is null!"); }
  return *p;
}

That code is much clearer and works as I need.

The question is: am I reinventing the wheel? Is there some related solution in standard or boost? Or do you have some comments on the solution?

PS. Related question (with no satisfying answer): Is there a C++ equivalent of a NullPointerException

like image 658
Mikhail Avatar asked Mar 18 '15 12:03

Mikhail


People also ask

What is pointer dereferencing in C?

Dereferencing is used to access or manipulate data contained in memory location pointed to by a pointer. *(asterisk) is used with pointer variable when dereferencing the pointer variable, it refers to variable being pointed, so this is called dereferencing of pointers.

Which C operator is used to dereference a pointer?

The unary operator * is used to declare a pointer and the unary operator & is used to dereference the pointer.. In both cases, the operator is “unary” because it acts upon a single operand to produce a new value.

Can you dereference a void pointer in C?

Some Interesting Facts: 1) void pointers cannot be dereferenced.

Can you always dereference a pointer?

That said, we can dereference the pointer without ever accessing the value it points to. For example: char *p = NULL; *p; We dereferenced the NULL pointer without accessing its value.


2 Answers

There are two ways of dealing with the "rare case of null-pointer" issue. First, it's the proposed exception solution. For this, the method deref_or_throw is a good thing, though I would prefer to throw a runtime_error, rather than an invalid_argument exception. You may also consider naming it NullPointerException, if you prefer that.

However, if the ptr != nullptr case is actually a precondition to the algorithm, you should try your best to achieve 100% safety in having this non-null case. An assert is the appropriate thing in this case.

Advantages of assert:

  • No cost at runtime in release mode
  • Makes it crystal clear to the developer, that he is responsible for this to never happen.

Disadvantages of assert:

  • Potential undefined behavior in release mode

You should also consider writing a method getObjectThatMayNeverBeNullButDoingTheSameAsGetObjectThatMayVeryRarelyBeNull(): A method that is guaranteed to return a non-null pointer to an object that is otherwise completely equivalent to your original method. However, if you can go the extra mile, I would strongly suggest to not returning a raw pointer anyway. Embrace C++11 and return objects by value :-)

like image 197
stefan Avatar answered Sep 20 '22 09:09

stefan


You can, and probably should, design with null cases in mind. For example, in your problem domain, when does it make sense for class methods to return null? When does it make sense to call functions with null arguments?

Broadly speaking, it can be beneficial to remove null references from code whenever possible. In other words, you can program to the invariant that "this will not be null here... or there". This has many benefits. You won't have to obfuscate code by wrapping methods in deref_or_throw. You may achieve more semantic meaning from code, because, how often are things null in the real world? Your code may be more readable if you use exceptions to indicate errors rather than null values. And finally, you reduce the need for error-checking and also reduce the risk of run time errors (the dreaded null pointer dereference).

If your system was not designed with null cases in mind, I'd say it's best to leave it alone and not go crazy wrapping everything with deref_or_throw. Perhaps take an Agile approach. As you are coding, inspect the contracts that your class objects offer as services. How often can these contracts reasonably be expected to return null? What is the semantic value of null in these cases?

You could probably identify the classes in your system that might reasonably be expected to return null. For these classes, null may be valid business logic, rather than fringe cases that are indicative of implementation errors. For the classes that might be expected to return null, the additional check may be worth the safety. In this sense, checking for null feels more like business logic rather than low level implementation details. Across the board, though, systematically wrapping all pointer deference in deref_or_throw might be a cure that is its own poison.

like image 30
Walt Dizzy Records Avatar answered Sep 19 '22 09:09

Walt Dizzy Records