33 votes

Fonction accrochage en C ++?

Avec "l'accrochage", je veux dire la capacité de manière non intrusive remplacer le comportement d'une fonction. Quelques exemples:

  • Imprimer un message de log avant et/ou après le corps de la fonction.
  • Envelopper le corps de la fonction dans un try catch corps.
  • Mesurer la durée d'une fonction
  • etc...

J'ai vu des différentes mises en œuvre dans divers langages de programmation et des bibliothèques:

  • La Programmation Orientée Aspects
  • JavaScript de première classe de fonctions
  • La programmation orientée objet pattern décorateur
  • WinAPI sous-classement
  • Ruby method_missing
  • SWIGs' %exception mot-clé qui est destiné à envelopper toutes les fonctions dans un bloc try/catch peut être (ab)est utilisée pour les fins de l'accrochage

Mes questions sont les suivantes:

  • IMO c'est incroyablement utile que je me demande pourquoi il n'a jamais été mis en œuvre en tant que C++ le langage. Existe-il des raisons qui empêchent d'être rendue possible?
  • Ce sont recommandés certaines techniques ou bibliothèques de l'implémenter dans un programme C++?

13voto

Dave S Points 11381

Si vous parlez à provoquer une nouvelle méthode pour être appelée avant/après d'un corps de fonction, sans modifier le corps de la fonction, vous pouvez vous baser sur ce, qui utilise une coutume shared_ptr deleter pour déclencher l'après-corps de la fonction. Il ne peut pas être utilisé pour try/catch, depuis l'avant et l'après nécessaire de séparer les fonctions à l'aide de cette technique.

En outre, la version ci-dessous utilise shared_ptr, mais avec le C++11, vous devriez être en mesure d'utiliser unique_ptr pour obtenir le même effet sans le coût de la création et de la destruction d'un pointeur partagé à chaque fois que vous l'utilisez.

#include <iostream>
#include <boost/chrono/chrono.hpp>
#include <boost/chrono/system_clocks.hpp>
#include <boost/shared_ptr.hpp>

template <typename T, typename Derived>
class base_wrapper
{
protected:
  typedef T wrapped_type;

  Derived* self() {
    return static_cast<Derived*>(this);
  }

  wrapped_type* p;

  struct suffix_wrapper
  {
    Derived* d;
    suffix_wrapper(Derived* d): d(d) {};
    void operator()(wrapped_type* p)
    {
      d->suffix(p);
    }
  };
public:
  explicit base_wrapper(wrapped_type* p) :  p(p) {};


  void prefix(wrapped_type* p) {
     // Default does nothing
  };

  void suffix(wrapped_type* p) {
     // Default does nothing
  }

  boost::shared_ptr<wrapped_type> operator->() 
  {
    self()->prefix(p);
    return boost::shared_ptr<wrapped_type>(p,suffix_wrapper(self()));
  }
};




template<typename T>
class timing_wrapper : public base_wrapper< T, timing_wrapper<T> >
{
  typedef  base_wrapper< T, timing_wrapper<T> > base;
  typedef boost::chrono::time_point<boost::chrono::system_clock, boost::chrono::duration<double> > time_point;

  time_point begin;
public:
  timing_wrapper(T* p): base(p) {}


  void prefix(T* p) 
  {
    begin = boost::chrono::system_clock::now();
  }

  void suffix(T* p)
  {
    time_point end = boost::chrono::system_clock::now();

    std::cout << "Time: " << (end-begin).count() << std::endl;
  }
};

template <typename T>
class logging_wrapper : public base_wrapper< T, logging_wrapper<T> >
{
  typedef  base_wrapper< T, logging_wrapper<T> > base;
public:
  logging_wrapper(T* p): base(p) {}

  void prefix(T* p) 
  {
    std::cout << "entering" << std::endl;
  }

  void suffix(T* p) 
  {
    std::cout << "exiting" << std::endl;
  }

};


template <template <typename> class wrapper, typename T> 
wrapper<T> make_wrapper(T* p) 
{
  return wrapper<T>(p);
}


class X 
{
public:
  void f()  const
  {
    sleep(1);
  }

  void g() const
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }

};



int main () {

  X x1;


  make_wrapper<timing_wrapper>(&x1)->f();

  make_wrapper<logging_wrapper>(&x1)->g();
  return 0;
}

5voto

Void Points 2583

Il y a compilateur de fonctionnalités spécifiques vous pouvez tirer parti de tels que, tels que GCC est -finstrument-fonctions. D'autres compilateurs aura probablement des caractéristiques similaires. Voir ce DONC, la question pour plus de détails.

Une autre approche est d'utiliser quelque chose comme Bjarne Stroustrup fonction de l'emballage technique.

4voto

Karoly Horvath Points 45145

Pour répondre à votre première question:

  • La plupart des langages dynamiques ont leur method_missing des constructions, PHP a une magie méthodes (__call et __callStatic) et Python a __getattr__. Je pense que la raison de ce qui n'est pas disponible en C++ qu'il va à l'encontre de l'tapé la nature de C++. La mise en œuvre de ce sur une classe signifie que toutes les fautes d'orthographe sera à la fin de l'appel de cette fonction (au moment de l'exécution!), ce qui empêche la capture de ces problèmes au moment de la compilation. Le mélange C++ avec le duck-typing ne semble pas être une bonne idée.
  • C++ essaie d'être aussi rapide que possible, de sorte que la première classe de fonctions sont hors de question.
  • AOP. Maintenant, c'est plus intéressant, techniquement il n'y a rien qui empêche que ce ajoutés à la norme C++ (en dehors du fait qu'ajouter une autre couche de complexité à un déjà extrêmement complexe standard est peut-être pas une bonne idée). En fait, il existe des compilateurs qui sont en mesure de vague de code, AspectC++ est l'un d'entre eux. Il y a un an ou si elle n'était pas stable mais il semble que, depuis, ils réussi à la version 1.0 avec un assez décent suite de test de sorte qu'il peut fait le travail maintenant.

Il ya un couple de techniques, voici une question connexe:

L'émulation CLOS :avant, :after et :autour de en C++.

2voto

André Caron Points 19543

IMO c'est un incroyablement utiles, alors pourquoi n'est-il pas un langage C++ fonctionnalité? Existe-il des raisons qui empêchent d'être rendue possible?

C++ le langage ne constitue pas un moyen de le faire directement. Cependant, elle ne pose pas de contrainte directe à l'encontre de ce (autant que je sache). Ce type de fonctionnalité est plus facile à mettre en œuvre dans les services d'un interprète qu'en code natif, car l'interpréter, c'est un morceau de logiciel, pas un CPU streaming instructions machine. Vous pourrait offrir un C++ interprète avec l'aide de crochets, si vous vouliez.

Le problème, c'est pourquoi les gens utilisent le C++. Beaucoup de gens sont à l'aide de C++ parce qu'ils veulent pure vitesse d'exécution. Pour atteindre cet objectif, les compilateurs de sortie de code natif dans le système d'exploitation préféré de format et d'essayer de coder en dur comme beaucoup de choses dans le fichier exécutable compilé. La dernière partie souvent des moyens de calcul d'adresses lors de la compilation/lien temps. Si vous corrigez une adresse de la fonction à l'époque (ou même pire, dans l'alignement du corps de la fonction), alors il n'y a pas plus de soutien pour les crochets.

Cela étant dit, il y sont des moyens pour permettre la connexion à bas prix, mais il nécessite un compilateur extensions et est totalement pas de portable. Raymond Chen blogué sur la façon hot patching est mis en œuvre dans l'API Windows. Il recommande également à l'encontre de son utilisation dans le code standard.

1voto

dmckee Points 50318

Au moins sur le framework c ++ que j'utilise fournit un ensemble de classes virtuelles pures

 class RunManager;
class PhysicsManager;
// ...
 

Dont chacun défini un ensemble d’actions

 void PreRunAction();
void RunStartAction()
void RunStopAction();
void PostRunAction();
 

qui sont des NOP, mais que l’utilisateur peut remplacer par la classe parent.

Combinez cela avec la compilation conditionnelle (oui, je sais "Yuk!" ) Et vous pouvez obtenir ce que vous voulez.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X