2010-05-20 18 views
6

UPDATE: Dopo alcune letture aggiuntive, quello che volevo veramente era l'associazione anticipata garantita (che dovrebbe essere tradotta in una chiamata immediata per funzioni non virtuali e codice non PIC), che può essere fatta passando una funzione (membro) come parametro del modello. Il problema che ho riscontrato è che gcc < 4.5 e icc 11.1 possono generare alcune funky istruzioni per le chiamate dei parametri del modello del puntatore function function. AFAICT, gcc> = 4,5 e vs2008 gestiscono bene queste chiamate dei parametri del modello.Dove sono i letterali degli indirizzi di funzione in C++?

Prima di tutto, forse letterali non è il termine corretto per questo concetto, ma è il più vicino che potrei pensare (non letterali nel senso di funzioni di cittadini di prima classe).

L'idea è che quando si effettua una chiamata di funzione convenzionale, si compila a qualcosa di simile:

callq <immediate address> 

Ma se si effettua una chiamata di funzione tramite un puntatore a funzione, si compila a qualcosa di simile:

mov <memory location>,%rax 
callq *%rax 

Che va tutto bene. Tuttavia, cosa succede se sto scrivendo una libreria di modelli che richiede un callback di qualche tipo con un elenco di argomenti specificato e l'utente della libreria deve sapere quale funzione desidera chiamare al tempo di compilazione? Quindi vorrei scrivere il mio modello per accettare un valore letterale di funzione come parametro del modello. Così, simile a

template <int int_literal> 
struct my_template {...};` 

Mi piacerebbe scrivere

template <func_literal_t func_literal> 
struct my_template {...}; 

e hanno chiamate a func_literal entro my_template compilare in callq <immediate address>.

Esiste una funzione in C++ per questo o una soluzione per ottenere lo stesso effetto? In caso negativo, perché no (ad esempio alcuni effetti collaterali cataclismici)? Che ne dici di C++ 0x o di un'altra lingua?

+0

Questo argomento è utile, poiché consente di scambiare diverse funzionalità durante l'esecuzione. Ad esempio, l'API DOS utilizza ID per mappare le funzioni. Ciò consente alla funzionalità DOS di cambiare con effetti minimi sull'eseguibile. Basta cambiare il contenuto (indirizzi di funzione) nella tabella di ricerca. –

+0

I Functional in combinazione con i modelli sono in fase di compilazione. La maggior parte degli STL funziona così o c'è qualche avvertimento nel tuo problema? – pmr

+0

@ avvertire che la soluzione non dovrebbe avere alcun impatto sul codice utente; se non stanno usando oggetti funzione, non voglio forzarli a usare oggetti funzione. – academicRobot

risposta

3

Se si utilizza un puntatore di funzione nel modello e si crea un'istanza con una funzione fissa, il compilatore deve utilizzare una chiamata diretta per quella chiamata al puntatore di funzione.

+0

+1 Appena provato, funziona alla grande. Che dire dell'accettazione delle funzioni membro dalla classe arbitraria (senza considerare le funzioni virtuali)? La tua risposta mi dà alcune idee per questo ... – academicRobot

1

CodeProject.com:

ho usato su diverse piattaforme: http://www.codeproject.com/kb/cpp/FastDelegate.aspx

Saw nei risultati di ricerca, leggerà: http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx

... o è questo il genere di cose che si stai cercando?

+0

Il primo collegamento utilizza ancora la chiamata puntatore, ritenendo che siano più veloci delle chiamate delle funzioni membro.Non ero a conoscenza del secondo, lo esaminerò ... – academicRobot

+0

Non ho visto prima che tu intendessi leggere quel secondo link. Ho giocato con lui un minuto fa e nessun dado. In realtà, è un po 'strano. C'è una struttura interna che incorpora la call fine (in modalità immediata), ma che viene chiamata tramite il puntatore di funzione. Pensa che è necessario per ottenere l'interopzione tra i delegati, che non è un requisito qui. – academicRobot

0

Nel linguaggio C++, i traduttori non piegano i nomi delle funzioni nell'eseguibile, sono persi e persi per sempre.

È possibile creare una tabella del nome della funzione in funzione dell'indirizzo della funzione. Dato che C e C++ sono di tipo stickler per le informazioni sul tipo, questo potrebbe essere più semplice dichiarato in linguaggio assembly. Nel linguaggio di alto livello, dovresti avere una tabella per ogni diverso tipo di puntatore a funzione. Tuttavia, in assemblea, non gli importa. Sebbene sia possibile utilizzare una funzione con un switch per restituire un puntatore a funzione o eseguire la funzione.

Un'alternativa è rappresentata da ID di funzione (enumerazione) rispetto a indirizzi di funzione.

+0

Non voglio usare il nome della funzione in fase di esecuzione, voglio usare un surrogato per il nome della funzione in * tempo di compilazione *. Le soluzioni proposte ti stanno ancora utilizzando i puntatori di funzione in fase di esecuzione. A meno che non fraintendano, per favore, spiega se questo è il caso. – academicRobot

0

Almeno se ho capito bene la tua domanda, questo è banale:

template <class func> 
struct whatever { 
    operator()() { 
     func(); 
    } 
}; 

La chiamata a func(); saranno generalmente finire come una chiamata diretta a func() come hai chiesto, o se func() è piccolo, il suo codice verrà spesso generato in linea. Se si desidera migliorare le probabilità che venga generato in linea, in genere si desidera scriverlo come un functor (classe che sovraccarica operator()) anziché una funzione normale. Come una funzione normale, più spesso si finirà per chiamare la funzione effettiva (ma sarà una chiamata diretta, non una chiamata tramite un puntatore).

Modifica: Non sono sicuro di cosa stavo pensando, ma hai perfettamente ragione: funzionerà solo con un funtore, non con una funzione reale. Mie scuse.

+0

Funziona alla grande con un funtore. Ma non verrà compilato per una funzione: int eco (int i) {return i;} int main (void) {qualunque whateverObj; return 1;} dà "Errore: atteso un tipo, ha ottenuto 'echo '" – academicRobot

+0

Questa è stata una delle prime cose che ho provato, quindi non avrai critiche da parte mia :) – academicRobot

+0

Questa è la soluzione corretta. Vedere 'std :: less ', che utilizza esattamente lo stesso modello e per lo stesso motivo. La chiamata diretta è il motivo per cui 'std :: sort' batte' std :: qsort'. – MSalters

1
#include <iostream>                

template<void F()>                
struct CALLER                 
{                    
    static void do_call()               
    {                    
    std::cout << __PRETTY_FUNCTION__ << std::endl;        
    F();                   
    };                    
};                    

void f()                   
{                    
    std::cout << __PRETTY_FUNCTION__ << std::endl;         
}                    

int main()                  
{                    
    CALLER<f>::do_call();               
    return(0);                  
}                    
0

Ho pensato di condividere la mia soluzione per funzioni standard, estesa da altre risposte qui. Questo utilizza parametrici variadici come una mano corta. Non sarebbe difficile (solo noioso) farlo come un insieme di modelli N-ari. È interessante notare che i modelli N-ari sono più flessibili per questo modello, poiché la struttura nidificata non sarebbe più necessaria. Compila con g++ -std=c++0x

template <typename F> 
struct caller; 

template <class R, class ... A> 
struct caller<R(A ...)>{ 
    template <R F(A ...)> 
    struct func{ 
     R operator()(A ... args){ 
     return F(args ...); 
     } 
    }; 
}; 

che viene richiamato e definito così:

int echoFunc(int i) {std::cout << "echo " << i << std::endl; return i;} 
... 
caller<int(int)>::func<echoFunc> f; 
f(1); 

Senza -O2 compila a due chiamate di funzione immediati annidate, con -O2 la chiamata a f(1) riduce ad un immediato invito echoFunc.

Questo funziona come previsto per le funzioni membro, con gcc> = 4.5 e vs2008.

0

È anche possibile passare un riferimento di funzione a un modello. Questo compila nell'ultimo clang (3.2) e stampa "Hello World!" come ci si aspetterebbe:

template<void(& f)()> struct test { 
    void operator()() { 
     f(); 
    } 
}; 
void foo() { 
    std::cout << "Hello World!\n"; 
} 
test<foo> bar; 
int main() { 
    bar(); 
} 

Non sono sicuro se questo sarà davvero fare la differenza rispetto all'utilizzo di un puntatore a funzione, però.

Problemi correlati