5

I'm trying to pass a member function as a function pointer so that I don't need to rely on singletons or global functions to handle Qt messages in Qt 5. As far as I can tell my std::function is of the correct type, it has the correct signature, and bind should be allowing me to jam in the implicit this pointer, essentially passing off a member function as a global/un-owned function.

void ProgramMessageHandler::setAsMessageHandlerForProgram() {
    std::function<void(QtMsgType, const QMessageLogContext &, const QString &)> funcPtr;

    funcPtr = std::bind(handleMessages, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);

    qInstallMessageHandler(funkPtr);
}

This doesn't compile. I can succesfully create my funcPtr variable but passing it into the qInstallMessageHandler function causes the following:

log\ProgramMessageManager.cpp:16: error: cannot convert 'std::function<void(QtMsgType, const QMessageLogContext&, const QString&)>' to 'QtMessageHandler {aka void (*)(QtMsgType, const QMessageLogContext&, const QString&)}' for argument '1' to 'void (* qInstallMessageHandler(QtMessageHandler))(QtMsgType, const QMessageLogContext&, const QString&)'
 oldHandler = qInstallMessageHandler(hackedPointerToHandleFunction);
                                                                  ^

I have read:

how to pass a member function as a function pointer?

How do you pass a member function pointer?

How to pass a member function as a parameter to a function that doesn't expect it?

Get function pointer from std::function when using std::bind

but none have helped me.

EDIT:

So it has been said that this isn't possible without a singleton or a global funtion... but why? The only difference between a member function and a global function with the same signature is that they don't have the same signature, there is an implicit this pointer as the first parameter to a member function.

Knowing that, why cannot I use std::bind to bridge this gap, by saying 'I know that the function being called takes the this argument before all of the others, force it in there, be the thing that knows about this. That would be so much cleaner than a singleton and a global function is just crap.

Community
  • 1
  • 1
Troyseph
  • 4,960
  • 3
  • 38
  • 61
  • A function pointer (unlike a function object) can have no state of its own so there is nowhere to "jam" in a `this` pointer. – Chris Drew Sep 15 '15 at 11:27
  • @ChrisDrew but isn't my `std::function` keeping track of it? I was under the impression that `std::bind` was able to bind together things that were not the same by keeping track of *extra* information, e.g. a `this` pointer... – Troyseph Sep 15 '15 at 11:30
  • Yes, a `std::function` could keep track of it. But `qInstallMessageHandler` doesn't take a `std::function` it takes a function pointer. – Chris Drew Sep 15 '15 at 11:31
  • @ChrisDrew so is there not a function pointer to a function inside the `std::function` which when called relays the call ++additional information to my member function? – Troyseph Sep 15 '15 at 11:46
  • What you are trying to do is not possible without singletons or global functions. – Chris Drew Sep 15 '15 at 11:49
  • @ChrisDrew I still can't see why it shouldn't be possible, see my edit =] – Troyseph Sep 15 '15 at 12:00
  • 2
    Bind works syntactically different from what you want, since it returns an object and not a function pointer. You can store the memberFunction + instance pair using bind, but in order to activate the memberFunction call you have to call a member operator () on the std::function object, which is different that calling a function through a function pointer. That is why your compiler complains. Theoretically you could store the call to operator () inside another function X and pass X to qInstallMessageHandler, but again, there you have a global function that you wanted to avoid. – Anorflame Sep 15 '15 at 14:04

2 Answers2

3

You are trying to pass a class instance where a function pointer is expected, while no such conversion exists.

You could do something like this:

class ProgramMessageHandler
{
  static void  myMessageHandler(QtMsgType, const QMessageLogContext &, const QString &);
};

in main.cpp (for example):

...
qInstallMessageHandler(ProgramMessageHander::myMessageHandler);
...

You still might have to deal with some issues as in singleton classes, but I think it makes sense that there is one message handler or at least one dispatcher that would somehow redirect different message to appropriate handlers. Hope this helps

Anorflame
  • 376
  • 3
  • 12
  • I was hoping that by using `std::bind` I could expose my instance function pointer as a function pointer and then shoehorn in the `this` argument after the call. After all `std::function` has the correct function signature (i.e. not an instance signature, as far as I can tell). – Troyseph Sep 15 '15 at 11:13
  • @Troyseph `std::bind` does not return a function pointer it returns a function object. – Chris Drew Sep 15 '15 at 11:29
  • @ChrisDrew from which I can't get a function pointer? is my issue that my `std::function` cannot be a tempory? – Troyseph Sep 15 '15 at 11:32
  • 1
    @Troyseph: No, if that was possible the C++ Standard wouldn't have bothered to introduce `std::function`, and `std::bind` would have returned a function pointer straight away. Heck, a _member_ function pointer usually is bigger than an ordinary function pointer anyway as it needs to have `virtual`-ness information embedded. It couldn't even fit. – MSalters Sep 15 '15 at 12:08
2

The result of std::bind is a function object and, as the error message says, you cannot convert a function object to a function pointer. A function pointer cannot store the this pointer like std::function can, you can't just "force it in there".

The only way to do something approaching what you want is to use a singleton to store the this pointer:

class ProgramMessageHandler {
private:
  static ProgramMessageHandler* currentHandler;
  void handleMessagesImpl(const std::string& message);
public:
  void setAsMessageHandlerForProgram(){
    currentHandler = this;
    qInstallMessageHandler(handleMessages);
  }
  static void handleMessages(const std::string& message) { 
    if (currentHandler) 
      currentHandler->handleMessagesImpl(message);
  }
};

ProgramMessageHandler* ProgramMessageHandler::currentHandler = nullptr;

int main() {
  ProgramMessageHandler handler;
  handler.setAsMessageHandlerForProgram();

  // trigger messages...
}

Live demo.

Chris Drew
  • 14,926
  • 3
  • 34
  • 54
  • Almost exactly the solution I settled on, why Qt is using C style syntax in a 2015 C++ library beats me... very annoying! – Troyseph Sep 16 '15 at 10:11
  • I guess that what I was really hoping for was that `function` object owned a function pointer, that I could pass as normal, which was bound to my member function through `bind` shenanigans... alas tis not the case. – Troyseph Sep 16 '15 at 10:14
  • 1
    To your question regarding why do they use such syntax, I would assume just because it saved them the work of rewriting tons of source code maybe... qInstallMessageHandler is a replacement for an older function, qInstallMsgHandler, which was introduced in qt 4.8, which was released in December 2011, only a couple of months after C++ 11 was approved (August 2011) . – Anorflame Sep 16 '15 at 20:59
  • The "why" is easy. Qt supported building with compilers w/o C++/11 support until quite recently, I _think_ 5.7 was the first to require that. And within a release cycle, source compatibility is promised, so it cannot be modified before Qt 6. And then there is still discussion if Qt can use std like std::function in its ABI, because of binary compatibility concerns. – André Mar 21 '17 at 05:11