C ++, передавая указатель на метод в качестве аргумента шаблона - PullRequest
4 голосов
/ 08 декабря 2010

У меня есть функция вызывающего абонента, подобная этой:

template<typename T, void (T::*method)()>
void CallMethod(T *object){
    (object->*method)(args);
}

, и хотя она отлично работает:

void (*function)(A *) = &CallMethod<A, &A::method>;

этот код не компилируется с ошибкой во второй строке:

void (A::*method)() = &A::method;
void (*function)(A *) = &CallMethod<A, method>;

Есть ли способ это исправить?Мне нужен шаблон CallMethod для получения постоянного указателя на метод, который хранится в переменной.

Ответы [ 5 ]

2 голосов
/ 08 декабря 2010

На данный момент я установил, что у вас есть API, который принимает функцию и указатель, и вам нужно предоставить его таким.Я предполагаю, что вы всегда должны указывать ему указатель A *

Если это очень общий обратный вызов, но должен быть функцией (не может быть boost :: function) и должен быть указателем (возможно, void*) тогда вам нужна функция, подобная этой:

struct GenericHolder
{
   boost::function0<void> func;
};

void GenericCallback( void * p )
{
   GenericHolder * holder = static_cast< GenericHolder * >(p);
   holder->func();
   delete holder;
}

Как и в этом случае, я вызываю delete во время вызова, поэтому я предполагаю, что мы вызываем new во время вызова, т.е.указатель на который сделан ваш звонок.Конечно, это может быть не так, что ваш указатель, который вы передаете, удаляется при первом вызове, поэтому управляйте временем жизни соответствующим образом.

Если вы контролируете «другую сторону», то не делайте этого намеренно, нона этой стороне удерживайте boost :: function и просто вызывайте ее.

Управление памятью все еще может быть проблемой, о которой вам нужно позаботиться.Например, когда вы вызываете boost :: bind, он оборачивает вещи в закулисную структуру.Если это указатели, которые вы наделили новыми, вам нужно как-то удалить их.Если они являются ссылками, они все равно должны быть действительными в точке вызова.указатели на локальные переменные также могут быть проблемой.shared_ptrs идеальны, конечно.И очень сложно найти отслеживание ошибок в ошибке boost :: bind, если вы часто используете эту концепцию.

2 голосов
/ 08 декабря 2010

Все параметры шаблона должны быть известны во время компиляции. Так что, если method действительно переменная, а не конкретная функция, нет способа сделать то, что вы хотите.

Вместо этого вы можете создать template struct, который имеет указатель на функцию-член в качестве члена и реализует operator(), который действует аналогично CallMethod<A, method>.

1 голос
/ 08 декабря 2010

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

Заголовок:

class AbstractBase
{
public:
    virtual void func() = 0;
}
class SubClass : public AbstractBase
{
public:
    void func();
}

Исходный файл:

void SubClass::func()
{
    std::cout << "Virtual function called.\n";
}

Пример использования:

int main()
{
    AbstractBase* base;
    base = new SubClass();
    base->func(); // virtual function call through base pointer will use SubClass's implementation
    return 0;
}

Вы можете хранить вектор указателей (или умных указателей, если вы используете boost или C ++ 0x), который вы можете просмотреть и использовать для выполнения всех видов зависимых от подклассов вещей.

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

ОБНОВЛЕНИЕ : видя, что вам нужен простой указатель на функцию C, есть несколько приемов, которые могут представить производительность во время выполнения в c ++ 0x или boost: bind, function::target и так далее ...

Вам понадобится , чтобы получить указатель на функцию из std/boost::function, и , чтобы связатьпервый аргумент для объекта .bind действует во время выполнения, поэтому, возможно, шаблон будет лучше в этом случае ...

0 голосов
/ 09 мая 2014

Что если 2 метода имеют одинаковую сигнатуру?

Шаблон не может различить его (он выбирает по типу, а не по значению), и, следовательно, одна и та же сгенерированная функция шаблона вызовет первый метод или второй, вы бы поставили на него свою руку?

Вы можете использовать некоторые приемы для создания другого типа во время компиляции, используя макрос __ LINE __ , но он включает в себя смешивание макроса с текущим кодом, за которым будет трудно следовать. Этот код:

template<typename T, void (T::*method)(), int>
void CallMethod(T *) { ... }
#define MakeCallMethod(X, Y) &CallMethod<X, X::&Y, __LINE__>

// This will work (it does not without the __LINE__ trick, ptr1 == ptr2 in that case)
typedef void (*ptrFunc)(A *);
ptrFunc ptr1 = MakeCallMethod(A, method);
ptrFunc ptr2 = MakeCallMethod(A, otherMethodWithSameSignature);

Assert(ptr1 != ptr2);

Однако вы не сможете сохранить указатель на метод в переменной и создать из него «автоматическую обертку». Сейчас время выполнения, для этого вам нужно решение времени выполнения.

0 голосов
/ 08 декабря 2010

Сначала создайте typedefs http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.5

Во-вторых,

void (*function)(A *) = &CallMethod<A, method>;

В этом случае метод является переменной, а переменные не могут быть аргументами шаблона.Я не проверял это, но, возможно, вы могли бы попробовать ...

void (*function)(A *) = &CallMethod<A, void (A::*method)()>;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...