Как использовать привязку для передачи функции-члена в качестве указателя на функцию?

Я пытаюсь передать функцию-член в качестве указателя на функцию, чтобы мне не нужно было полагаться на синглтоны или глобальные функции для обработки сообщений Qt в Qt 5. Насколько я могу судить, мой std::function является правильным type, у него правильная сигнатура, а bind должен позволять мне вставлять неявный указатель this, фактически выдавая функцию-член за глобальную/не принадлежащую ему функцию.

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

Это не компилируется. Я могу успешно создать свою переменную funcPtr, но передача ее в функцию qInstallMessageHandler приводит к следующему:

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

Я прочитал:

как передать функцию-член в качестве указателя на функцию?< /а>

Как передать указатель функции-члена?

Как передать функцию-член в качестве параметра функции, которая этого не ожидает?

Получить указатель функции из std::function при использовании std::bind

но ни один мне не помог.

ИЗМЕНИТЬ:

Итак, было сказано, что это невозможно без синглтона или глобальной функции... но почему? Единственная разница между функцией-членом и глобальной функцией с одинаковой сигнатурой заключается в том, что они не имеют одинаковую сигнатуру, а неявный указатель this в качестве первого параметра функции-члена.

Зная это, почему я не могу использовать std::bind для преодоления этого разрыва, говоря: «Я знаю, что вызываемая функция принимает аргумент this перед всеми остальными, принудительно вставьте его туда, будьте той вещью, которая знает о this. Это было бы намного чище, чем синглтон, а глобальная функция — просто дерьмо.


person Troyseph    schedule 15.09.2015    source источник
comment
Указатель функции (в отличие от объекта функции) не может иметь собственного состояния, поэтому указатель this некуда впихивать.   -  person Chris Drew    schedule 15.09.2015
comment
@ChrisDrew, но разве мой std::function не отслеживает это? У меня сложилось впечатление, что std::bind может связывать воедино разные вещи, отслеживая дополнительную информацию, например. указатель this...   -  person Troyseph    schedule 15.09.2015
comment
Да, std::function может это отслеживать. Но qInstallMessageHandler не принимает std::function, он принимает указатель на функцию.   -  person Chris Drew    schedule 15.09.2015
comment
@ChrisDrew, так нет ли указателя функции на функцию внутри std::function, которая при вызове передает дополнительную информацию о вызове ++ моей функции-члену?   -  person Troyseph    schedule 15.09.2015
comment
То, что вы пытаетесь сделать, невозможно без синглетонов или глобальных функций.   -  person Chris Drew    schedule 15.09.2015
comment
@ChrisDrew Я до сих пор не понимаю, почему это должно быть невозможно, смотрите мое редактирование =]   -  person Troyseph    schedule 15.09.2015
comment
Bind работает синтаксически иначе, чем вы хотите, поскольку возвращает объект, а не указатель на функцию. Вы можете сохранить пару memberFunction + instance с помощью bind, но для активации вызова memberFunction вам необходимо вызвать оператор-член () для объекта std::function, что отличается от вызова функции через указатель на функцию. Вот почему ваш компилятор жалуется. Теоретически вы могли бы сохранить вызов оператора () внутри другой функции X и передать X в qInstallMessageHandler, но опять же, у вас есть глобальная функция, которой вы хотели избежать.   -  person Anorflame    schedule 15.09.2015


Ответы (2)


Вы пытаетесь передать экземпляр класса, где ожидается указатель на функцию, хотя такого преобразования не существует.

Вы можете сделать что-то вроде этого:

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

в main.cpp (например):

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

Вам все еще, возможно, придется иметь дело с некоторыми проблемами, как в одноэлементных классах, но я думаю, что имеет смысл иметь один обработчик сообщений или, по крайней мере, один диспетчер, который каким-то образом перенаправлял бы разные сообщения соответствующим обработчикам. Надеюсь это поможет

person Anorflame    schedule 15.09.2015
comment
Я надеялся, что с помощью std::bind я смогу выставить свой указатель функции экземпляра как указатель функции, а затем втиснуть аргумент this после вызова. В конце концов, std::function<void(QtMsgType, const QMessageLogContext &, const QString &)> имеет правильную сигнатуру функции (т.е. не сигнатуру экземпляра, насколько я могу судить). - person Troyseph; 15.09.2015
comment
@Troyseph std::bind не возвращает указатель на функцию, он возвращает объект функции. - person Chris Drew; 15.09.2015
comment
@ChrisDrew, из которого я не могу получить указатель на функцию? моя проблема в том, что мой std::function не может быть временным? - person Troyseph; 15.09.2015
comment
@Troyseph: Нет, если бы это было возможно, стандарт C++ не стал бы вводить std::function, а std::bind сразу бы вернул указатель на функцию. Черт возьми, указатель на функцию member обычно больше обычного указателя на функцию, так как в него должна быть встроена информация о virtual-ности. Он даже не мог подойти. - person MSalters; 15.09.2015

Результатом std::bind является объект функции, и, как говорится в сообщении об ошибке, вы не можете преобразовать объект функции в указатель функции. Указатель функции не может хранить указатель this, как std::function, вы не можете просто «заставить его туда».

Единственный способ сделать что-то близкое к тому, что вы хотите, — это использовать синглтон для хранения указателя this:

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...
}

Прямая демонстрация.

person Chris Drew    schedule 15.09.2015
comment
Почти точно решение, на котором я остановился, почему Qt использует синтаксис стиля C в библиотеке C++ 2015 года, бьет меня ... очень раздражает! - person Troyseph; 16.09.2015
comment
Я думаю, на что я действительно надеялся, так это на то, что объект function владел указателем на функцию, который я мог передать как обычно, который был привязан к моей функции-члену через bind махинаций... увы, это не так. - person Troyseph; 16.09.2015
comment
На ваш вопрос о том, почему они используют такой синтаксис, я бы предположил, что это спасло их от работы по переписыванию тонны исходного кода, возможно... был выпущен в декабре 2011 года, всего через пару месяцев после утверждения C++ 11 (август 2011 года). - person Anorflame; 16.09.2015
comment
Почему легко. Qt поддерживал сборку с помощью компиляторов без поддержки C++/11 до недавнего времени, я думаю 5.7 был первым, кто потребовал этого. И в цикле выпуска обещана совместимость с исходным кодом, поэтому он не может быть изменен до Qt 6. И затем все еще обсуждается, может ли Qt использовать std как std::function в своем ABI из-за проблем с двоичной совместимостью. - person André; 21.03.2017