【问题标题】:Pass member function as callback to constructor other class将成员函数作为回调传递给构造函数其他类
【发布时间】:2018-11-17 16:08:35
【问题描述】:

您好,我想将成员函数作为参数传递给其他类的构造函数。我尝试了很多解决方案,但都失败了。 所以.. 头等舱..

class WebSocketsServerRunner : public WebSocketsServer {
   private:
       ThreadController<uint8_t> threads = ThreadController<uint8_t>();
       SensorsState sensorsState;

    void notifyClient(uint8_t clientNumber) { // this is the callback
        String message = sensorsState.readAsJSON();
        WebSocketsServer::sendTXT(clientNumber, message);
    }

    void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {

        // solution bellow doesn't work!
        Thread<uint8_t>* thread = new Thread<uint8_t>(static_cast<int>(num),  &WebSocketsServerRunner::notifyClient, num, 5000);

        threads.add(thread);

    } 
};

和线程构造函数声明:

    Thread(int _id, void (*callback)(void) = NULL, long _interval = 0);
    Thread(int _id, void (*callback)(Arg), Arg arg, long _interval = 0);

Arg 是template &lt;typename Arg&gt;

我什么都试过了..std::bind、&CLASS_NAME::METHOD_NAME、static_cast 但没有任何效果..

编译给了我:

no known conversion for argument 2 from 'void (WebSocketsServerRunner::*)(uint8_t) {aka void (WebSocketsServerRunner::*)(unsigned char)}' to 'void (*)(unsigned char)'

...

no matching function for call to 'Thread&lt;unsigned char&gt;::Thread(int, void (WebSocketsServerRunner::*)(uint8_t), uint8_t&amp;, int)'

【问题讨论】:

  • 最大的问题在Thread类中,因为指向非静态成员函数的指针等于非成员函数指针.我建议你看看std::function 开始帮助你解决这个问题。继续使用std::bindlambdas 可能是明智的。
  • auto const bind = std::bind(&amp;WebSocketsServerRunner::notifyClient, this, std::placeholders::_1); Thread&lt;uint8_t&gt;* thread = new Thread&lt;uint8_t&gt;(static_cast&lt;int&gt;(num), bind, num, 5000); 给我no known conversion for argument 2 from 'const std::_Bind&lt;std::_Mem_fn&lt;void (WebSocketsServerRunner::*)(unsigned char)&gt;(WebSocketsServerRunner*, std::_Placeholder&lt;1&gt;)&gt;' to 'void (*)(unsigned char)' :(
  • 可以修改Thread吗?否则,您可能无法做到这一点。如果Thread 需要一个函数指针,我不知道将您的对象绑定到此方法。
  • 我可以修改 ;) 但如何修改? std::function 而不是函数指针?
  • 经典方法是将指向您的类的指针传递到回调的参数中,并从静态回调中调用您的成员函数。问题是当你的线程需要一个没有你的 this 指针的静态签名时,编译器正在为你做这件事。但实际上你为什么不使用允许你使用函数对象的 std::thread ,这意味着你可以根据需要使用 std::function 和 std::bind ?

标签: c++ compiler-errors callback std functor


【解决方案1】:

指向成员函数的指针与普通函数指针的语法不同:

void(*function)();                  // pointer to function
void(YourClass::*memberFunction)(); // pointer to member function

背后的原因是成员函数有一个附加的隐藏/透明参数,接受调用该函数的对象,并以this 指针的形式呈现给您。

所以调用成员函数也不同(你需要一个对象来调用函数):

void(YourClass::*memberFunction)() = &YourClass::someFunction;
YourClass instance;
YourClass* pointer = &instance;
(instance.*memberFunction)();
(pointer->*memberFunction)();

请注意,.*-&gt;* 运算符的优先级都低于函数调用运算符 ()(如果这是一个好的决定可能值得商榷,但它就是这样......),所以你 需要括号。

下面我展示只是为了说明成员函数的性质,请注意它实际上是未定义的行为(但它很可能会起作用) - 永远不要在生产性代码中使用它:

void(*functionR)(YourClass&)
    = reinterpret_cast<&void(*)(YourClass&)(&YourClass::someFunction);
void(*functionP)(YourClass*)
    = reinterpret_cast<&void(*)(YourClass*)(&YourClass::someFunction);
YourClass instance;
YourClass* pointer = &instance;
functionR(instance);
functionP(pointer);

有趣的方面是引用和指针都可以解决问题,这表明在幕后,引用——如果被实例化的话——也只不过是指针,只是应用了特定的规则(所以最后,语法糖)。

不过,上面也揭示了调用成员函数,即使你非法使用普通的函数指针,你仍然需要一个可以调用函数的对象!

那么有哪些选项?

static 成员函数没有透明的this 函数参数,即。 e.它们可以像任何普通函数一样使用:

static void notifyClient(uint8_t clientNumber)
// ^^^ (!)
{
     WebSocketsServerRunner& instance = selectById(clientNumber);
     String message = instance.sensorsState.readAsJSON();
     WebSocketsServer::sendTXT(clientNumber, message);
}

如果您无法使用客户端编号获取实例,则必须修改 Thread 类,使其也包含调用函数的对象。

大多数通用,但不一定是最安全的,使用void* 指针:

class Thread
{
    void(*function)(void*);
    void* parameter;

     void run()
     {
         function(parameter);
     }
};

您现在可以使用静态成员和普通函数,但是您需要将参数转换回以获取实例,此外您不能使用智能指针进行安全内存管理。更好的方法:

template <typename T> 
class Thread
{
    std::function<void(T*)>;
    std::shared_ptr<T> instance;

     void run()
     {
         function(instance.get());
     }
public:
     template <typename F>
     Thread(F function, std::shared_ptr<T>& instance)
         : function(function), instance(instance)
     { }
};

其中有趣的部分是您可以在 std::function 对象中存储指向成员函数的指针以及独立函数。另一个变种:

template <typename T> 
class Thread
{
    T t;

     void run()
     {
         t();
     }
public:
     Thread(T&& t)
         : t(std::move(t))
     { }
};

这个可以用 lambdas 喂:

Thread t([&instance]() { instance.doSomething(); });

直接使用std::thread 可能是一个有趣的替代方案:

class WebSocketsServerRunner
{
public:
    static void run(uint8_t clientNumber)
    {
        WebSocketsServerRunner instance;
        for(;;)
        {
            // do whatever your Thread class did
        }
    }
};

std::thread t10(&WebSocketsServerRunner::run, 10U);
std::thread t12(&WebSocketsServerRunner::run, 12U);

编辑:根据你的 cmets,不适合...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 2020-02-25
    • 2020-11-19
    • 2011-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多