更改函数指针签名并将其调用为忽略返回类型是否安全?

时间:2014-08-20 09:43:41

标签: c++ std-function stdbind

在我们的代码库中,我们有使用(例如std::function<void()>)存储的回调。有时我们希望将具有不同签名的函数绑定到回调,这可以使用bind来完成。这适用于不同的函数参数,但是尝试将返回某些内容的函数绑定到期望返回void的回调不起作用,请参阅here

我们发现最简单的解决方案是将绑定函数的签名强制转换为具有相同参数但返回类型为void的函数:

#include <functional>
#include <iostream>

int ReturnInt()
{
  std::cout << "ReturnInt" << std::endl;
  return 5;
}

struct Struct
{
  int ReturnInt()
  {
    std::cout << "Test::Func" << std::endl;
    return 30;
  }
};

template<typename ReturnType, typename ... ArgumentTypes>
auto IgnoreResult(ReturnType (*i_Func)(ArgumentTypes ...))
  -> void (*)(ArgumentTypes ...)
{
  return reinterpret_cast<void (*)(ArgumentTypes ...)>(i_Func);
}

template<typename ReturnType, typename ClassType, typename ... ArgumentTypes>
auto IgnoreResult(ReturnType (ClassType::*i_Func)(ArgumentTypes ...))
  -> void (ClassType::*)(ArgumentTypes ...)
{
  return reinterpret_cast<void (ClassType::*)(ArgumentTypes ...)>(i_Func);
}


int main(int argc, char **argv)
{
  std::function<void ()> BoundType;

  Struct instance;
  BoundType = std::bind(IgnoreResult(&Struct::ReturnInt), &instance);
  BoundType();

  BoundType = std::bind(IgnoreResult(&ReturnInt));
  BoundType();

  return 0;
}

已经使用 Visual Studio 2013 11月CTP cygwin clang 3.4.2 cygwin gcc 4.8.3 对此进行了测试,并且适用于所有平台但调用已转换为不同函数签名的函数指针是未定义的行为。

我知道某些调用约定可能会破坏它,但据我所知Microsoft calling conventions,返回类型是通过寄存器而不是通过堆栈传递的。我们也从不指定不同的调用约定,并始终使用默认值。

假设 gcc clang Microsoft 编译器不会改变其行为,这是一种安全的方法来忽略绑定回调时返回函数的类型?

3 个答案:

答案 0 :(得分:4)

你可以使用lambdas:

#include <functional>
#include <iostream>

int ReturnInt()
{
  std::cout << "ReturnInt" << std::endl;
  return 5;
}

struct Struct
{
  int ReturnInt()
  {
    std::cout << "Test::Func" << std::endl;
    return 30;
  }
};

int main(int argc, char **argv)
{
  std::function<void ()> BoundType;

  Struct instance;
  BoundType = [&instance] { instance.ReturnInt(); };
  BoundType();

  BoundType = [] { ReturnInt(); };
  BoundType();

  return 0;
}

答案 1 :(得分:4)

  

更改函数指针签名并调用它来忽略返回类型是否安全?

不,不是,c.f。 C ++标准,第5.2.10节[expr.reinterpret.cast]

  

通过指向函数类型(8.3.5)的函数调用函数的效果与函数定义中使用的类型不同是未定义的。

即使它出现来处理特定的编译器/平台,也没有什么可以保证它真正(隐藏的副作用,堆栈损坏......)。

你应该考虑一个新的设计,首先不需要这个演员阵容(没有更多的背景很难回答)

答案 2 :(得分:3)

您可以使用类似于employed in libsigc++2的方法(通过sigc :: hide_return())解决您的问题。