Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Safe call operator - only call function if object is not null

Tags:

c++

function

Is there something like following in c++:

https://kotlinlang.org/docs/reference/null-safety.html#safe-calls

I want to shorten calls like following:

int x = 0;
IPtr pClass(...);
if (pClass)
{
    pClass->...
    pClass->...
    x = pClass->function();
}

Any macro/language trick I can use to make this look like following pseudo code?

IPtr pClass(...);
pClass?->... // only call function if pClass != nil
pClass?->... // only call function if pClass != nil
int x = pClass?->function() | 0; // only call function if pClass != nil, otherwise assign 0 to x

Edit - UseCase

I develop a few plugins for another software and other plugins of myself may be available or not.

Most simple example would be a logging plugin, where I want to have a lot of logging calls all over in my code and want to log something only, if the logging module was loaded and is available. I get a pointer to the singleton logger module from the software my plugin is for. My code would be polluted by log lines like following:

if (logger)
{
    logger->log(...);
}

And it would be more beautiful and compact to make this a one liner without the if that is null safe...

like image 837
prom85 Avatar asked Oct 25 '17 11:10

prom85


2 Answers

Code style solution (The RIGHT solution)

The design could be improved to solve this issue. When the logger plugin is not loaded, you shouldn't receive a nullptr but rather a dummy logger object that has an empty implementation.

Factories (especially ones creating global infrastructure objects) should not return nullptr.

Smart pointer wrapper

This is a concept you could use:

struct Object {
  void run() { std::cout << "run" << std::endl; }
  void stop() { std::cout << "stop" << std::endl; }
};

template < typename T >
struct safe_ptr : std::unique_ptr<T> {
  safe_ptr(T * ptr) : std::unique_ptr<T>(ptr ? ptr : &t) {}

  virtual ~safe_ptr() {
    if (::std::unique_ptr<T>::get() == &t)
      ::std::unique_ptr<T>::release();
  }

  using std::unique_ptr<T>::operator*;
  using std::unique_ptr<T>::operator->;

private:
  T t;
};

int main() {
  safe_ptr<Object> safe(nullptr);
  safe->run();
}

The safe_ptr<T> will use a valid pointer to an object if initialized using a nullptr.

You could of course improve this solution to work only with Objects that define a default fallback static object, e.g:

struct Object {
  void run() { std::cout << "run" << std::endl; }
  void stop() { std::cout << "stop" << std::endl; }

  static Object fallback;
};

Object Object::fallback;

template < typename T >
struct safe_ptr : std::unique_ptr<T> {
  safe_ptr(T * ptr) : std::unique_ptr<T>(ptr ? ptr : &T::fallback) {}

  virtual ~safe_ptr() {
    if (::std::unique_ptr<T>::get() == &T::fallback)
      ::std::unique_ptr<T>::release();
  }

  using std::unique_ptr<T>::operator*;
  using std::unique_ptr<T>::operator->;
};

This will avoid multiple allocations of the private T t and will allow a specific initialization of the fallback object.

like image 65
Daniel Trugman Avatar answered Sep 28 '22 18:09

Daniel Trugman


Not the solution you're suggesting, but since I personally find these "safe" call operators particularly hard to follow I'll suggest an alternative code style based on a lambda called on the spot:

int const x = [&] {
    IPtr const pClass = /*...*/;

    if(!pClass)
        return 0;

    pClass->/*...*/;
    pClass->/*...*/;
    return pClass->function();
}();

This gizmo is called an IIFE by Javascipt folks, and is incredibly useful to wrap complicated initializations with branching code paths.

like image 24
Quentin Avatar answered Sep 28 '22 20:09

Quentin