Вот решение, которое вы часто можете использовать. Его преимущество заключается в том, что он не требует никаких изменений в реальном коде функции ( не добавляет вызовов к функциям в стеке, изменяет параметры для передачи имен функций или связывается с дополнительными библиотеками. ). Чтобы заставить его работать, вам просто нужно использовать немного магии препроцессора:
Простой пример
// orignal function name was 'FunctionName'
void FunctionNameReal(...)
{
// Do Something
}
#undef FunctionName
#define FunctionName printf("Calling FunctionName from %s\n",__FUNCTION__);FunctionNameReal
Вы должны временно переименовать свою функцию, но см. Примечание ниже для получения дополнительных предложений. Это приведет к выражению printf()
в каждой точке вызова функции. Очевидно, что вы должны принять некоторые меры, если вы вызываете функцию-член или вам нужно захватить возвращаемое значение ( Как передать вызов функции и __FUNCTION__
пользовательской функции, которая возвращает то же самое типа ... ), но основная техника та же. Возможно, вы захотите использовать __LINE__
и __FILE__
или некоторые другие макросы препроцессора в зависимости от того, какой у вас компилятор. (Этот пример специально для MS VC ++, но, вероятно, работает в других.)
Кроме того, вы можете захотеть поместить что-то подобное в свой заголовок в окружении #ifdef
охранников, чтобы условно включить его, что также может переименовать реальную функцию для вас.
ОБНОВЛЕНИЕ [2012-06-21]
Я получил запрос на расширение моего ответа. Как оказалось, мой приведенный выше пример немного упрощен. Вот несколько полностью компилируемых примеров обработки этого с использованием C ++.
Пример с полным исходным кодом с возвращаемым значением
Использование class
с operator()
делает это довольно простым. Этот первый метод работает для автономных функций с и без возвращаемых значений. operator()
просто необходимо отразить тот же результат, что и рассматриваемая функция, и иметь совпадающие аргументы.
Вы можете скомпилировать это с помощью g++ -o test test.cpp
для версии без отчетов и g++ -o test test.cpp -DREPORT
для версии, которая отображает информацию о вызывающем абоненте.
#include <iostream>
int FunctionName(int one, int two)
{
static int calls=0;
return (++calls+one)*two;
}
#ifdef REPORT
// class to capture the caller and print it.
class Reporter
{
public:
Reporter(std::string Caller, std::string File, int Line)
: caller_(Caller)
, file_(File)
, line_(Line)
{}
int operator()(int one, int two)
{
std::cout
<< "Reporter: FunctionName() is being called by "
<< caller_ << "() in " << file_ << ":" << line_ << std::endl;
// can use the original name here, as it is still defined
return FunctionName(one,two);
}
private:
std::string caller_;
std::string file_;
int line_;
};
// remove the symbol for the function, then define a new version that instead
// creates a stack temporary instance of Reporter initialized with the caller
# undef FunctionName
# define FunctionName Reporter(__FUNCTION__,__FILE__,__LINE__)
#endif
void Caller1()
{
int val = FunctionName(7,9); // <-- works for captured return value
std::cout << "Mystery Function got " << val << std::endl;
}
void Caller2()
{
// Works for inline as well.
std::cout << "Mystery Function got " << FunctionName(11,13) << std::endl;
}
int main(int argc, char** argv)
{
Caller1();
Caller2();
return 0;
}
Пример выходных данных (отчетность)
Reporter: FunctionName() is being called by Caller1() in test.cpp:44
Mystery Function got 72
Reporter: FunctionName() is being called by Caller2() in test.cpp:51
Mystery Function got 169
По сути, везде, где встречается FunctionName
, он заменяет его на Reporter(__FUNCTION__,__FILE__,__LINE__)
, чистый эффект которого заключается в том, что препроцессор пишет некоторый экземпляр объекта с немедленным вызовом функции operator()
. Вы можете просмотреть результат (в gcc) замен препроцессора с помощью g++ -E -DREPORT test.cpp
. Caller2 () становится таким:
void Caller2()
{
std::cout << "Mystery Function got " << Reporter(__FUNCTION__,"test.cpp",51)(11,13) << std::endl;
}
Вы видите, что __LINE__
и __FILE__
были заменены. (Я не уверен, почему __FUNCTION__
все еще показывает в выводе, чтобы быть честным, но скомпилированная версия сообщает правильную функцию, так что она, вероятно, имеет отношение к многопроходной предварительной обработке или ошибке gcc.)
Пример с полным исходным кодом с функцией-членом класса
Это немного сложнее, но очень похоже на предыдущий пример. Вместо того, чтобы просто заменить вызов функции, мы также заменим класс.
Как и в предыдущем примере, вы можете скомпилировать это с помощью g++ -o test test.cpp
для версии без отчетов и g++ -o test test.cpp -DREPORT
для версии, которая отображает информацию о вызывающем абоненте.
#include <iostream>
class ClassName
{
public:
explicit ClassName(int Member)
: member_(Member)
{}
int FunctionName(int one, int two)
{
return (++member_+one)*two;
}
private:
int member_;
};
#ifdef REPORT
// class to capture the caller and print it.
class ClassNameDecorator
{
public:
ClassNameDecorator( int Member)
: className_(Member)
{}
ClassNameDecorator& FunctionName(std::string Caller, std::string File, int Line)
{
std::cout
<< "Reporter: ClassName::FunctionName() is being called by "
<< Caller << "() in " << File << ":" << Line << std::endl;
return *this;
}
int operator()(int one, int two)
{
return className_.FunctionName(one,two);
}
private:
ClassName className_;
};
// remove the symbol for the function, then define a new version that instead
// creates a stack temporary instance of ClassNameDecorator.
// FunctionName is then replaced with a version that takes the caller information
// and uses Method Chaining to allow operator() to be invoked with the original
// parameters.
# undef ClassName
# define ClassName ClassNameDecorator
# undef FunctionName
# define FunctionName FunctionName(__FUNCTION__,__FILE__,__LINE__)
#endif
void Caller1()
{
ClassName foo(21);
int val = foo.FunctionName(7,9); // <-- works for captured return value
std::cout << "Mystery Function got " << val << std::endl;
}
void Caller2()
{
ClassName foo(42);
// Works for inline as well.
std::cout << "Mystery Function got " << foo.FunctionName(11,13) << std::endl;
}
int main(int argc, char** argv)
{
Caller1();
Caller2();
return 0;
}
Вот пример вывода:
Reporter: ClassName::FunctionName() is being called by Caller1() in test.cpp:56
Mystery Function got 261
Reporter: ClassName::FunctionName() is being called by Caller2() in test.cpp:64
Mystery Function got 702
Основными моментами этой версии являются класс, который украшает исходный класс, и функция замены, которая возвращает ссылку на экземпляр класса, позволяя operator()
выполнить фактический вызов функции.
Надеюсь, это кому-нибудь поможет!