Вот уродливый хак, который я изобрел некоторое время назад , чтобы решить эту проблему:
#include <boost/function.hpp>
#include <boost/bind.hpp>
using ::boost::function;
using ::boost::bind;
typedef int (*callback_t)(const char *, int);
typedef function<int(const char *, int)> MyFTWFunction;
template <MyFTWFunction *callback>
class callback_binder {
public:
static int callbackThunk(const char *s, int i) {
return (*callback)(s, i);
}
};
extern void register_callback(callback_t f);
int random_func(const char *s, int i)
{
if (s && *s) {
return i;
} else {
return -1;
}
}
MyFTWFunction myfunc;
class FooClass {
public:
virtual int callme(const char *s, int x) { return 0; };
};
int main(int argc, const char *argv[])
{
FooClass foo;
myfunc = bind(&FooClass::callme, &foo, _1, _2);
register_callback(&callback_binder<&myfunc>::callbackThunk);
return 0;
}
Возможно, это можно исправить, чтобы использовать вещи из TR1 и удалить зависимость от Boost.
И, конечно же, myfunc
- это глобальная переменная. Это должна быть глобальная переменная. У вас должна быть одна глобальная переменная для каждого возможного объекта, к которому вы хотите обратиться. OTOH, вы можете иметь столько глобалов, сколько захотите.
Основная проблема здесь заключается в том, что абсолютно невозможно делать то, что вы хотите в данных ограничениях. Указатель на объект, в который вы хотите перезвонить, должен прийти откуда-то. В некоторых языках (например, в Python) вы можете создать функцию «на лету», которая имеет собственную копию указателя объекта. Это не может быть сделано в C ++. Все функции должны существовать полностью во время компиляции. Вы не можете создавать новые экземпляры функций во время выполнения.
С C ++ 0x вы можете сортировать функции во время выполнения с помощью лямбда-функций. Но эти функции имеют неопределенный тип, и нет абсолютно никакого способа, которым вы могли бы когда-либо передать их функции C и заставить это работать. Лямбда-выражения предназначены для предоставления в качестве параметров шаблона, и их довольно сложно использовать для чего-либо еще, поскольку их адрес не может быть получен, и даже если бы вы могли, вы не могли бы знать, на какой тип указывает указатель.
Я настоятельно рекомендую не использовать его. Маленькие void *
большинство интерфейсов обратного вызова позволяют вам указать, что передача вам вместе с данными предназначена для хранения какого-либо указателя объекта. Если возможно, вы должны сделать это вместо этого.