指向函数的指针和指向对象的指针语义

Pointer-to-Function and Pointer-to-Object Semantics

我在获取部分限定的函数对象以便稍后在另一个线程中使用可变参数调用时遇到问题。

在 GCC 中,我一直在使用我制作的宏和 typedef,但我正在完成我的项目并试图清除警告。

#define Function_Cast(func_ref) (SubscriptionFunction*) func_ref
typedef void(SubscriptionFunction(void*, std::shared_ptr<void>));

使用如下所示的 Function_Cast 宏会导致 "warning: casting between pointer-to-function and pointer-to-object is conditionally-supported"

Subscriber* init_subscriber = new Subscriber(this, Function_Cast(&BaseLoaderStaticInit::init), false);

我真正需要的是一个指针,我可以将其作为 std::bind 的对象。这通常是如何完成的?

还有,这个有条件支持的东西真的很烦人。我知道在 x86 上我的代码可以正常工作并且我知道依赖 sizeof(void*) == sizeof(this*) 的局限性 *.

此外,有没有办法让 clang 像对待数据指针一样对待函数指针,以便我的代码能够编译?我很想知道它失败的程度(如果失败的话)。

相关代码:

#define Function_Cast(func_ref) (SubscriptionFunction*) func_ref
typedef void(SubscriptionFunction(void*, std::shared_ptr<void>));
typedef void(CallTypeFunction(std::shared_ptr<void>));

Subscriber(void* owner, SubscriptionFunction* func, bool serialized = true) {
    this->_owner = owner;
    this->_serialized = serialized;
    this->method = func;

    call = std::bind(&Subscriber::_std_call, this, std::placeholders::_1);
}

void _std_call(std::shared_ptr<void> arg) { method(_owner, arg); }

通常是这样的:

#include <functional>
#include <memory>

struct subscription
{
  // RAII unsubscribe stuff in destructor here....
};

struct subscribable
{
  subscription subscribe(std::function<void()> closure, std::weak_ptr<void> sentinel)
  {
    // perform the subscription

    return subscription {
      // some id so you can unsubscribe;
    };
  }


  //
  //

  void notify_subscriber(std::function<void()> const& closure, 
                         std::weak_ptr<void> const & sentinel)
  {
    if (auto locked = sentinel.lock())
    {
      closure();
    }
  }
};

这里的问题是您试图使用 成员函数 指针来代替函数指针,因为您知道,在幕后,它是通常实现为 function(this, ...).

struct S {
    void f() {}
};

using fn_ptr = void(*)(S*);

void call(S* s, fn_ptr fn)
{
    fn(s);
    delete s;
}

int main() {
    call(new S, (fn_ptr)&S::f);
}

http://ideone.com/fork/LJiohQ

但不能保证这会真正起作用,而且在明显的情况下(虚函数)它可能不会起作用。

成员函数应该像这样传递:

void call(S* s, void (S::*fn)())

并像这样调用:

(s->*fn)();

http://ideone.com/bJU5lx

当人们想要支持不同的类型时,他们如何解决这个问题是使用非成员函数 trampoline。您可以使用静态 [member] 函数或 lambda 来执行此操作:

auto sub = new Subscriber(this, [](auto* s){ s->init(); });

或者,如果您希望在调用站点实现类型安全,模板化构造函数:

template<typename T>
Subscriber(T* t, void(T::*fn)(), bool x);

http://ideone.com/lECOp6

如果您的 Subscriber 构造函数采用 std::function<void(void))> 而不是函数指针,您可以传递捕获 lambda 并消除采用 void*:

的需要
new Subscriber([this](){ init(); }, false);