Как найти имя вызывающей функции?

голоса
34

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

В C ++ как я могу получить имя функции вызывающей программы?

Задан 09/12/2008 в 16:43
источник пользователем
На других языках...                            


9 ответов

голоса
39

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

Кроме того , вы можете поставить что - то вроде этого в заголовке вашего окружения #ifdefохранников условно включить его, который может обрабатывать переименование фактической функции для вас.

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

Надеюсь, что кто-то помогает!

Ответил 18/12/2008 в 16:22
источник пользователем

голоса
17

Здесь есть два варианта:

  1. Вы можете получить полную трассировку стека ( в том числе имя, модуль, и смещение вызывающей функции) с последними версиями Glibc с функциями трассировку GNU . Смотрите мой ответ здесь для деталей. Это, наверное, проще всего.

  2. Если это не то , что вы ищете, то вы можете попробовать libunwind , но это будет включать в себя больше работы.

Имейте в виду, что это не то, что вы можете знать статически (как с PRETTY_FUNCTION); вы на самом деле придется ходить стек, чтобы выяснить, что функция называется вас. Так что это не то, что на самом деле стоит делать в обычной отладочной printfs. Если вы хотите сделать более серьезную отладку или анализ, хотя, то это может быть полезно для вас.

Ответил 09/12/2008 в 16:50
источник пользователем

голоса
8

С 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

Ответил 15/05/2017 в 02:30
источник пользователем

голоса
2

Изменение Aaron ответа. Я не уверен , есть ли этот ответ эта проблема, но когда вы делаете #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
Ответил 17/11/2018 в 16:13
источник пользователем

голоса
2

Если не будет больше вопрос, чем вы явно просил, просто переименовать функцию и пусть компилятор / линкер сказать вам, где он называется.

Ответил 21/06/2012 в 18:48
источник пользователем

голоса
0

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

// 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;
} 
Ответил 22/05/2018 в 00:34
источник пользователем

голоса
0

В приближении елей, просто Grep в кодовом для имен функций. Затем наступает Doxygen, а затем динамическое ведение журнала (как обсуждается другими).

Ответил 09/12/2008 в 17:18
источник пользователем

голоса
0

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

Ответил 09/12/2008 в 16:54
источник пользователем

голоса
-2

Cflow может быть использован , чтобы получить граф вызовов исходного кода , написанного на C / C ++. Вы можете разобрать этот граф вызовов , чтобы получить то , что вы хотите.

Ответил 23/07/2019 в 17:17
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more