Oh noes, there could be a null pointer!

It’s been some time since I wrote anything about C++ programming, so here’s something. Sorry for users of HourGlass and other softwares of mine if this isn’t of much interest. 😉 (I will probably start a separate blog specifically about programming later this year.)

Ideally one’s C++ code would never attempt to use an object via a null pointer, but in some cases having the possibility of a null pointer is just convenient to have. Maybe it’d be too tiresome to write an object that has some well defined null/empty/do-nothing state or whatever.

Class whose methods will be called in the examples :

class MyObject
{
public:
    double m_x=0.5;
    double foo() { m_x+=0.1; return sin(m_x)+cos(m_x); }
    void not_much() { }
    double borken_sum(double a, double b) { return m_x+a+b; }
    std::string mangle(std::string a)
    {
        std::reverse(a.begin(),a.end());
        return std::to_string(m_x)+a;
    }
};

The usual way to deal with a pointer that can be either null or valid is something like :

// MyObject* m_object ends up as nullptr or a valid pointer
// in some way before this code is run
// foo() of valid object should return a double
if (m_object!=nullptr)
  return m_object->foo(); 
else
  return 0.0; 

Or, more tersely with the terrible ternary operator :

return m_object!=nullptr ? m_object->foo() : 0.0;

I today came up(*) with an alternative solution that involves quite a hairy helper function but I kind of like this solution, assuming it actually works properly in most scenarios. (I didn’t extensively test this yet. There could be cases where the compiler gives an error or maybe the performance of making an object method call this way is horribly bad. Maybe Clang and GCC totally hate this. This was tested with the Visual Studio 2013 C++ compiler.)

The helper function in all its insanity :

template<typename U,typename F,typename... Args>
auto safe_call(U* ptr,F f,Args... args) -> decltype(std::mem_fn(f)(ptr,args...))
{
    if (ptr!=nullptr)
        return std::mem_fn(f)(ptr,args...);
    return decltype(std::mem_fn(f)(ptr,args...))();
}

Usage in client code :

return safe_call(m_object,&MyObject::foo);

In this version, no annoying additional control flow needs to be written, “nullptr” doesn’t need to be written, “m_object” doesn’t need to be written twice and the fallback value of 0.0 will be default constructed from the type the MyObject::foo() method returns.

Due to the variadic template, member functions with 1 or more arguments can also be used. The auto return type in the helper function also works for member functions that don’t return a value.

Additional usage examples :

double a=safe_call(m_object,&MyObject::borken_sum,2.0,1.0);
std::cout << safe_call(m_object,&MyObject::mangle,"Cat Attack") << "\n";
safe_call(m_object,&MyObject::not_much);

The downside is having to write the names of the class and the method with the &ClassName::MethodName notation.

Of course the safe_call-style technique doesn’t buy much if several methods of an object need to be called in a row. The following usual solution will work just fine and expresses the intent :

if (m_object!=nullptr)
{
  double a=m_object->foo();
  m_object->not_much();
  auto r=m_object->mangle("ooF");
} 

(*) No doubt this has been invented by someone else before me but I wanted to see if I could write it myself.

Advertisements
This entry was posted in C++, Programming. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s