C ++: адрес функции-члена (указатели на функции) - PullRequest
3 голосов
/ 30 июля 2011

У меня есть класс X , который имеет этот метод:

void setRxHandler(void (*h)(int));

И я хочу передать ему функцию-член, которая существует в экземплярах класса Y .

void comm_rxHandler(int status);

Я попробовал следующее:

x.setRxHandler(comm_rxHandler) 

Но он получает следующую ошибку компиляции (я использую Qt):

ошибка: нет подходящей функции для вызова ‘X :: setRxHandler (<неразрешенный тип перегруженной функции>)’

Итак, как я могу это сделать?

Я заметил, что если я объявлю comm_rxHandler (класс Y) как статический, у меня нет ошибок. Но я хочу использовать comm_rxHandler как нестатический метод. Также я хочу, чтобы метод setRxHandler (класс X) был универсальным, а не специфичным для класса. Поэтому я не могу объявить этот метод как:

setRxHandler(void (Y::*h)(int))

Как это сделать? Можете ли вы помочь мне в этом?

Спасибо!

Ответы [ 5 ]

1 голос
/ 30 июля 2011

Измените свой обратный вызов на это:

void setRxHandler(std::function(<void(int)>);

Тогда вы можете использовать связующие:

setRxHandler( std::bind(&class_name::comm_rxHandler, obj) );

(std::function и std::bind являются частью следующей версии стандарта C ++. Вполне вероятно, что ваш компилятор уже поставляется с ними. В противном случае они могут жить в пространстве имен std::tr1. Если все остальное не сработает, вы найдете их в boost - где они были изобретены - как boost::function и boost::bind.)

Однако вы также можете передавать не * или статические функции в setRxHandler, а также функциональные объекты (что является результатом std::bind).

Если ваш компилятор уже поддерживает лямбда-функции (также часть следующего стандарта, но уже поддерживается, например, последними версиями GCC и VC), вы также можете использовать одну из них:

setRxHandler( [](){obj.comm_rxHandler();} );
1 голос
/ 30 июля 2011

C ++ не поддерживает связанные методы .Чтобы вызвать функцию-член через указатель на функцию, вам нужно иметь две вещи: экземпляр класса и указатель на функцию.

Так что setRxHandler(void (Y::*h)(int)) почти правильно.Вам нужно объявить его как:

void setRxHandler(Y*, void (Y::*h)(int));

Чтобы вызвать setRxHandler(), вам нужно передать ему аргументы следующим образом:

Y y;
setRxHandler(&y, &Y::comm_rxHandler);

В методе setRxHandler() вы можете вызватьуказатель на функцию с использованием этого синтаксиса:

void setRxHandler ( Y* y, void (Y::*h)(int) )
{
    // ...
    (y->*h)(0);
    // ...
}

Чтобы сделать универсальный, вам нужно абстрагировать параметр Y, но это трудно сделать правильно.Проверьте Boost.Function для существующей реализации, которая поддерживает этот вариант использования, и многие другие.

0 голосов
/ 30 июля 2011

Как уже упоминали другие, C ++ не предоставляет эту функциональность.

Другой вариант, который вы можете использовать, это libsigc ++ , который широко используется в gtkmm, см. этот пример в их руководстве, например, о том, как передавать указатели на функции-члены.Ваш пример может выглядеть примерно так:

// sigc::slot<void, int> is a 'slot' to hold a function with return type void 
// and 1 int argument.
void setRxHandler(sigc::slot<void, int> slot);
void comm_rxHandler(int status);

//sigc::mem_fun() can convert a member function to a function slot.
x.setRxHandler(sigc::mem_fun(*this, &X::comm_rxHandler));
0 голосов
/ 30 июля 2011

Вы можете либо создать базовый класс для Y и использовать его (чтобы не быть "специфичным для класса"), либо использовать шаблоны:

template <class T>
setRxHandler(void (T::*h)(int));

Но тогда это может вызвать вопросы о том, как использоватьфункция-член (вы сообщите нам, если это так).

0 голосов
/ 30 июля 2011

Как и сейчас, прототип setRxHandler получает указатель на функцию, которая ничего не возвращает, и принимает int.Как вы заметили, это не будет работать с функциями-членами, потому что они не могут быть вызваны как обычные функции (вы должны также обработать указатель this, что означает наличие экземпляра этого класса для вызова метода на).

Чтобы он работал как с функциями-членами, так и с неспецифическими ( generic ), вы должны либо создать базовый класс, и иметь все классы, которые вы хотите использовать setRxHandler спроизводные от этого класса:

class Base { ... };
class Derived : public Base { ... };

// then for the prototype
void setRxHandler(void (Base::*h)(int)) { ... }
// and you can use setRxHandler for all types that derive from Base, which gives you more control than the second option, which is:

или использование шаблонов:

template<typename T>
void setRxHandler(void (T::*h)(int)) { ... }

С помощью опции шаблона вы действительно не можете контролировать, какой класс будет использоваться с setRxHandler (исключая RTTI), что может быть именно тем, что вы хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...