函数钩在C++中?

Sta*_*ked 33 c++

"挂钩"是指非侵入性地覆盖函数行为的能力.一些例子:

  • 在函数体之前和/或之后打印日志消息.
  • 将函数体包裹在try catch体中.
  • 测量函数的持续时间
  • 等等...

我在各种编程语言和库中看到了不同的实现:

  • 面向方面编程
  • JavaScript的第一类函数
  • OOP装饰图案
  • WinAPI子类化
  • Ruby的 method_missing
  • SWIG%exception关键字用于包装try/catch块中的所有函数,可以(ab)用于挂钩

我的问题是:

  • IMO这是一个非常有用的功能,我想知道为什么它从未被实现为C++语言功能.是否有任何理由阻止这种情况发生?
  • 在C++程序中实现这一点有哪些推荐的技术或库?

Dav*_*e S 13

如果您正在讨论在函数体之前/之后调用新方法而不更改函数体,则可以将其基于,它使用自定义shared_ptr删除器来触发后体函数.它不能用于try/catch,因为之前和之后需要使用这种技术的单独功能.

此外,下面的版本使用shared_ptr,但使用C++ 11,你应该可以使用它unique_ptr来获得相同的效果,而无需每次使用它时创建和销毁共享指针的成本.

#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;
}
Run Code Online (Sandbox Code Playgroud)


Voi*_*oid 5

您可以利用特定于编译器的功能,例如GCC的-finstrument-functions.其他编译器可能具有类似的功能.有关其他详细信息,请参阅此问题.

另一种方法是使用类似Bjarne Stroustrup的函数包装技术.

  • MSVC++的相应开关是`/ Gh`:http://msdn.microsoft.com/en-us/library/c63a9b7h.aspx (2认同)