Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function hooking in C++?

Tags:

c++

With "hooking" I mean the ability to non-intrusively override the behavior of a function. Some examples:

  • Print a log message before and/or after the function body.
  • Wrap the function body in a try catch body.
  • Measure duration of a function
  • etc...

I have seen different implementations in various programming languages and libraries:

  • Aspect Oriented Programming
  • JavaScript's first class functions
  • OOP decorator pattern
  • WinAPI subclassing
  • Ruby's method_missing
  • SWIG's %exception keyword which is meant to wrap all functions in a try/catch block can be (ab)used for the purpose of hooking

My questions are:

  • IMO this is such an incredibly useful feature that I wonder why it has never been implemented as a C++ language feature. Are there any reasons that prevent this from being made possible?
  • What are some recommended techniques or libraries to implement this in a C++ program?
like image 819
StackedCrooked Avatar asked Oct 12 '11 17:10

StackedCrooked


2 Answers

If you're talking about causing a new method to be called before/after a function body, without changing the function body, you can base it on this, which uses a custom shared_ptr deleter to trigger the after-body function. It cannot be used for try/catch, since the before and after need to be separate functions using this technique.

Also, the version below uses shared_ptr, but with C++11 you should be able to use unique_ptr to get the same effect without the cost of creating and destroying a shared pointer every time you use it.

#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; } 
like image 91
Dave S Avatar answered Sep 28 '22 10:09

Dave S


There are compiler-specific features you can leverage such as, such as GCC's -finstrument-functions. Other compilers will likely have similar features. See this SO question for additional details.

Another approach is to use something like Bjarne Stroustrup's function wrapping technique.

like image 28
Void Avatar answered Sep 28 '22 11:09

Void