With "hooking" I mean the ability to non-intrusively override the behavior of a function. Some examples:
I have seen different implementations in various programming languages and libraries:
method_missing
%exception
keyword which is meant to wrap all functions in a try/catch block can be (ab)used for the purpose of hookingMy questions are:
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; }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With