Как добавить код при входе в каждую функцию? - PullRequest
12 голосов
/ 22 февраля 2011

Я хочу добавить код перед каждым вызовом функции, чтобы выполнить некоторую проверку.Единственный способ, которым я знаю:

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

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

Есть ли более чистый способ добиться этого?Я ищу метод, поэтому мне не нужно добавлять «SOME_CODE» в каждую функцию вручную.

Ответы [ 6 ]

12 голосов
/ 22 февраля 2011

Это зависит от того, какой компилятор вы используете.Я использую DevStudio 2005, и из интерактивной справки есть параметр командной строки компилятора:

/ Gh (Включить функцию _penter Hook)

Вызывает вызов функции _penter вначало каждого метода или функции.

Функция _penter не является частью какой-либо библиотеки, и вы можете предоставить определение _penter.

Если вы не планируете явно вызывать _penter,вам не нужно предоставлять прототип.Функция должна выглядеть так, как если бы она имела следующий прототип, и она должна помещать содержимое всех регистров при входе и извлекать неизмененное содержимое при выходе:

void __declspec(naked) _cdecl _penter( void );
10 голосов
/ 22 февраля 2011

Для 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. Будьте осторожны, чтобы не вызывать инструментальную функцию из-за крючков инструмента! (См. Справочную страницу для способов исключения вещей)

5 голосов
/ 19 апреля 2012

То, что вы ищете, называется «инструментарий кода», и я выступил с докладом на GDC 2012 по ручному и автоматизированному инструментарию (нажмите здесь для примеров кода).

Есть несколько способов сделать то, что вы хотите.Функции обертки, Detours и Trampolines, или CAI (инструментарий, автоматизированный компилятором), который является методами _penter () / __cyg_profile_func_enter (), упомянутыми в других ответах.

Все эти, а также некоторые другие методы инструментирования подробно описаны вPDF ссылки выше.

5 голосов
/ 22 февраля 2011

В зависимости от того, чего вы надеетесь достичь в результате этого, вы можете что-то сделать (достаточно легко для свободных функций или статических функций-членов) в 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 variadic или много перегрузок.Заставить его работать с функциями-членами также сложнее.

Я набросал схему (не навязчивого) способа сделать это и для функций-членов:

#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 для раскрытия необернутых методов, но вы должны быть осторожны с деструкторами, и вы легко можете случайно обойти это.(например, неявное приведение для ссылки на базу).Ненавязчивый способ также ограничен работой только для открытого интерфейса, но не для внутренних вызовов.

С 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.

1 голос
/ 22 февраля 2011

Достаточное количество профилировщиков (и подобных инструментов) может / будет делать такие вещи. На самом деле это проще на двоичном уровне, чем на уровне исходного кода. На уровне исходного кода достаточно сложно, чтобы для большинства практических целей вы могли бы также относиться к нему как к невозможному.

В зависимости от того, какой компилятор вы используете, вполне вероятно, что у него есть флаг для поддержки таких вставок - обычно он представлен главным образом для поддержки профилировщиков и тому подобного, о котором я упоминал выше.

0 голосов
/ 22 февраля 2011

Как кто-то сказал в комментарии, это типичный случай использования аспекта / функции программирования. Вы можете попробовать this , чтобы использовать аспектное / функциональное программирование с c ++.

my2c

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...