Πώς μπορώ να βρω το όνομα της συνάρτησης κλήση;

ψήφοι
34

Έχω χρησιμοποιήσει PRETTY_FUNCTION για να εξαγάγετε το τρέχον όνομα της συνάρτησης, όμως έχω επαναυλοποιηθεί ορισμένες λειτουργίες και θα θέλατε να μάθετε ποιες λειτουργίες είναι καλώντας τους.

Στην C ++ πως μπορώ να βρω το όνομα της λειτουργίας της ρουτίνας κλήση;

Δημοσιεύθηκε 09/12/2008 στις 16:43
πηγή χρήστη
Σε άλλες γλώσσες...                            


8 απαντήσεις

ψήφοι
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__ή κάποιες άλλες μακροεντολές προεπεξεργαστή ανάλογα με το compiler που έχετε. (Το παράδειγμα αυτό είναι ειδικά για το 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__)το καθαρό αποτέλεσμα των οποίων είναι η προεπεξεργαστή γράφει κάποιο αντικείμενο 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__εξακολουθεί να εμφανίζει στην έξοδο για να είμαι ειλικρινής, αλλά η μεταγλωττισμένη έκδοση αναφέρει το δικαίωμα λειτουργίας, γι 'αυτό μάλλον έχει να κάνει με multi-pass προεπεξεργασίας ή gcc σφάλμα.)

Πλήρης Παράδειγμα Πηγή με Λειτουργία Class κράτος

Αυτό είναι λίγο πιο περίπλοκο, αλλά πολύ παρόμοιο με το προηγούμενο παράδειγμα. Αντί απλά αντικαθιστώντας την κλήση της συνάρτησης, είμαστε επίσης την αντικατάσταση της κατηγορίας.

Όπως και το παραπάνω παράδειγμα, μπορείτε να συγκεντρώσετε αυτό με 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. Μπορείτε να πάρετε μια πλήρη stacktrace (συμπεριλαμβανομένου του ονόματος, ενότητα, και την αντιστάθμιση της λειτουργίας κλήσης) με τις πρόσφατες εκδόσεις της glibc με τις λειτουργίες backtrace 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

Εκτός και αν υπάρχει περισσότερο στο ζήτημα από ό, τι ζήτησε ρητά, απλά μετονομάσετε τη λειτουργία και αφήστε τον compiler / σύνδεσης σας πω πού λέγεται.

Απαντήθηκε 21/06/2012 στις 18:48
πηγή χρήστη

ψήφοι
0

Μπορείτε να χρησιμοποιήσετε αυτόν τον κωδικό, για να παρακολουθείτε τόπους του ελέγχου στην τελευταία 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;
} 
Απαντήθηκε 22/05/2018 στις 00:34
πηγή χρήστη

ψήφοι
0

Στο έλατα προσέγγιση, grep μόνο το κομμάτι του κώδικα για τα ονόματα των λειτουργιών. Στη συνέχεια έρχεται Doxygen, και στη συνέχεια, δυναμική καταγραφή (και συζητήθηκαν από τους άλλους).

Απαντήθηκε 09/12/2008 στις 17:18
πηγή χρήστη

ψήφοι
0

Πιθανότατα θέλετε τα ονόματα όλων των λειτουργιών που δυνητικά θα μπορούσε να τους καλέσετε. Αυτό είναι βασικά ένα σύνολο ακμών στο γράφημα κλήση. Doxygen μπορεί να δημιουργήσει το γράφημα κλήση, και τότε είναι απλά ένα θέμα εξετάζοντας τις εισερχόμενες ακμές των λειτουργιών του κόμβου σας.

Απαντήθηκε 09/12/2008 στις 16:54
πηγή χρήστη

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