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

Chr*_*phe 12 c++

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

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

class testObject
{
void function1()
{
 SOME_CODE
 ...
}
void function2()
{
 SOME_CODE
 ...
}
}
Run Code Online (Sandbox Code Playgroud)

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

Ski*_*izz 12

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

/ Gh(启用_penter挂钩功能)

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

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

除非您打算显式调用_penter,否则不需要提供原型.该函数必须显示为具有以下原型,并且必须在条目上推送所有寄存器的内容并在退出时弹出未更改的内容:

void __declspec(naked) _cdecl _penter( void );
Run Code Online (Sandbox Code Playgroud)


Fle*_*exo 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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 如果使用线程局部变量来防止重新进入 __cyg_profile_func_enter()/__cyg_profile_func_exit(),则不必担心调用检测函数。 (2认同)

Fle*_*exo 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;
}
Run Code Online (Sandbox Code Playgroud)

显然,这需要一些更通用的工作,例如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;
}
Run Code Online (Sandbox Code Playgroud)

您也可以跳过私有继承并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";
}
Run Code Online (Sandbox Code Playgroud)

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


Adi*_*sak 5

您正在寻找的是"代码检测",我在GDC 2012上就手动和编译器自动化仪器进行了讨论(点击此处查看代码示例).

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

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