2013-08-13 15 views
5

qualche modo collegato a my previous question herePrendi l'oggetto chiamante o il metodo in d

C'è un modo per ottenere l'oggetto chiamante all'interno di una funzione o di un metodo in d?

esempio:

class Foo 
{ 
    public void bar() 
    { 
     auto ci = whoCalledMe(); 
     // ci should be something that points me to baz.qux, _if_ baz.qux made the call 

    } 
} 

class Baz 
{ 
    void qux() 
    { 
     auto foo = new Foo(); 
     foo.bar(); 
    } 
} 

Domande:

  1. fa qualcosa di simile whoCalledMe esiste? e se sì, come si chiama?
  2. se qualcosa esiste, può essere utilizzato in fase di compilazione (in un modello) e se sì, come?

In alternativa;

  1. è possibile accedere allo stack di chiamata in fase di esecuzione? come con php debug_backtrace?
+0

un po 'difficile quando la chiamata può essere effettuata anche da 'main' –

+0

@ratchetfreak: mi manca il punto del tuo commento? ricorda che il codice è lì per spiegare una cosa generale, non per limitare la domanda. – Kris

+0

Non credo sia possibile ottenere ciò che si desidera in qualsiasi linguaggio compilato. Riguardo alla domanda 2: al momento della compilazione ci possono essere più chiamanti di una funzione, è possibile ottenere l'elenco di tutti i chiamanti possibili (e non credo sia possibile in D ora). –

risposta

4

Non è direttamente possibile ottenere informazioni sul tuo "chiamante". Potresti avere un po 'di fortuna nel recuperare l'indirizzo dallo stack delle chiamate, ma questa è un'operazione di basso livello e dipende da cose come se il tuo programma è stato compilato con frame di stack. Dopo aver ottenuto l'indirizzo, in teoria è possibile convertirlo in un nome di funzione e in un numero di riga, sono disponibili simboli di debug per il binario del programma, ma (di nuovo) questo è altamente specifico della piattaforma e dipende dalla toolchain utilizzata per compilare il programma .

In alternativa, si potrebbe trovare questo utile:

void callee(string file=__FILE__, int line=__LINE__, string func=__FUNCTION__)() 
{ 
    writefln("I was called by %s, which is in %s at line %d!", func, file, line); 
} 

void caller() 
{ 
    // Thanks to IFTI, we can call the function as usual. 
    callee(); 
} 

Ma nota che non è possibile utilizzare questo trucco per metodi di classe non definitive, perché ogni chiamata alla funzione genererà una nuova istanza modello (e il compilatore deve conoscere in anticipo l'indirizzo di tutti i metodi virtuali di una classe).

+0

ho trovato, e persino usato, __FILE__ e __LINE__ prima, ma non sapevo ancora __FUNCTION__. ci sono più di queste "costanti magiche"? – Kris

+2

'__FUNCTION__' è stato [aggiunto] (https://github.com/D-Programming-Language/dmd/pull/1462) di recente. L'elenco di questa famiglia di parole chiave è [qui] (http://dlang.org/traits.html#specialkeywords). –

+1

hm questo aggiunge un sacco di codice in eccesso se lo si utilizza molte volte ... non è quella soluzione ideale – Quonux

5

Per espandere su ciò CyberShadow said, dal momento che è possibile ottenere il nome completo della funzione utilizzando __FUNCTION__, è anche possibile ottenere la funzione come un simbolo utilizzando un mixin:

import std.stdio; 
import std.typetuple; 

void callee(string file=__FILE__, int line=__LINE__, string func=__FUNCTION__)() 
{ 
    alias callerFunc = TypeTuple!(mixin(func))[0]; 
    static assert(&caller == &callerFunc); 

    callerFunc(); // will eventually overflow the stack 
} 

void caller() 
{ 
    callee(); 
} 

void main() 
{ 
    caller(); 
} 

Lo stack traboccherà qui dal momento che queste due funzioni finiscono per chiamarsi vicendevolmente in modo indefinito.

2

Trovare il chiamante è qualcosa che i debugger fanno e in genere richiede di aver creato il programma con gli interruttori di informazioni di debug simbolici attivati. Leggere le informazioni di debug per capirlo è altamente dipendente dal sistema ed è piuttosto avanzato.

Il meccanismo di annullamento dell'eccezione trova anche il chiamante, ma tali tabelle non vengono generate per le funzioni che non ne hanno bisogno e le tabelle non includono il nome della funzione.

+0

E se richiedessi una funzione '__CLASS__' come se ci fossero già' __FILE__', '__LINE ___' e '__FUNCTION__'? Mentre ci siamo, probabilmente dovrei anche chiedere '__MODULE__'. A proposito, sono più che disposto a "fare i documenti richiesti". – Kris

+0

'__MODULE__' è già implementato. Puoi estrarre la classe da '__FUNCTION__', poiché è pienamente qualificata. –

+0

@CyberShadow potrei, ma perché richiedere il disordine? Sembra adattarsi troppo bene alle altre costanti magiche per non essere lì per me. Così bene, in realtà, che la sua assenza potrebbe essere considerata piuttosto sorprendente. Deve avere letto '__MODULE__' poiché non è nell'esempio di codice. Colpa mia. – Kris