C ++ неверная ссылка проблема - PullRequest
2 голосов
/ 27 марта 2010

Я пишу некоторую реализацию обратного вызова в C ++.

У меня есть абстрактный класс обратного вызова, скажем:

/** Abstract callback class. */
class callback {
public:

    /** Executes the callback. */
    void call() { do_call(); };
protected:

    /** Callback call implementation specific to derived callback. */
    virtual void do_call() = 0;
};

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

/** Makes the callback a single-argument callback. */
template <typename T>
class singleArgumentCallback {
protected:
    /** Callback argument. */
    T arg;

public:
    /** Constructor. */
    singleArgumentCallback(T arg): arg(arg) { }
};

/** Makes the callback a double-argument callback. */
template <typename T, typename V>
class doubleArgumentCallback {
protected:
    /** Callback argument 1. */
    T arg1;

    /** Callback argument 2. */
    V arg2;

public:
    /** Constructor. */
    doubleArgumentCallback(T arg1, V arg2): arg1(arg1), arg2(arg2) { }
};

Например, обратный вызов функции с одним аргументом будет выглядеть так:

/** Single-arg callbacks. */
template <typename T>
class singleArgFunctionCallback:
    public callback,
    protected singleArgumentCallback<T> {

    /** Callback. */
    void (*callbackMethod)(T arg);

public:
   /** Constructor. */
    singleArgFunctionCallback(void (*callback)(T), T argument):
        singleArgumentCallback<T>(argument),
        callbackMethod(callback) { }

protected:
    void do_call() {
        this->callbackMethod(this->arg);
    }
};

Для удобства пользователя мне бы хотелось иметь метод, который создает обратный вызов без необходимости думать о деталях, чтобы можно было вызывать (к сожалению, этот интерфейс не подлежит изменению):

void test3(float x) { std::cout << x << std::endl; }
void test5(const std::string& s) { std::cout << s << std::endl; }

make_callback(&test3, 12.0f)->call();
make_callback(&test5, "oh hai!")->call();

Моя текущая реализация make_callback(...) выглядит следующим образом:

/** Creates a callback object. */
template <typename T, typename U> callback* make_callback(
    void (*callbackMethod)(T), U argument) {
    return new singleArgFunctionCallback<T>(callbackMethod, argument);
}

К сожалению, когда я звоню make_callback(&test5, "oh hai!")->call();, я получаю пустую строку в стандартном выводе. Я полагаю, что проблема заключается в том, что ссылка выходит из области видимости после инициализации обратного вызова.

Я пытался использовать указатели и ссылки, но невозможно получить указатель / ссылку на ссылку, поэтому я потерпел неудачу. Единственное решение, которое у меня было, - запретить замену ссылочного типа на T (например, T не может быть std :: string &), но это печальное решение, поскольку мне нужно создать еще один класс singleArgCallbackAcceptingReference, принимающий указатель на функцию со следующей подписью:

void (*callbackMethod)(T& arg);

Таким образом, мой код дублируется 2 ^ n раз, где n - количество аргументов функции обратного вызова.

Кто-нибудь знает обходной путь или знает, как его исправить? Заранее спасибо!

Ответы [ 3 ]

3 голосов
/ 27 марта 2010

Проблема в том, что в make_callback(), T становится const std::string&, что в свою очередь становится T в вашем singleArgumentCallback. U, однако, является const char*, поэтому временный объект std::string создается и привязывается к этой ссылке в singleArgumentCallback. Когда make_callback() заканчивается, этот временный объект уничтожается, и созданный объект singleArgumentCallback остается со ссылкой на более не существующий объект.

Что вам нужно будет сделать, это сначала удалить ссылки (и, возможно, квалификаторы cv) из типов, переданных в make_callback(). Как Марсело предложил , Boost.TypeTraits может помочь вам в этом, но если вы хотите, то не сложно приготовить что-то самостоятельно:

template< typename T > struct remove_ref     { typedef T result_type; };
template< typename T > struct remove_ref<T&> { typedef T result_type; };

Затем измените make_callback() на:

template <typename T, typename U>
callback* make_callback(void (*callbackMethod)(T), U argument)
{
    typedef typename remove_ref<T>::result_type  arg_type;
    return new singleArgFunctionCallback<arg_type>(callbackMethod, argument);
}
0 голосов
/ 28 марта 2010

Благодаря sbi , у меня все работает: -)

Решение, с которым я столкнулся, здесь:

template <typename T> struct removeRef     { typedef T resultType; };
template <typename T> struct removeRef<T&> { typedef T resultType; };

/** Single-arg callbacks. */
template <typename T, typename U>
class singleArgFunctionCallback:
    public callback,
    protected singleArgumentCallback<U> {

    /** Callback. */
    void (*callbackMethod)(T arg);

public:
    /** Constructor. */
    singleArgFunctionCallback(void (*callback)(T), U argument):
        singleArgumentCallback<U>(argument),
        callbackMethod(callback) { }

protected:
    void do_call() {
        this->callbackMethod(this->arg);
    }
};

template <typename T, typename U>
callback* make_callback(void (*callbackMethod)(T), U argument) {
    typedef T ArgumentType;
    typedef typename removeRef<T>::resultType StrippedArgumentType;
    return new singleArgFunctionCallback<ArgumentType, StrippedArgumentType>(callbackMethod, argument);
}

Если кто-нибудь увидит какие-либо возможные улучшения, я буду рад узнать!

Спасибо всем, Karol

0 голосов
/ 27 марта 2010

Boost.TypeTraits может помочь. add_reference преобразует конкретные типы в ссылочные типы, оставляя ссылочные типы как есть.

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