2012-03-30 7 views
12

Problema in breve:
Come si può implementare la funzionalità static if, proposta in C++ 11, in semplice C++?statico se in chiaro C++?

Storia e problema originale:
Recentemente mi è venuto in mente un problema di questo tipo. Ho bisogno di una classe di Sender con un'interfaccia simile

class Sender 
{ 
    void sendMessage(...); 
    void sendRequest(...); 
    void sendFile(...); 
    // lots of different send methods, not important actually 
} 

In alcuni casi ho bisogno di creare un DoubleSender, vale a dire un'istanza di questa classe, che avrebbe chiamato i suoi metodi per due volte, vale a dire al momento della chiamata, diciamo, un metodo sendMessage(...), lo stesso messaggio deve essere inviato due volte.

mie soluzioni:
Primo approccio:
Avere un isDouble membro, e alla fine di ogni chiamata di metodo fare un controllo

sendMessage(...) { ... if(isDouble) { sendMessage(...); } 

Beh, io non voglio questo, perché in realtà avrò bisogno di un doppio post molto recentemente, e questa parte del codice nella sezione time-critical sarà passiva al 98%.

Secondo approccio:
Ereditare una classe DoubleSender da Sender, e mettere in atto i suoi metodi come:

void DoubleSender::sendMessage(...) 
{ 
    Sender::sendMessage(...); 
    Sender::sendMessage(...); 
} 

Beh, questo è accettabile, ma richiede molto spazio di codice sgradevole (davvero molto, perché ci sono un sacco di diversi metodi send..

Terzo approccio:.
Ima gine che sto usando C++ 11 :). Allora posso fare questa classe generica e produrre la parte necessaria di codice secondo tempalte argomento utilizzando static if:

enum SenderType { Single, Double }; 
template<SenderType T> 
class Sender 
{ 
    void sendMessage(...) 
    { 
     // do stuff 
     static if (T == Single) 
     { 
     sendMessage(...); 
     } 
    } 
}; 

Questo è più corto, più facile da leggere rispetto alle soluzioni precedenti, non genera codice aggiuntivo e ... è C++ 11, che purtroppo non posso usare nel mio lavoro.

Quindi, ecco dove sono arrivato alla mia domanda: come posso implementare l'analogo static if in C++?
Inoltre, gradirei qualsiasi altro suggerimento su come risolvere il mio problema originale.
Grazie in anticipo.

+1

Si potrebbe guardare a come Boost lo fa con la sua [static_assert] (http://www.boost.org/doc/libs/1_49_0/doc/html/boost_staticassert.html) forse? –

+0

Si consiglia di familiarizzare con boost.mpl – Anycorn

+10

'static se non esiste in C++ 11. –

risposta

8

Citando @JohannesSchaubLitb

with my static_if that works on gcc one can do it :)
in qualche modo limitato

(vedi anche here)

Questo trucco comporta una specifica interpretazione GCC delle specifiche sul Lambda in C++ 11.Come tale, (probabilmente) diventerà un rapporto sui difetti rispetto allo standard. Questo porterà al trucco che non funziona più nella versione più recente di GCC (non funziona già in 4.7).

Vedere il commento filo sotto per qualche dettaglio in più da Johanness

http://ideone.com/KytVv:

#include <iostream> 

namespace detail { 
template<bool C> 
struct call_if { template<typename F> void operator<<(F) { } }; 

template<> 
struct call_if<true> { 
    template<typename F> 
    void operator<<(F f) { f(); } 
}; 
} 

#define static_if(cond) detail::call_if<cond>() << [&] 

template<bool C, typename T> 
void f(T t) { 
    static_if(C) { 
    t.foo(); 
    }; 
} 

int main() { 
    f<false>(42); 
} 
+0

Questa cosa funziona solo su GCC 4.5.1 a causa di un bug nel fatto che il contenuto della lambda non viene controllato correttamente. Si dovrebbe ottenere un "tipo" int "non ha un errore con nome" foo "". – Xeo

+1

@Xeo puoi essere più preciso?Non è un bug ma un'interpretazione di ciò che lo Standard non definisce con molta precisione (l'istanziazione dei membri delle classi locali non verrà eseguita a meno che non siano richiesti da GCC e Clang). Comeau/EDG lo interpretano in modo diverso e istanziano sempre tutti i membri della classe locale. Avremo questo come un nuovo rapporto di problema nella prossima lista di problemi. –

+0

Sfortunatamente GCC4.7 appare sempre per istanziare operator() (per qualsiasi motivo), quindi questo trucco non funziona più con loro. Né per Clang :( –

7

Perché non rendere obbligatoria l'applicazione di invio una politica della classe mittente e utilizzare CRTP:

template<class Derived> 
class SingleSenderPolicy 
{ 
    public: 
    template< class memFunc > 
    void callWrapperImpl(memFunc f, ...) 
    { 
     static_cast<Derived *>(this)->f(...); 
    } 
}; 

template< class Derived > 
class DoubleSenderPolicy 
{ 
    public: 
    template< class memFunc > 
    void callWrapperImpl(memFunc f, ...) 
    { 
     static_cast<Derived *>(this)->f(...); 
     static_cast<Derived *>(this)->f(...); 
    } 
}; 

template< class SendPolicy> 
class Sender : public SendPolicy<Sender> 
{ 
public: 
    void sendMessage(...) 
    { 
     // call the policy to do the sending, passing in a member function that 
     // acutally performs the action 
     callWrapperImpl(&Sender::sendMessageImpl, ...); 
    } 

    void doSomethingElse(...) 
    { 
     callWrapperImpl(&Sender::doSomethingElseImpl, ...); 
    } 


protected: 
    void sendMessageImpl(...) 
    { 
     // Do the sending here 
    } 

    void doSomethingElseImpl(...) 
    { 
     // Do the sending here 
    } 
}; 

Le funzioni pubbliche di sendXXX nella tua classe semplicemente inoltrano al wrapper di chiamata, passando in una funzione membro che implementa la funzionalità reale. Questa funzione membro verrà chiamata in base allo SendPolicy della classe. CRTP salva l'uso di bind per avvolgere gli argomenti e questo puntatore verso l'alto con la funzione membro da chiamare.

Con una sola funzione non riduce la quantità di codice, ma se si hanno molte chiamate potrebbe essere d'aiuto.

Nota: questo codice è uno scheletro per fornire una possibile soluzione, non è stato compilato.

Nota: Sender<DoubleSenderPolicy> e Sender<SingleSenderPolicy> sono tipi completamente diversi e non condividono una relazione di ereditarietà dinamica.

+1

+1 per la risposta "seria". Il design basato su criteri è davvero una scelta naturale qui – sehe

5

maggior parte dei compilatori fanno costante ripiegamento e la rimozione codice morto, quindi se si scrive un regolare se dichiarazione come questa:

enum SenderType { Single, Double }; 
template<SenderType T> 
class Sender 
{ 
    void sendMessage(...) 
    { 
     // do stuff 
     if (T == Single) 
     { 
     sendMessage(...); 
     } 
    } 
}; 

Il ramo se otterrà rimosso quando viene generato il codice.

La necessità di static if è quando le istruzioni causano un errore del compilatore. Così dicono hai avuto qualcosa di simile (il suo codice in qualche modo pseudo):

static if (it == random_access_iterator) 
{ 
    it += n; 
} 

Dal momento che non è possibile chiamare += su iteratori di accesso non casuale, allora il codice sarebbe sempre non riescono a compilare con un normale if, anche con la rimozione del codice morto. Perché il compilatore controllerà ancora la sintassi prima di rimuovere il codice. Quando si utilizza static if, il compilatore salterà il controllo della sintassi se la condizione non è vera.

+1

salterà davvero l'analisi della sintassi o solo l'analisi semantica? –

+0

@Janus salterà l'analisi semantica. Ha ancora bisogno di fare analisi sintattica per trovare la fine del blocco condizionale. – Ponkadoodle

0
std::string a("hello world"); 
// bool a = true; 
if(std::is_same<std::string, decltype(a)>::value) { 
    std::string &la = *(std::string*)&a; 
    std::cout << "std::string " << la.c_str() << std::endl; 
} else { 
    bool &la = *(bool*)&a; 
    std::cout << "other type" << std::endl; 
}