如何在每个函数的入口处添加代码?

时间:2011-02-22 16:45:32

标签: c++

我想在每次函数调用之前添加一些代码来进行一些检查。我知道的唯一方法是:

#define SOME_CODE printf("doing something...");

class testObject
{
void function1()
{
 SOME_CODE
 ...
}
void function2()
{
 SOME_CODE
 ...
}
}

有更清洁的方法来实现这一目标吗?我正在寻找一种方法,所以我没有手动为每个函数添加“SOME_CODE”。

6 个答案:

答案 0 :(得分:12)

这取决于您使用的编译器。我正在使用DevStudio 2005,在线帮助中有这个编译器命令行选项:

  

/ Gh(启用_penter挂钩功能)

     

在每个方法或函数的开头调用_penter函数。

     

_penter函数不是任何库的一部分,由您来为_penter提供定义。

     

除非您打算明确调用_penter,否则您无需提供原型。该函数必须显示为具有以下原型,并且必须在输入时推送所有寄存器的内容并在退出时弹出未更改的内容:

void __declspec(naked) _cdecl _penter( void );

答案 1 :(得分:10)

对于gcc,有一个类似于MSVC的解决方案,其他人发布了答案:

#include <iostream>
int depth=-1;
extern "C" {
    void __cyg_profile_func_enter (void *, void *) __attribute__((no_instrument_function));
    void __cyg_profile_func_exit (void *, void *) __attribute__((no_instrument_function));
    void __cyg_profile_func_enter (void *func,  void *caller)
    {
        depth++;
    }


    void __cyg_profile_func_exit (void *func, void *caller)
    {
        depth--;
    }
}

class Foo {
public:
    void bar() {
        std::cout << "bar: " << depth << std::endl;
    }
};

int main() {
    Foo f;
    f.bar();
    return 0;
}

g++ -Wall -Wextra -finstrument-functions汇编。注意不要在仪器挂钩内调用仪表功能! (有关排除事物的方法,请参见手册页)

答案 2 :(得分:5)

根据您希望实现的结果,您可以在C ++中使用包含真实调用的函数对象(例如:

)来创建一些内容(对于自由函数或静态成员函数来说非常容易)。
#include <iostream>

template<void f(void)>
struct Wrap {
   void operator()() const {
      std::cout << "Pre call hook" << std::endl;
      f();
   }
};

namespace {
   void test_func() {
      std::cout << "Real function" << std::endl;
   }
}

const Wrap<&test_func> wrapped_test_func = {};

int main() {
   wrapped_test_func();
   return 0;
}

显然,这需要更多的工作才能具有足够的通用性,例如: C ++ 0x可变参数模板或批次的重载。使其与成员函数很好地协作也更加繁琐。

我已经为成员函数绘制了一个(非侵入性)方式的大纲:

#include <iostream>

template<class C, void (C::*F)()>
class WrapMem {
   C& inst;
public:
   WrapMem(C& inst) : inst(inst) {}

   void operator()() {
      std::cout << "Pre (method) call hook" << std::endl;
      ((inst).*(F))();
   }

   void operator()() const {
      std::cout << "Pre (method, const) call hook" << std::endl;
      ((inst).*(F))();
   }
};

class Foo {
public:
   void method() { std::cout << "Method called" << std::endl; }
   void otherstuff() {}
};

class FooWrapped : private Foo  {
public:
   FooWrapped() : method(*this) {}
   using Foo::otherstuff;
   WrapMem<Foo,&Foo::method> method;
};

int main() {
   FooWrapped f;
   f.otherstuff();
   f.method();
   return 0;
}

您也可以跳过私有继承和using来公开非包装方法,但是你需要注意析构函数,如果你这样做,很容易被意外绕过。 (例如,隐式转换以供参考base)。非侵入式方式也仅限于仅用于公共接口而不用于内部调用。

使用C ++ 11,您可以获得完美的转发,并且还可以将包装对象的构造减少为一个简单的宏,该宏接受类和成员函数名称并为您推导出其余部分,例如:

#include <iostream>
#include <utility>

template <typename Ret, typename ...Args>
struct Wrapper {
  template <class C, Ret (C::*F)(Args...)> 
  class MemberFn {
    C& inst;
  public:
    MemberFn(C& inst) : inst(inst) {}
    MemberFn& operator=(const MemberFn&) = delete;

    Ret operator()(Args&& ...args) {
      return ((inst).*(F))(std::forward<Args>(args)...);
    }

    Ret operator()(Args&& ...args) const {
      return ((inst).*(F))(std::forward<Args>(args)...);
    }
  };
};

template <typename T>
struct deduce_memfn;
template <typename C, typename R, typename... Args>
struct deduce_memfn<R (C::*)(Args...)> {
  template <R(C::*F)(Args...)> 
  static typename Wrapper<R, Args...>::template MemberFn<C, F> make();
};

template <typename T>
decltype(deduce_memfn<T>()) deduce(T);

template <typename T>
struct workaround : T {}; // Clang 3.0 doesn't let me write decltype(deduce(&Class::Method))::make...

#define WRAP_MEMBER_FN(Class, Method) decltype(workaround<decltype(deduce(&Class::Method))>::make<&Class::Method>()) Method = *this

class Foo {
public:
  Foo(int);
  double method(int& v) { return -(v -= 100) * 10.2; }
  void otherstuff();
};

class WrappedFoo : private Foo {
public:
  using Foo::Foo; // Delegate the constructor (C++11)
  WRAP_MEMBER_FN(Foo, method);
  using Foo::otherstuff;
};

int main() {
  WrappedFoo f(0);
  int i = 101;
  std::cout << f.method(i) << "\n";
  std::cout << i << "\n";
}

(注意:此扣除不适用于重载) 这是用Clang 3.0测试的。

答案 3 :(得分:5)

您正在寻找的是“代码检测”,我在manual and compiler-automated instrumentation的GDC 2012上发表了演讲(点击here了解代码示例)。

有很多方法可以做你想要的。包装函数,Detours和Trampolines,或CAI(编译器自动化检测),它们是其他答案中提到的_penter()/ __cyg_profile_func_enter()方法。

所有这些以及其他一些仪器方法都在上面链接的PDF中详细说明。

答案 4 :(得分:1)

相当数量的剖析器(和类似的工具)可以/将会做这样的事情。它在二进制级别实际上比源代码级别更容易。在源代码级别,对于大多数实际目的而言,您可能也很难将其视为不可能。

根据您使用的编译器,它有一个非常公平的机会,它有一个支持这种插入的标志 - 通常主要用于支持分析器,如上所述。

答案 5 :(得分:0)

正如有人在评论中所说,这是方面/特征编程的典型用例。您可以尝试this使用c ++进行方面/特征编程。

MY2C