简体   繁体   English

将成员函数作为回调传递给构造函数的其他类

[英]Pass member function as callback to constructor other class

Hi I want to pass member function as argument to constructor other class. 嗨,我想将成员函数作为参数传递给构造函数其他类。 I tried lot solutions but all given me fail. 我尝试了很多解决方案,但都给我失败了。 So.. First class.. 所以..头等舱..

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

    } 
};

And Thread constructor declaration: 和线程构造函数的声明:

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

Where Arg is template <typename Arg> 其中Arg是template <typename Arg>

I tried everything.. std::bind, &CLASS_NAME::METHOD_NAME, static_cast but nothing works.. 我尝试了所有方法.. std :: bind,&CLASS_NAME :: METHOD_NAME,static_cast,但没有任何效果。

Compile gives me : 编译给我:

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<unsigned char>::Thread(int, void (WebSocketsServerRunner::*)(uint8_t), uint8_t&, int)'

Pointers to member functions have different syntax than ordinary function pointers: 指向成员函数的指针与普通函数指针的语法不同:

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

Reason behind is that member functions have an additional, hidden/transparent parameter accepting the object the function is called on and is presented to you as this pointer. 其背后的原因是成员函数还有一个附加的,隐藏/透明的参数,该参数接受调用该函数并以this指针的形式显示给您的对象。

So calling a member function differs as well (you need an object to call the function on): 因此,调用成员函数也有所不同(您需要一个对象才能调用该函数):

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

Be aware that both .* and ->* operators have lower precedence than function call operator () (if it was a good decision might be arguable, but it is as it is...), so you need the parentheses. 请注意, .*->*运算符的优先级均低于函数调用操作符() (如果可以做出正确的决定,这是可以争论的,但实际上是……),因此您需要使用括号。

The following I show only for illustration of the nature of member functions, be aware that it actually is undefined behaviour (but it is very likely to work) – don't ever use this in productive code : 下面我显示的成员函数性质的说明 ,要知道,它实际上是不确定的行为 (但它是非常有可能的工作) - 永远不要使用在生产代码

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

The interesting aspect of is that both reference and pointer should do the trick, revealing that under the hoods, references – if instanciated at all – are nothing more than pointers as well, solely with specific rules applied (so in the end, syntactic sugar). 有趣的方面是,引用和指针都应该发挥作用,这表明在幕后,引用(如果被实例化的话)也不过是指针,而仅仅是应用了特定的规则(因此,最后是语法糖) 。

Above reveals as well, though, that to call a member function, even if you illegally use an ordinary function pointer, you still need an object on which you can call the function! 上面还显示了调用成员函数,即使您非法使用普通函数指针,您仍然需要一个可以调用该函数的对象!

So which are the options? 那有哪些选择呢?

static member functions do not have the transparent this function parameter, ie they can be used just like any ordinary function: static成员函数没有透明的this函数参数,即,它们可以像任何普通函数一样使用:

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

If you cannot get the instance using the client number, you'll have to modify the Thread class such that it holds the object to call the function on as well. 如果您无法使用客户端编号获取实例,则必须修改Thread类,使其包含用于调用该函数的对象。

Most generic , not necessarily the safest one, though, is using a void* pointer: 不过,最通用的 (不一定是最安全的)使用的是void*指针:

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

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

You now could use static member and normal functions, but you need to cast the parameter back to get the instance, additionally you cannot use smart pointers for safe memory management. 现在,您可以使用静态成员函数和普通函数,但是您需要将参数强制转换回以获得实例,此外,您不能使用智能指针进行安全的内存管理。 Better approach: 更好的方法:

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

The interesting part of is that you can store both pointer to member function as well as free-standing functions in the std::function object. 有趣的部分是您可以将指向成员函数的指针以及独立函数存储在std :: function对象中。 Another variant: 另一个变体:

template <typename T> 
class Thread
{
    T t;

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

This one could be feeded with lambdas: 这个可以喂lambda:

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

It possibly might be an interesting alternative to use std::thread directly: 直接使用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); 

Edit: according to your comments, not suitable... 编辑:根据您的评论,不合适...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM