Как найти имя вызывающей функции?
Я использую PRETTY_FUNCTION для вывода текущего имени функции, однако я переопределил некоторые функции и хотел бы узнать, какие функции их вызывают.
В С++ как я могу получить имя функции вызывающей подпрограммы?
Ответы
Ответ 1
Вот два варианта:
-
Вы можете получить полную трассировку стека (включая имя, модуль и смещение вызывающей функции) с последними версиями glibc с функциями обратной трассировки GNU. Смотрите мой ответ здесь для деталей. Это, наверное, самая легкая вещь.
-
Если это не совсем то, что вы ищете, то вы можете попробовать libunwind, но это потребует больше работы.
Имейте в виду, что это не то, что вы можете знать статически (как с PRETTY_FUNCTION); вам действительно нужно пройтись по стеку, чтобы выяснить, какая функция вас назвала. Так что это не то, что действительно стоит делать в обычных отладочных printfs. Если вы хотите сделать более серьезную отладку или анализ, то это может быть полезно для вас.
Ответ 2
Вот решение, которое вы часто можете использовать. Он имеет то преимущество, что не требует изменений в действительном функциональном коде (без добавления вызовов к функциям stackwalk, изменения параметров для передачи имен функций или ссылки на дополнительные библиотеки). Чтобы заставить его работать, вам просто нужно использовать немного препроцессорной магии:
Простой пример
// 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 VС++, но, вероятно, работает и в других.)
Кроме того, вы можете поместить что-то вроде этого в свой заголовок, окруженный защитой #ifdef
, чтобы условно включить его, что также может обрабатывать переименование фактической функции для вас.
ОБНОВЛЕНИЕ [2012-06-21]
У меня появился запрос на расширение моего ответа. Как оказалось, мой приведенный выше пример немного упрощен. Ниже приведены некоторые примеры компиляции с использованием С++.
Пример полного источника с возвращаемым значением
Использование 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()
выполнять фактический вызов функции.
Надеюсь, что это поможет кому-то!
Ответ 3
С версией GCC ≥ 4.8 вы можете использовать __builtin_FUNCTION
- не путать с __FUNCTION__
и аналогичным - кажется, это немного неясно.
Пример:
#include <cstdio>
void foobar(const char* str = __builtin_FUNCTION()){
std::printf("called by %s\n", str);
}
int main(){
foobar();
return 0;
}
выход:
called by main
пример в WandBox
Ответ 4
Если вопрос не задан более подробно, чем вы явно задали, просто переименуйте функцию и дайте компилятору/компоновщику рассказать вам, где он вызывается.
Ответ 5
Вариация ответа Аарона. Я не уверен, имеет ли этот ответ эту проблему, но когда вы выполняете функцию #define function
, она становится глобальной переменной, а затем, если в вашем проекте есть несколько классов с одинаковым именем функции класса-члена, у всех классов будет переопределено имя функции. к той же функции.
#include <iostream>
struct ClassName {
int member;
ClassName(int member) : member(member) { }
int secretFunctionName(
int one, int two, const char* caller, const char* file, int line)
{
std::cout << "Reporter: ClassName::function_name() is being called by "
<< caller << "() in " << file << ":" << line << std::endl;
return (++member+one)*two;
}
};
#define unique_global_function_name(first, second) \
secretFunctionName(first, second, __FUNCTION__,__FILE__,__LINE__)
void caller1() {
ClassName foo(21);
int val = foo.unique_global_function_name(7, 9);
std::cout << "Mystery Function got " << val << std::endl;
}
void caller2() {
ClassName foo(42);
int val = foo.unique_global_function_name(11, 13);
std::cout << "Mystery Function got " << val << std::endl;
}
int main(int argc, char** argv) {
caller1();
caller2();
return 0;
}
Результат:
Reporter: ClassName::function_name() is being called by caller1() in D:\test.cpp:26
Mystery Function got 261
Reporter: ClassName::function_name() is being called by caller2() in D:\test.cpp:33
Mystery Function got 702
Ответ 6
Возможно, вам нужны имена всех функций, которые могут их вызывать. Это в основном набор ребер в графе вызовов. doxygen может сгенерировать граф вызовов, а затем просто посмотреть на входящие ребра узла вашей функции.
Ответ 7
В первом приближении просто используйте grep для базы данных для имен функций. Затем идет Doxygen, а затем динамическое ведение журнала (оба обсуждаются другими).
Ответ 8
Вы можете использовать этот код, чтобы отслеживать локусы контроля в последних n точках вашей программы. Использование: см. Основную функцию ниже.
// What: Track last few lines in loci of control, gpl/moshahmed_at_gmail
// Test: gcc -Wall -g -lm -std=c11 track.c
#include <stdio.h>
#include <string.h>
#define _DEBUG
#ifdef _DEBUG
#define lsize 255 /* const int lsize=255; -- C++ */
struct locs {
int line[lsize];
char *file[lsize];
char *func[lsize];
int cur; /* cur=0; C++ */
} locs;
#define track do {\
locs.line[locs.cur]=__LINE__ ;\
locs.file[locs.cur]=(char*)__FILE__ ;\
locs.func[locs.cur]=(char*) __builtin_FUNCTION() /* __PRETTY_FUNCTION__ -- C++ */ ;\
locs.cur=(locs.cur+1) % lsize;\
} while(0);
void track_start(){
memset(&locs,0, sizeof locs);
}
void track_print(){
int i, k;
for (i=0; i<lsize; i++){
k = (locs.cur+i) % lsize;
if (locs.file[k]){
fprintf(stderr,"%d: %s:%d %s\n",
k, locs.file[k],
locs.line[k], locs.func[k]);
}
}
}
#else
#define track do {} while(0)
#define track_start() (void)0
#define track_print() (void)0
#endif
// Sample usage.
void bar(){ track ; }
void foo(){ track ; bar(); }
int main(){
int k;
track_start();
for (k=0;k<2;k++)
foo();
track;
track_print();
return 0;
}
Ответ 9
Cflow может использоваться для получения графа вызовов исходного кода, написанного на C/C++. Вы можете проанализировать этот график вызовов, чтобы получить то, что вы хотите.