2012-01-21 11 views
5

Abbiamo classi modello complesse con alcuni metodi che non funzionano con determinati criteri o tipi. Pertanto, quando rileviamo quei tipi (in fase di compilazione, utilizzando i caratteri tipografici), attiviamo un'asserzione statica con un bel messaggio.Posso escludere alcuni metodi dall'istanza del modello manuale?

Ora eseguiamo anche molte istanze di template manuali. In parte è così che i metodi sono obbligati a compilare il sintattico per verificare i metodi. Riduce anche il tempo di compilazione per l'utente della biblioteca. Il problema è che le asserzioni statiche sono sempre attivate e di conseguenza non possiamo istanziare manualmente la classe template in questione.

Esiste una soluzione alternativa?

EDIT: per renderlo più chiaro, ecco un esempio (l'istanza esplicita in questo caso non riuscirà a someFunc1():

// header 
template <typename T> 
class someClass 
{ 
    void someFunc() {} 
    void someFunc1() { static_assert(false, assertion_failed); } 
}; 

// source 
template someClass<int>; // Explicit instantiation 

EDIT2:. Ecco un altro esempio Questa volta si può compilare per vedere quello che voglio dire. In primo luogo compilare subito. il codice dovrebbe compilare. Poi Decommentare [2] e l'affermazione statica dovrebbe sparare. Ora commento fuori [2] e Decommentare [1]. il asserzione statica wi Sparerai perché stai istanziando esplicitamente il modello. Voglio evitare di rimuovere l'istanziazione esplicita a causa dei benefici che ne derivano (vedi sopra per i benefici).

namespace Loki 
{ 
    template<int> struct CompileTimeError; 
    template<> struct CompileTimeError<true> {}; 
} 

#define LOKI_STATIC_CHECK(expr, msg) \ 
    { Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 

template <typename T> 
class foo 
{ 
public: 

    void func() {} 
    void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); } 
}; 

template foo<int>; 
//template foo<double>; // [1] 

int main() 
{ 
    foo<int> a; 
    a.func1(); 

    foo<double> b; 
    //b.func1(); //[2] 

    return 0; 
} 
+0

Non è chiaro dalla descrizione qual è il problema, ma dall'ambito del problema sembra che enable_if sia per voi (http://www.boost.org/doc/libs/1_48_0/libs/utility/ enable_if.html) – bobah

+0

@bobah: non è affatto una cattiva idea ... ci penserò. Se riesci a preparare un semplice esempio che posso verificare, lo contrassegnerò come risposta? – Samaursa

+1

Sono d'accordo con gli altri che si lamentano della mancanza di dettagli. Ma comunque, questo mi ha fatto suonare un campanello: "Il problema è che le asserzioni statiche sono sempre sparate". http://www.boost.org/doc/libs/1_48_0/doc/html/boost_staticassert.html#boost_staticassert.templates, ultima osservazione: è questo il problema che stai riscontrando? –

risposta

0

non ho avuto l'opportunità di testare enable_if come suggerito da bobah ma sono venuto su con una soluzione che non richiede spinta e che soddisfa il mio requisito originale in buona misura (dico buona e non piena, spiegherà alla fine)

la soluzione è quella di mettere un modello fittizio sul codice che fallirà se compilato sotto qualche selezionato tipi ed è bene sotto gli altri. Quindi:

struct dummyStruct {}; 

#define DUMMY_TEMP typename dummy 
#define DUMMY_PARAM dummyStruct 

namespace Loki 
{ 
    template<int> struct CompileTimeError; 
    template<> struct CompileTimeError<true> {}; 
} 

#define LOKI_STATIC_CHECK(expr, msg) \ 
{ Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 

template <typename T> 
class foo 
{ 
public: 

    void func() {} 
    template <typename T_Dummy> 
    void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); } 
}; 

template foo<int>; 
template foo<double>; // [1] 

int main() 
{ 
    foo<int> a; 
    a.func1<DUMMY_PARAM>(); 

    foo<double> b; 
    //b.func1<DUMMY_PARAM>(); //[2] - this is a static error 

    return 0; 
} 

In tutto il mio codice di modello, questo tipo di funzioni (vale a dire quelli che hanno statica asserisce o il lavoro su alcuni tipi e potrebbe non riuscire in altri usando tipo tratti [in questo caso c'è una selezione di diverse funzioni per diversi tipi]) sono nascoste dal client. Quindi nella mia implementazione, l'aggiunta di dummy parameter è un compromesso OK.

Come bonus, mi consente di sapere che questa funzione è progettata per essere utilizzata solo da determinati tipi. Inoltre, il mio problema originale di istanziazione esplicita è risolto da questa semplice tecnica.

2

Va bene, quindi se si sta costringendo la creazione di istanze di tutti i metodi che utilizzano istanziazione esplicita, non si può ottenere via con qualsiasi trucchi di tempo di compilazione per evitare esemplificazione dei metodi offendere, come ad esempio enable_if. Sarebbe stato abbastanza semplice spostare l'errore in runtime, ma non è consigliabile.

Penso che il meglio che si possa fare è spostare l'errore nel tempo di collegamento, che assicurerà staticamente che il programma non contenga un percorso di codice che potrebbe potenzialmente chiamare la funzione proibita, ma i messaggi di errore non saranno molto utili a chiunque non sappia della restrizione che stai imponendo. In ogni caso, la soluzione è quella di dichiarare una specializzazione delle funzioni membro proibiti, ma non definirli:

template<typename T> 
struct Foo { 
    void bar() { 
     std::cout << "bar\n"; 
    } 
    void baz() { 
     std:: cout << "baz\n"; 
    } 
}; 

template<> void Foo<int>::baz(); // use of Foo<int>::baz() will resolve to this specialization, and linking will fail 

template struct Foo<int>; 
template struct Foo<char>; 

int main() { 
    Foo<int> f; 
    f.bar(); 
    // f.baz(); // uncommenting this line results in an ugly link time error 
    Foo<char> b; 
    b.bar(); 
    b.baz(); // works with Foo<char> 
} 

La statica afferma non è più contribuire a dare bei messaggi di errore quando viene commesso un errore nel codice client, ma si potrebbe desiderare di lasciarli dentro perché spareranno se ti dimentichi di fornire una specializzazione.

+1

Questo va bene se stai creando un'istanza. Nel nostro caso stiamo usando l'istanziazione esplicita che causa il problema (vedi modifica sopra) – Samaursa

+0

Per evitare confusione per qualsiasi SOer futuro, il mio commento sopra è per la risposta _before_ le modifiche (che è stata fatta prima ho chiarito la mia domanda con alcune modifiche pure) – Samaursa

+0

+1 ** Questa dovrebbe essere la risposta. ** Richiede un po 'di lavoro ridondante ma consegna :) – CodeAngry

1

enable_if è un meccanismo flessibile per il targeting preciso dei metodi di modello, potrebbe essere ciò che si sta cercando. Esempio:

#include <string> 
#include <iostream> 

#include <boost/utility.hpp> 
#include <boost/type_traits.hpp> 
#include <boost/static_assert.hpp> 

template <class T> class mywrapper 
{ 
    T _value; 

    template <class V> 
    typename boost::enable_if<boost::is_scalar<V>, void>::type printval_(V const& value) 
    { 
    BOOST_STATIC_ASSERT(boost::is_scalar<V>::value); 
    std::cout << "scalar: " << value << std::endl; 
    } 

    template <class V> 
    typename boost::enable_if<boost::is_compound<V>, void>::type printval_(V const& value) 
    { 
    BOOST_STATIC_ASSERT(boost::is_compound<V>::value); 
    std::cout << "compound: " << value << std::endl; 
    } 

public: 
    mywrapper(T const& value):_value(value) { } 
    void printval() { printval_(_value); } 
}; 

template class mywrapper<int>; 
template class mywrapper<std::string>; 

int main() 
{ 
    mywrapper<int> ival(333); 
    mywrapper<std::string> sval("test"); 

    ival.printval(); 
    sval.printval(); 
    return 0; 
} 
+0

Attualmente non ho 'boost' al momento, ma funzionerà anche con l'istanziazione esplicita? Per esempio, subito prima di 'int main()' se metti 'template mywrapper ; template mywrapper ; 'e in ognuna delle funzioni, si ha un asser statico per assicurarsi che si stia lavorando rispettivamente con i tipi scalari e composti; lo compilerà allora? – Samaursa

+0

ha aggiornato l'esempio con le affermazioni statiche e l'istanza – bobah

+0

Grazie a Bobah! Lo verificherò appena riesco a ottenere il 'boost'. – Samaursa

3

Non si può avere entrambi: non si può avere un'affermazione statica per evitare di istanze e istanziare esplicitamente il tipo! Questa è una contraddizione evidente. Quello che si può avere, funzionalità tuttavia, è condizionatamente incluso, anche se è un po 'un dolore al collo: se una certa funzione di membro non dovrebbe essere sostenuto per determinati tipi, è possibile spostare questa funzione in una classe di base che ha condizionato lo . In questo modo non useresti un'asserzione statica ma rimuoverai semplicemente la funzione membro. Mi rendo conto che questo introduce interessanti altri problemi, ad es. per quanto riguarda la posizione delle variabili membro, ma penso che nel contesto che stai descrivendo sia il meglio che puoi ottenere.

Ecco un breve esempio di come questo potrebbe apparire come:

template <typename T, bool = std::numeric_limits<T>::is_integer> struct foo_base; 
template <typename T> struct foo_base<T, false> { /* intentionally left blank */ }; 
template <typename T> struct foo_base<T, true> { void foo() { /*...*/ } }; 

template <typename T> 
struct Foo: foo_base<T> { /* .... */ }; 

template struct Foo<int>; // will have foo() 
template struct Foo<double>; // will not have foo() 
Problemi correlati