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

时间:2018-11-17 16:08:36

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

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

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 <typename Arg>

我尝试了所有方法。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<unsigned char>::Thread(int, void (WebSocketsServerRunner::*)(uint8_t), uint8_t&, int)'

1 个答案:

答案 0 :(得分:0)

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

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

请注意,.*->*运算符的优先级都低于函数调用运算符()(如果是一个好的决定,可能会争论不休,但实际上是这样。) 。),因此需要括号。

下面我仅出于说明作用展示成员函数的性质,请注意,这实际上是未定义的行为(但很可能起作用)– < strong>永远不要在生产性代码中使用它们:

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

这可以喂lambda:

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

编辑:根据您的评论,不适合...