安全调用操作符 - 如果object不为null,则仅调用函数

时间:2017-10-25 11:52:13

标签: c++ function

在c ++中是否有以下内容:

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

我想缩短如下调用:

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

我可以使用任何宏/语言技巧使其看起来像伪代码?

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

编辑 - 使用帐户

我为另一个软件开发了一些插件,我自己也可以使用其他插件。

最简单的例子是一个日志插件,我想在我的代码中遍布很多日志调用,并且只想记录某些内容,如果日志模块已加载并且可用。我从我的插件所用的软件中获得了指向单例记录器模块的指针。我的代码会受到以下日志行的污染:

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

如果没有安全的话,那么将它作为一个单独的衬垫会更美观和紧凑......

2 个答案:

答案 0 :(得分:3)

代码样式解决方案( RIGHT 解决方案)

可以改进设计以解决此问题。如果未加载logger插件,则不应该收到nullptr,而应该是一个虚拟logger对象,其实现为空。

工厂(尤其是创建全局基础架构对象的工厂)不应返回nullptr

智能指针包装

这是您可以使用的概念

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();
}

如果使用safe_ptr<T>初始化,nullptr将使用指向对象的有效指针。

您当然可以改进此解决方案,使其仅适用于定义默认回退静态对象的对象,例如:

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->;
};

这将避免私有T t的多次分配,并允许fallback对象的特定初始化。

答案 1 :(得分:2)

不是你建议的解决方案,但由于我个人觉得这些“安全”的呼叫操作员特别难以理解,我建议基于现场调用的lambda替代代码风格:

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

    if(!pClass)
        return 0;

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

这个Gizmo被Javascipt人称为IIFE,用分支代码路径包装复杂的初始化非常有用。