Einen Zeiger auf die aktuelle Funktion in C (gcc) erhalten?

Lesezeit: 9 Minuten

Benutzer-Avatar
LB40

Gibt es eine magische Variable in gcc, die einen Zeiger auf die aktuelle Funktion enthält?

Ich hätte gerne eine Art Tabelle, die für jeden Funktionszeiger eine Reihe von Informationen enthält.

Ich weiß, dass es eine __func__-Variable gibt, die den Namen der aktuellen Funktion als Zeichenfolge enthält, aber nicht als Funktionszeiger.

Dies soll die Funktion dann nicht aufrufen, sondern nur als Index verwendet werden.

BEARBEITEN
Grundsätzlich möchte ich in der Lage sein, verschachtelte Funktionen kurz vor der Ausführung der aktuellen Funktion auszuführen (und auch die Rückkehr zu erfassen, um einige Dinge auszuführen.) Im Grunde ist dies wie __cyg_profile_func_enter und __cyg_profile_func_exit (die Instrumentierungsfunktionen) … Das Problem besteht jedoch darin, dass diese Instrumentierungsfunktionen global und nicht funktionsspezifisch sind.

BEARBEITEN
Im Linux-Kernel können Sie verwenden unsigned long kallsyms_lookup_name(const char *name) aus include/linux/kallsyms.h … Notiere dass der CONFIG_KALLSYMS Option muss aktiviert sein.

  • Darf ich fragen, warum Sie das versuchen, gibt es vielleicht geeignetere Alternativen?

    – Hassan Syed

    28. Januar 2010 um 13:44 Uhr

  • Ich möchte in der Lage sein, verschachtelte Funktionen innerhalb dieser Funktion zu registrieren und (in beliebiger Reihenfolge) wiederzugeben, ohne einen tatsächlichen Code zu erstellen. Die Sache ist, dass ich die verschachtelten Funktionen generiere, aber ich kenne den Namen der Funktion, in der ich meine verschachtelten Funktionen generiere, nicht wirklich. Ich wollte Änderungen am Parser vermeiden.

    – LB40

    28. Januar 2010 um 13:51 Uhr

  • Ist die Anzahl der Funktionen im Design endlich? oder werden Sie Funktionen hinzufügen ad hoc ?

    – Hassan Syed

    28. Januar 2010 um 13:54 Uhr

  • Nein, ich werde Funktionen ad hoc hinzufügen

    – LB40

    28. Januar 2010 um 14:36 ​​Uhr

  • Ich schlage dann vor, das C++-Funktoren-Idiom zu verwenden — Wenn Sie können, wäre es nicht so viel Overhead im Vergleich zu einfachem C. Sie müssen Boost nicht verwenden — Funktoren sind ziemlich einfach zu implementieren. loki-lib.sourceforge.net/index.php?n=Muster.Befehl Es gibt einige weitere Links in meiner Antwort unten: D

    – Hassan Syed

    28. Januar 2010 um 14:39 Uhr


void f() {
   void (*fpointer)() = &f;
}

  • ja, so würden Sie die Informationen in der Funktion fest codieren: D Schaukeln und Kreisverkehre wirklich. Diese Lösung ist nicht so elegant wie diese Arbeit außerhalb der Funktion zu erledigen – da diese Arbeit möglicherweise Code generiert werden kann.

    – Hassan Syed

    28. Januar 2010 um 13:46 Uhr


  • 😀 … Ich wollte etwas allgemeineres … ohne den Namen von f zu kennen. eine art generische linie könnte man in irgendeiner funktion …

    – LB40

    28. Januar 2010 um 13:48 Uhr

  • @LB ja, diese Lösung ist chaotisch, das Generieren eines Frameworks ist wahrscheinlich die beste Wahl (aber erklären Sie das Problem bitte weiter).

    – Hassan Syed

    28. Januar 2010 um 13:50 Uhr

  • Mit mehr Informationen darüber, was Sie tatsächlich erreichen möchten, könnten Sie Alternativen erhalten.

    – David Rodríguez – Dribeas

    28. Januar 2010 um 13:51 Uhr

  • @LB Posten Sie in Ihrer Frage einen Code, der veranschaulicht, wie Ihre Verwendung einer solchen Funktion (falls vorhanden) aussehen soll.

    anon

    28. Januar 2010 um 13:51 Uhr


Hier ist ein Trick, der die Adresse des Anrufers bekommt, es kann wahrscheinlich ein bisschen aufgeräumt werden. Beruht auf a GCC-Erweiterung zum Abrufen des Werts eines Labels.

#include <stdio.h>

#define MKLABEL2(x) label ## x
#define MKLABEL(x) MKLABEL2(x)
#define CALLFOO do { MKLABEL(__LINE__): foo(&&MKLABEL(__LINE__));} while(0)

void foo(void *addr)
{
    printf("Caller address %p\n", addr);
}

int main(int argc, char **argv)
{
    CALLFOO;
    return 0;
}

  • Verwenden __builtin_return_address(0) Innerhalb foo(), anstatt die Adresse eines Labels zu übergeben, ist eine einfachere Möglichkeit, so ziemlich dasselbe zu tun. (Das gibt die Adresse der Anweisung nach dem Aufruf an foo(), anstatt die Adresse eines konstanten Ladevorgangs direkt vor dem Aufruf; aber beides ist sowieso nicht die eigentliche Adresse der Funktion …)

    – Matthew Slattery

    28. Januar 2010 um 23:59 Uhr

  • @Matthew: Das ist so viel schöner als dieser Hack.

    – Hasturkun

    29. Januar 2010 um 0:15 Uhr

#define FUNC_ADDR (dlsym(dlopen(NULL, RTLD_NOW), __func__))

Und kompilieren Sie Ihr Programm wie

gcc -rdynamic -o foo foo.c -ldl

  • :-)… hinterhältig, aber ich befürchte, dass es nicht möglich ist, das Kompilierungs-Flag zu ändern, um alle Symbole + die Einbeziehung von dlfcn.h einzuschließen.

    – LB40

    28. Januar 2010 um 16:09 Uhr

Benutzer-Avatar
Fortran

Ich denke, Sie könnten Ihre Tabelle mit Zeichenfolgen (den Funktionsnamen) als Schlüssel erstellen und dann durch Vergleichen mit nachschlagen __func__ eingebaute Variable.

Um einen gültigen Funktionsnamen zu erzwingen, könnten Sie ein Makro verwenden, das den Funktionszeiger abruft, einige Dummy-Operationen damit ausführt (z. B. ihn einer temporären Variablen eines kompatiblen Funktionstyps zuweist), um zu überprüfen, ob es sich tatsächlich um einen gültigen Funktionsbezeichner handelt, und dann Zeichenfolgen erstellt (mit #) den Funktionsnamen, bevor er als Taste verwendet wird.

AKTUALISIEREN:

Was ich meine ist so etwas wie:

typedef struct {
  char[MAX_FUNC_NAME_LENGTH] func_name;
  //rest of the info here
} func_info;

func_info table[N_FUNCS];

#define CHECK_AND_GET_FUNC_NAME(f) ({void (*tmp)(int); tmp = f; #f})

void fill_it()
{
  int i = -1;
  strcpy(table[++i].func_name, CHECK_AND_GET_FUNC_NAME(foo));
  strcpy(table[++i].func_name, CHECK_AND_GET_FUNC_NAME(bar));
  //fill the rest
}

void lookup(char *name) {
  int i = -1;
  while(strcmp(name, table[++i]));
  //now i points to your entry, do whatever you need
}

void foo(int arg) {
  lookup(__func__);
  //do something
}

void bar(int arg) {
  lookup(__func__);
  //do something
}

(Der Code benötigt möglicherweise einige Korrekturen, ich habe nicht versucht, ihn zu kompilieren, er dient nur zur Veranschaulichung der Idee.)

Ich hatte auch das Problem, dass ich die Adresse der aktuellen Funktion brauchte, als ich eine Makrovorlagen-Coroutinen-Abstraktion erstellte, die Leute wie moderne Coroutinen-Sprachfunktionen (await und async) verwenden können. Es kompensiert ein fehlendes RTOS, wenn es eine zentrale Schleife gibt, die verschiedene asynchrone Funktionen als (kooperative) Tasks einplant. Das Umwandeln von Interrupt-Handlern in asynchrone Funktionen verursacht sogar Wettlaufbedingungen wie in einem präventiven Multitasking-System.

Mir ist aufgefallen, dass ich die Adresse der aufrufenden Funktion für die endgültige Rücksprungadresse einer Coroutine kennen muss (die natürlich nicht die Rücksprungadresse des ursprünglichen Aufrufs ist). Nur asynchrone Funktionen müssen ihre eigene Adresse kennen, damit sie sie als verstecktes erstes Argument in einem AWAIT()-Makro übergeben können. Da das Instrumentieren des Codes mit einer Makrolösung so einfach ist wie das Definieren der Funktion, reicht es aus, ein asynchrones Schlüsselwort-ähnliches Makro zu haben.

Dies ist eine Lösung mit GCC-Erweiterungen:

#define _VARGS(...) _VARGS0(__VA_ARGS__)
#define _VARGS0(...) ,##__VA_ARGS__

typedef union async_arg async_arg_t;
union async_arg {
    void (*caller)(void*);
    void *retval;
};
#define ASYNC(FUNCNAME, FUNCARGS, ...)            \
void FUNCNAME (async_arg_t _arg _VARGS FUNCARGS)  \
GENERATOR(                                        \
    void (*const THIS)(void*) = (void*) &FUNCNAME;\
    static void (*CALLER)(void*),                 \
    CALLER = _arg.caller;                         \
    __VA_ARGS__                                   \
)

#define GENERATOR(INIT,...) {                     \
    __label__ _entry, _start, _end;               \
    static void *_state = (void*)0;               \
    INIT;                                         \
_entry:;                                          \
    if (_state - &&_start <= &&_end - &&_start)   \
        goto *_state;                             \
    _state = &&_start;                            \
_start:;                                          \
    __VA_ARGS__;                                  \
_end: _state = &&_entry;                          \
}

#define AWAIT(FUNCNAME,...) ({          \
    __label__ _next;                    \
    _state = &&_next;                   \
    return FUNCNAME((async_arg_t)THIS,##__VA_ARGS__);\
    _next: _arg.retval;                 \
})

#define _I(...) __VA_ARGS__
#define IF(COND,THEN) _IF(_I(COND),_I(THEN))
#define _IF(COND,THEN) _IF0(_VARGS(COND),_I(THEN))
#define _IF0(A,B) _IF1(A,_I(B),)
#define _IF1(A,B,C,...) C

#define IFNOT(COND,ELSE) _IFNOT(_I(COND),_I(ELSE))
#define _IFNOT(COND,ELSE) _IFNOT0(_VARGS(COND),_I(ELSE))
#define _IFNOT0(A,B) _IFNOT1(A,,_I(B))
#define _IFNOT1(A,B,C,...) C

#define IF_ELSE(COND,THEN,ELSE) IF(_I(COND),_I(THEN))IFNOT(_I(COND),_I(ELSE))

#define WAIT(...) ({                 \
    __label__ _next;                 \
    _state = &&_next;                \
    IF_ELSE(_I(__VA_ARGS__),         \
        static __typeof__(__VA_ARGS__) _value;\
        _value = (__VA_ARGS__);      \
        return;                      \
        _next: _value;               \
    , return; _next:;)               \
})

#define YIELD(...) do {              \
    __label__ _next;                 \
    _state = &&_next;                \
    return IF(_I(__VA_ARGS__),(__VA_ARGS__));\
_next:;                              \
} while(0)

#define RETURN(VALUE) do {   \
    _state = &&_entry;       \
    if (CALLER != 0)         \
        CALLER((void*)(VALUE +0));\
    return;                  \
} while(0)

#define ASYNCALL(FUNC, ...) FUNC ((void*)0,__VA_ARGS__)

Ich weiß, eine portablere (und vielleicht sicherere) Lösung würde die Switch-Case-Anweisung anstelle von Label-Adressen verwenden, aber ich denke, Gotos sind effizienter als Switch-Case-Anweisungen. Es hat auch den Vorteil, dass Sie die Makros problemlos und innerhalb beliebiger anderer Kontrollstrukturen verwenden können break wird keine unerwarteten Auswirkungen haben.

Sie können es wie folgt verwenden:

#include <stdint.h>
int spi_start_transfer(uint16_t, void *, uint16_t, void(*)());
#define SPI_ADDR_PRESSURE 0x24

ASYNC(spi_read_pressure, (void* dest, uint16_t num),
    void (*callback)(void) = (void*)THIS;    //see here! THIS == &spi_read_pressure
    int status = WAIT(spi_start_transfer(SPI_ADDR_PRESSURE,dest,num,callback));
    RETURN(status);
)

int my_gen() GENERATOR(static int i,
    while(1) {
        for(i=0; i<5; i++)
            YIELD(i);
    }
)

extern volatile int a;
ASYNC(task_read, (uint16_t threshold),
    while(1) {
        static uint16_t pressure;
        int status = (int)AWAIT(spi_read_pressure, &pressure, sizeof pressure);
        if (pressure > threshold) {
            a = my_gen();
        }
    }
)

Sie müssen verwenden AWAIT asynchrone Funktionen für Rückgabewert aufrufen und ASYNCALL ohne Rückgabewert. AWAIT kann nur angerufen werden ASYNC-Funktionen. Sie können verwenden WAIT mit oder ohne Wert. WAIT ergibt den Ausdruck, der als Argument angegeben wurde, der zurückgegeben wird, NACHDEM die Funktion fortgesetzt wird. WAIT kann verwendet werden ASYNC-funktioniert nur. Halten Sie das Argument mit WAIT verschwendet jeweils ein neues Stück statischen Speicher WAIT() Rufen Sie jedoch mit Argument auf, daher wird die Verwendung empfohlen WAIT() ohne Argument. Es könnte verbessert werden, wenn überhaupt WAIT Aufrufe würden dieselbe einzelne statische Variable für die gesamte Funktion verwenden.

Es ist nur eine sehr einfache Version einer Coroutine-Abstraktion. Diese Implementierung kann keine verschachtelten oder miteinander verflochtenen Aufrufe derselben Funktion haben, da alle statischen Variablen einen statischen Stapelrahmen umfassen.

Wenn Sie dieses Problem lösen möchten, müssen Sie auch zwischen dem Fortsetzen eines alten und dem Starten eines neuen Funktionsaufrufs unterscheiden. Sie können Details wie eine Stack-Frame-Warteschlange am Funktionsstart im ASYNC-Makro hinzufügen. Erstellen Sie eine benutzerdefinierte Struktur für den Stapelrahmen jeder Funktion (was auch innerhalb des Makros und eines zusätzlichen Makroarguments erfolgen kann). Dieser benutzerdefinierte Stapelrahmentyp wird beim Aufrufen des Makros aus einer Warteschlange geladen, beim Verlassen wieder gespeichert oder nach Beendigung des Anrufs entfernt. Sie könnten einen Stack-Frame-Index als alternatives Argument in verwenden async_arg_t Union. Wenn das Argument eine Adresse ist, startet es einen neuen Aufruf, oder wenn es einen Stack-Frame-Index erhält, nimmt es einen alten Aufruf wieder auf. Der Stapelrahmenindex oder die Fortsetzung muss als benutzerdefiniertes Argument an den Rückruf übergeben werden, der die Coroutine fortsetzt.

Benutzer-Avatar
Gemeinschaft

Wenn Sie sich für C++ entschieden haben, könnten Ihnen die folgenden Informationen helfen:

Objekte werden typisiert, Funktoren sind Funktionen, die als Objekte verpackt sind, RTTI ermöglicht die Identifizierung des Typs zur Laufzeit.

Funktoren bringen einen Laufzeitaufwand mit sich, und wenn dies ein Problem für Sie ist, würde ich vorschlagen, das Wissen mithilfe der Codegenerierung fest zu codieren oder eine OO-Hierarchie von Funktoren zu nutzen.

Nein, die Funktion ist sich selbst nicht bewusst. Sie müssen die Tabelle, von der Sie sprechen, selbst erstellen, und wenn Sie möchten, dass eine Funktion sich ihrer selbst bewusst ist, müssen Sie den Index als Parameter an die globale Tabelle (oder den Zeiger der Funktion) übergeben.

Hinweis: Wenn Sie dies tun möchten, sollten Sie ein konsistentes Namensschema für den Parameter haben.

1158500cookie-checkEinen Zeiger auf die aktuelle Funktion in C (gcc) erhalten?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy