J'écris une implémentation de rappel en C++.Problème de référence invalide C++
J'ai une classe de rappel abstraite, disons:
/** 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;
};
Chaque rappel que je crée (accepter des fonctions monoargumental, fonctions double arguments ...) est créé comme un mixin en utilisant une des options suivantes :
/** 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) { }
};
Par exemple, un rappel de la fonction unique arg ressemblerait à ceci:
/** 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);
}
};
Pour un confort d'utilisation, je voudrais avoir une méthode qui crée un rappel sans avoir l'utilisateur pense sur les détails, de sorte que l'on peut appeler (cette interface est pas sujette au changement, malheureusement):
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();
Mon la mise en œuvre actuelle de make_callback(...)
est la suivante:
/** Creates a callback object. */
template <typename T, typename U> callback* make_callback(
void (*callbackMethod)(T), U argument) {
return new singleArgFunctionCallback<T>(callbackMethod, argument);
}
Malheureusement, quand je l'appelle make_callback(&test5, "oh hai!")->call();
je reçois une chaîne vide sur la sortie standard. Je crois que le problème est que la référence sort de la portée après l'initialisation du rappel.
J'ai essayé d'utiliser des pointeurs et des références, mais il est impossible d'avoir un pointeur/référence à référence, donc j'ai échoué. La seule solution que j'avais était d'interdire le remplacement du type de référence par T (par exemple, T ne peut pas être std :: string &) mais c'est une solution triste car je dois créer une autre classe singleArgCallbackAcceptingReference acceptant un pointeur de fonction avec la signature suivante:
void (*callbackMethod)(T& arg);
Ainsi, mon code est dupliqué 2 fois, où n est le nombre d'arguments d'une fonction de rappel.
Est-ce que quelqu'un sait une solution de contournement ou a une idée de comment résoudre ce problème? Merci d'avance!
"J'essayé d'utiliser des pointeurs et des références, mais il est impossible d'avoir un pointeur/référence à la référence": 'int a = 1; int & a_ref = a; int * p_a_ref = &a_ref; (* p_a_ref) = 2; ' – mlvljr
@mlvljr: Est-ce supposé être un pointeur vers une référence? Ce n'est pas. Le pointeur pointe sur 'a'. Vous ne pouvez simplement pas pointer vers une référence. – GManNickG
@GMan "Est-ce supposé [d] être un pointeur vers une référence?" - bien sûr que non, cela montre juste comment obtenir [facilement] un pointeur vers quelque chose, bien, référencé par une référence (une chose que OP était probablement prêt à avoir mais ne savait pas), c'est-à-dire juste une autre) niveau d'indirection, rien d'autre. – mlvljr