Home > Mobile >  Passing a function from another class to a function
Passing a function from another class to a function

Time:12-11

The XSetErrorHandler() function from X11 lib expects a function.

If I define a function above my main() [in main.cpp] like:

int catch_error(Display *disp, XErrorEvent *xe){return 0;}

Then I do the following:

XSetErrorHandler(catch_error); 

It works.

How can I put the catch_error function in a class named WindowHandler and then pass it to XSetErrorHandler();

I tried the following:

WindowHandler window_handler = new WindowHandler();
XSetErrorHandler(WindowHandler::catch_error);
XSetErrorHandler(window_handler->catch_error);
XSetErrorHandler((*window_handler).catch_error);

Error for 1) argument of type "int (WindowHandler::*)(Display *disp, XErrorEvent *xe)" is incompatible with parameter of type "XErrorHandler"

Error for 2) and 3) A pointer to a bound function may only be used to call that fucntion

Thanks for your help!

CodePudding user response:

The problem is that a non-static member function expects an implicit first argument, which is a pointer or a reference to the class (in this case WindowHandler). So the signature differs from the free catch_error function, if we take the implicit argument into account.

I don't know how XSetErrorHandler is declared, but the function might accept functors: In this case, you could wrap the call to WindowHandler::catch_error in a lambda expression that captures a pointer to your WindowHandler instance.

WindowHandler window_handler = new WindowHandler();
XSetErrorHandler(
    [window_handler](Display *disp, XErrorEvent *xe) {
        return window_handler->catch_error(disp, xe);
    }
);
delete window_handler; // if you really must use new (why would you ever?), please also delete.

Here is some further reading on pointers to member functions: https://isocpp.org/wiki/faq/pointers-to-members

Edit:

I just realized, that XSetErrorHandler does not accept functors, but only function pointers https://tronche.com/gui/x/xlib/event-handling/protocol-errors/XSetErrorHandler.html. In this case, WindowHandler::catch_error must be static. You can only treat a lambda as a function pointer, if it does not capture anything.

If you really want to capture the error_flag in a member of WindowHandler, the only option I see for you is to have one global instance of WindowHandler (a so-called singleton). Then the static catch_error function can set the member of this singleton, no implicit this argument is required.

Note that global variables and singletons are discouraged and should be avoided if possible. There are some situations, where this is not possible. Logging and error handling are often in these scenarios.

#include <iostream>
#include <memory>

struct Display {};
struct XErrorEvent {};

void XSetErrorHandler(
      int (*handler)(Display *, XErrorEvent *)
) {}

class WindowHandler 
{
private:
    // constructor is private: No other class should be allowed to construct instances
    WindowHandler() = default;

    int catch_error_impl(Display*, XErrorEvent*) {
        std::cout << "Error happened\n";
        error_flag = 1;
        return 0;
    }  

public:
    // don't copy or move a singleton
    WindowHandler(WindowHandler const&) = delete;
    WindowHandler(WindowHandler&&) = delete;

    // the global WindowHandler singleton gets instantiated the first time the get_instance_ptr function is called
    static WindowHandler* get_instance_ptr() {
        // calling new in the argument of unique_ptr ctor is ok. memory is freed when the dtor is called
        static auto handler = std::unique_ptr<WindowHandler>(new WindowHandler());
        return handler.get();
    }

    static int catch_error(Display* disp, XErrorEvent* xe) {
        return get_instance_ptr()->catch_error_impl(disp, xe);
    } 

    int error_flag {0};
};

int main()
{
    XSetErrorHandler(WindowHandler::catch_error);
    std::cout << WindowHandler::get_instance_ptr()->error_flag << std::endl;
}

https://godbolt.org/z/MTWcfK3bs

  •  Tags:  
  • c
  • Related