2010-06-23 3 views
4

Je suis en train de concevoir une méthode pour une classe de gestionnaire d'entrée. Voici un code de pseudo ...Méthode efficace de transmission de la fonction de membre de rappel

void InputHandler::ScanEvents(boost::function1< void, std::string& > &func) { 
    // Scan keys, determining string to pass 
    // If string found, call func with string as its argument on object tied to func 
} 

Je ne suis pas sûr de savoir comment mettre en œuvre, ou s'il est même possible, puisque le point de l'ensemble d'une fonction est de séparer de son interlocuteur. L'idée est qu'un objet possède une fonction membre privée et un membre boost :: function qui le détient. Chaque fois qu'il appelle ScanEvents sur son InputHandler, il transmet cette fonction, de sorte que ScanEvents peut "l'activer" chaque fois qu'un événement approprié est trouvé.

L'efficacité est une préoccupation, car c'est dans un domaine où les performances sont importantes, et cette fonction est appelée fréquemment.

P.S. Je jure que je me souviens avoir lu un exemple comme celui-ci dans l'un des livres de Scott Meyer, mais je ne le trouve pas pour la vie de moi. Peut-être était-ce dans Modern C++ Design ... regardant ...

Répondre

2

Quelque chose le long des lignes de

class Thingy 
{ 
public: 
    Thingy() : callback(bind(&Thingy::Callback, this, _1)) {} 

    void DoStuff() 
    { 
     handler.ScanEvents(callback); 
    } 

private: 
    InputHandler handler; 
    function<void(string)> callback; 

    void Callback(string s) 
    { 
     cout << "Called with " << s << endl; 
    } 
}; 

doit faire ce que vous décrivez. Mais il serait probablement plus efficace pour le rappel de prendre la chaîne par référence.

+0

Test maintenant. – random

+0

A travaillé comme un charme. Mais une question. Pour une raison quelconque, boost :: function ne compilerait pas en tant que type de paramètre (message d'erreur: nombre incorrect d'arguments template), il n'accepterait que boost :: function . J'ai suivi la syntaxe portable listée à http://www.boost.org/doc/libs/1_43_0/doc/html/function/tutorial.html#id866854. Une idée de pourquoi ça n'a pas marché? – random

+1

Non, vous n'avez pas suivi la syntaxe portable. Regardez plus attentivement. Les modèles ne portent pas le même nom. –

0

Habituellement, appeler une méthode privée sur une autre classe n'est pas la meilleure idée.

Vous pouvez mettre en œuvre une classe de style d'interface et d'hériter que, et écrivez votre méthode ScanEvents d'attendre ce type d'entrée, par exemple:

class Notifyable { 
    public: 
    virtual void notify(std::string s) = 0; 
}; 

void InputHandler::ScanEvents(Notifyable &n) { 
    // Scan keys, determining string to pass 
     // If string found, call func with string as its argument on object tied to func 
     n.notify(<string>); 
} 

Je pense que cela pourrait être plus rapide aussi, juste parce que vous » re éviter tout frais supplémentaire avec l'enveloppe boost :: function.

+0

Vous remplacez la surcharge d'un appel 'boost :: function' par la surcharge d'un appel de fonction virtuelle. En pratique, ils sont susceptibles d'être plus ou moins les mêmes. –

+0

Ok - mais il y a du code supplémentaire à intégrer avec boost, et potentiellement une courbe d'apprentissage aussi. – sje397

3

boost::function a un temps système raisonnablement lent - environ 20ns per call. Je ne l'appellerais pas à partir d'une boucle interne - mais je n'appellerais pas non plus d'autre type de pointeur de fonction. Pour appeler une fonction membre, il suffit d'utiliser boost::lambda::bind pour construire une fonction enveloppe anonyme:

ih->ScanEvents(boost::lambda::bind(&ThisClass::CallbackFunc, this, boost::lambda::_1)); 

Une option si vous avez besoin vraiment, la performance très élevé serait d'utiliser une fonction de modèle inlinable avec un paramètre foncteur boost::lambda:

template<typename F> 
double democaller(const F &f) { 
    double x = 1; 
    for (int i = 0; i < 1000000; i++) { 
     x = f(x); 
    } 
    return x; 
} 

namespace l = boost::lambda; 

void demouser() { 
    std::cout << democaller(l::_1 + 1); 
} 

Sur des paramètres d'optimisation suffisamment élevés, l'expression lambda peut être insérée dans le site appelant, éliminant presque totalement le surdébit.

+0

Cependant, avec la méthode que vous avez d'abord publiée, vous liez dans la fonction. Si vous liez, dites dans le constructeur, et enregistrez-le en tant que membre, cela ne supprime-t-il pas les frais généraux de l'appel? C'est à dire. L'exemple de Mike Seymour. – random

+0

Oui, cela permet d'économiser un peu de frais généraux - il est cependant susceptible d'être très faible, du moins si vous n'avez qu'un petit nombre de paramètres fixes. Je voulais juste garder l'exemple simple :) – bdonlan

1

Vous recherchez peut-être l'implémentation de la commande dans Modern C++ Design.

La fonction boost :: function a tout ce qu'il faut pour faire ce qu'elle fait. Son but est de vous permettre de passer un "pointeur" vers n'importe quelle fonction pouvant répondre à son interface. Si vous n'avez pas besoin de ce comportement, cette surcharge est réelle. Si vous faites ainsi, honnêtement, je ne peux pas voir une meilleure approche que boost :: function (elle est même optimisée pour s'assurer que vous n'obtiendrez pas beaucoup d'utilisation de la mémoire supplémentaire des fonctions virtuelles dans certaines implémentations).

Il est possible de construire une méthode qui aboutit à un code éventuellement aligné, mais dès que vous essayez de le stocker dans une interface générique, vous obtiendrez le préfixe boost :: function (peut-être plus).

Ce que je recommande est d'utiliser simplement boost :: function jusqu'à ce que vous trouviez vraiment besoin de le remplacer par quelque chose de plus rapide. ALORS, et seulement alors vous écrivez les choses modèles de complexité variable pour y arriver. Stocker simplement la fonction boost :: peut être tout à fait suffisant pour ce dont vous avez besoin. Vous pouvez également regarder boost :: signals.

+0

Merci pour l'info. Pourriez-vous jeter un coup d'œil au commentaire que j'ai laissé dans la réponse de Mike? C'est sous le compilateur gcc. – random