Подтвердить что ты не робот

Как добавить код при вводе каждой функции?

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

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

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

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

4b9b3361

Ответ 1

В зависимости от того, чего вы надеетесь достичь в результате этого, вы можете сделать что-то (достаточно легко для бесплатных функций или статических функций-членов) на С++ с объектами-функторами, которые переносят реальные вызовы, например:

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

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

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

Ответ 2

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

/Gh (Enable _penter Hook Function)

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

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

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

void __declspec(naked) _cdecl _penter( void );

Ответ 3

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

Ответ 4

То, что вы ищете, называется "инструментарием кода", и я поговорил в GDC 2012 на ручном и компиляционном автоматизированном приборостроении (щелкните здесь для образцов кода).

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

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

Ответ 5

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

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

Ответ 6

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

my2c