开发者

How to add code at the entry of every function?

I want to add some code before every function call to do some checking. The only way I know is:

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

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

Is there a cleaner way to achieve this? I'm 开发者_C百科looking for a method so I don't have add "SOME_CODE" to every function manually.


For gcc there's a similar solution to the MSVC one someone else posted as an answer:

#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;
}

Compile with g++ -Wall -Wextra -finstrument-functions. Be careful not to call an instrumented function from within the instrument hooks though! (See the man page for ways of excluding things)


It depends on the compiler you're using. I'm using DevStudio 2005 and, from the online help there's this compiler command line option:

/Gh (Enable _penter Hook Function)

Causes a call to the _penter function at the start of every method or function.

The _penter function is not part of any library and it is up to you to provide a definition for _penter.

Unless you plan to explicitly call _penter, you do not need to provide a prototype. The function must appear as if it had the following prototype, and it must push the content of all registers on entry and pop the unchanged content on exit:

void __declspec(naked) _cdecl _penter( void );


What you are looking for is called "code instrumentation" and I gave a talk at GDC 2012 on manual and compiler-automated instrumentation (click here for code samples).

There are a number of ways to do what you want. Wrapper functions, Detours and Trampolines, or CAI (compiler-automated instrumentation) which is _penter() / __cyg_profile_func_enter() methods mentioned in other answers.

All of these as well as some other instrumentation methods are detailed in the PDF's linked above.


Depending on what you're hoping to achieve as a result of this you can sort of make something (easily enough for free functions or static member functions) in C++ with functor objects which wrap real calls, e.g.:

#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;
}

Clearly this needs some more work to be generic enough e.g. C++0x variadic templates or lots of overloads. Making it work nicely with member functions is also more fiddly.

I've sketched an outline for a (non-intrusive) way of doing this for member functions too:

#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;
}

You could also skip the private inheritance and using to expose non-wrapped methods, but you need to be careful about destructors and it's easy to accidentally bypass if you do that. (e.g. implicit cast for reference to base). The non-intrusive way is also limited to only working for the public interface and not for internal calls either.

With C++11 you can get perfect forwarding and also reduce the construction of the wrapping objects to a simple macro that takes the class and member function name and deduces the rest for you, e.g.:

#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";
}

(Note: this deduction won't work with overloads) This was tested with Clang 3.0.


A fair number of profilers (and similar tools) can/will do things like this. It's actually easier at the binary level than the source code level. At the source code level, it's sufficiently difficult that for most practical purposes you might as well treat it as impossible.

Depending on the compiler you're using, there's a pretty fair chance that it has a flag to support such insertions -- usually present primarily to support the profilers and such I mentioned above.


As someone said in a comment, it is a typical use case of aspect / feature programming. You can try this to use aspect / feature programming with c++.

my2c

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜