2012-01-20 9 views
56

Sono consapevole di this question che cita "ATTENZIONE STATIC" di Boost, ma vorrei chiedere ancora una volta, in particolare, come ho potuto realizzare un static_warning che funziona in modo simile a static_assert ma emette solo un avvertimento al momento della compilazione, piuttosto che un errore di compilazione fallito.Esiste uno static_warning?

Mi piacerebbe qualcosa di simile alla proposta di Alexandrescu per un asser statico in pre-C++ 11 giorni che in qualche modo è riuscito a stampare alcune utili informazioni contestuali come parte dell'errore.

Sarebbe accettabile richiedere all'utente l'abilitazione di alcuni avvisi standard del compilatore affinché questa costruzione funzioni (forse "conversione puntatore non valida" o "interruzione di regole di aliasing rigide") - qualsiasi avviso che dovrebbe far parte di un è comunque possibile utilizzare la compilazione normale.

In breve, voglio static_warning(false, "Hello world"); per creare un avviso del compilatore che dovrebbe in qualche modo includere la stringa "ciao mondo" nel messaggio di avviso. È possibile, diciamo in GCC e MSVC, e come?

Sarei felice di regalare una ricompensa in cambio di una soluzione particolarmente intelligente.


come un po 'di spiegazione: ho avuto l'idea quando si parla di this question: Un avvertimento statica sarebbe un modo utile per rintracciare attraverso il processo di compilazione delle specializzazioni template complessi, che sono altrimenti abbastanza difficile eseguire il debug . Un avvertimento statico potrebbe essere usato come un semplice faro affinché il compilatore emetta "Sto compilando questa parte del codice."


Aggiornamento . Idealmente, l'avviso dovrebbe essere attivato nella seguente configurazione:

template <typename T> struct Foo 
{ 
    static_warning(std::is_pointer<T>::value, "Attempting to use pointer type."); 
    // ... 
}; 

int main() { Foo<int> a; Foo<int*> b; } 
+0

Sei alla ricerca di portabilità? So che alcuni compilatori implementano ganci simili per il preprocessore ('# error',' # warning', '# message'), quindi forse che avrebbe senso implementare effettivamente quelli in gcc e Clang? –

+1

@VioletGiraffe: '# warning' riguarda gli avvisi del preprocessore per quanto ne so e non ha nulla a che fare con le istanze dei template. –

+2

GCC consente di applicare l'attributo 'deprecato' a variabili, tipi e funzioni; questo può includere un messaggio arbitrario (vedi http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html#Type-Attributes).Ho provato a creare una soluzione per usarlo, ma finora i dettagli mi sfuggono; potrebbe essere un componente di soluzione fattibile, però. –

risposta

37

Playing fuori dal commento di Michael E:

#if defined(__GNUC__) 
#define DEPRECATE(foo, msg) foo __attribute__((deprecated(msg))) 
#elif defined(_MSC_VER) 
#define DEPRECATE(foo, msg) __declspec(deprecated(msg)) foo 
#else 
#error This compiler is not supported 
#endif 

#define PP_CAT(x,y) PP_CAT1(x,y) 
#define PP_CAT1(x,y) x##y 

namespace detail 
{ 
    struct true_type {}; 
    struct false_type {}; 
    template <int test> struct converter : public true_type {}; 
    template <> struct converter<0> : public false_type {}; 
} 

#define STATIC_WARNING(cond, msg) \ 
struct PP_CAT(static_warning,__LINE__) { \ 
    DEPRECATE(void _(::detail::false_type const&),msg) {}; \ 
    void _(::detail::true_type const&) {}; \ 
    PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \ 
} 

// Note: using STATIC_WARNING_TEMPLATE changes the meaning of a program in a small way. 
// It introduces a member/variable declaration. This means at least one byte of space 
// in each structure/class instantiation. STATIC_WARNING should be preferred in any 
// non-template situation. 
// 'token' must be a program-wide unique identifier. 
#define STATIC_WARNING_TEMPLATE(token, cond, msg) \ 
    STATIC_WARNING(cond, msg) PP_CAT(PP_CAT(_localvar_, token),__LINE__) 

La macro può essere richiamata nello spazio dei nomi, struttura e ambito della funzione. Dato l'ingresso:

#line 1 
STATIC_WARNING(1==2, "Failed with 1 and 2"); 
STATIC_WARNING(1<2, "Succeeded with 1 and 2"); 

struct Foo 
{ 
    STATIC_WARNING(2==3, "2 and 3: oops"); 
    STATIC_WARNING(2<3, "2 and 3 worked"); 
}; 

void func() 
{ 
    STATIC_WARNING(3==4, "Not so good on 3 and 4"); 
    STATIC_WARNING(3<4, "3 and 4, check"); 
} 

template <typename T> struct wrap 
{ 
    typedef T type; 
    STATIC_WARNING(4==5, "Bad with 4 and 5"); 
    STATIC_WARNING(4<5, "Good on 4 and 5"); 
    STATIC_WARNING_TEMPLATE(WRAP_WARNING1, 4==5, "A template warning"); 
}; 

template struct wrap<int>; 

GCC 4.6 (a livello di avviso predefinito) produce:

 
static_warning.cpp: In constructor ‘static_warning1::static_warning1()’: 
static_warning.cpp:1:1: warning: ‘void static_warning1::_(const detail::false_type&)’ 
    is deprecated (declared at static_warning.cpp:1): Failed with 1 and 2 [-Wdeprecated-declarations] 
static_warning.cpp: In constructor ‘Foo::static_warning6::static_warning6()’: 
static_warning.cpp:6:3: warning: ‘void Foo::static_warning6::_(const detail::false_type&)’ 
    is deprecated (declared at static_warning.cpp:6): 2 and 3: oops [-Wdeprecated-declarations] 
static_warning.cpp: In constructor ‘func()::static_warning12::static_warning12()’: 
static_warning.cpp:12:3: warning: ‘void func()::static_warning12::_(const detail::false_type&)’ 
    is deprecated (declared at static_warning.cpp:12): Not so good on 3 and 4 [-Wdeprecated-declarations] 
static_warning.cpp: In constructor ‘wrap<T>::static_warning19::static_warning19() [with T = int]’: 
static_warning.cpp:24:17: instantiated from here 
static_warning.cpp:19:3: warning: ‘void wrap<T>::static_warning19::_(const detail::false_type&) [with T = int]’ 
    is deprecated (declared at static_warning.cpp:19): Bad with 4 and 5 [-Wdeprecated-declarations] 

Mentre Visual C++ 2010 (a/W3 o superiore) dice:

 
warnproj.cpp(1): warning C4996: 'static_warning1::_': Failed with 1 and 2 
warnproj.cpp(1) : see declaration of 'static_warning1::_' 
warnproj.cpp(6): warning C4996: 'Foo::static_warning6::_': 2 and 3: oops 
warnproj.cpp(6) : see declaration of 'Foo::static_warning6::_' 
warnproj.cpp(12): warning C4996: 'func::static_warning12::_': Not so good on 3 and 4 
warnproj.cpp(12) : see declaration of 'func::static_warning12::_' 
warnproj.cpp(19): warning C4996: 'wrap<T>::static_warning19::_': Bad with 4 and 5 
    with 
    [ 
     T=int 
    ] 
warnproj.cpp(19) : see declaration of 'wrap<T>::static_warning19::_' 
    with 
    [ 
     T=int 
    ] 
warnproj.cpp(19) : while compiling class template member function 'wrap<T>::static_warning19::static_warning19(void)' 
    with 
    [ 
     T=int 
    ] 
warnproj.cpp(24) : see reference to class template instantiation 'wrap<T>::static_warning19' being compiled 
    with 
    [ 
     T=int 
    ] 

Clang ++ 3.1 su Linux produce l'output discutibilmente migliore (colore non mostrato):

 
tst3.cpp:1:1: warning: '_' is deprecated: Failed with 1 and 2 
     [-Wdeprecated-declarations] 
STATIC_WARNING(1==2, "Failed with 1 and 2"); 
^ 
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING' 
    PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \ 
            ^
tst3.cpp:6:3: warning: '_' is deprecated: 2 and 3: oops 
     [-Wdeprecated-declarations] 
    STATIC_WARNING(2==3, "2 and 3: oops"); 
^
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING' 
    PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \ 
            ^
tst3.cpp:12:3: warning: '_' is deprecated: Not so good on 3 and 4 
     [-Wdeprecated-declarations] 
    STATIC_WARNING(3==4, "Not so good on 3 and 4"); 
^
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING' 
    PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \ 
            ^
tst3.cpp:19:3: warning: '_' is deprecated: Bad with 4 and 5 
     [-Wdeprecated-declarations] 
    STATIC_WARNING(4==5, "Bad with 4 and 5"); 
^
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING' 
    PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \ 
            ^
tst3.cpp:23:17: note: in instantiation of member function 
     'wrap<int>::static_warning19::static_warning19' requested here 
template struct wrap<int> 
       ^
4 warnings generated. 
+0

Perché non stai utilizzando l'attributo gcc 'warning'? – PlasmaHH

+0

@PlasmaHH: Dal GC texinfo: 'warning (" message ")': "Se questo attributo viene utilizzato su una dichiarazione di funzione e una chiamata a tale funzione non viene eliminata tramite ... ottimizzazioni, un avvertimento che includerà' messaggio sarà diagnosticato. " Non volevo che l'ottimizzazione fosse una preoccupazione. Dopo tutto, le chiamate alla funzione STATIC_WARNING (...) sono progettate per essere completamente ottimizzate. – Managu

+0

Ah, strano, avevo l'impressione che avvertimento e deprecato avessero gli stessi requisiti lì e un messaggio diverso. – PlasmaHH

15

Ecco il migliore che è venuta in mente finora. È semplice e non si adatta perfettamente alle tue esigenze, ma invece va per il percorso di BOOST_MPL_ASSERT_MSG in quanto il tuo messaggio deve assumere la forma di un identificatore valido. (Per quanto ne so, l'unico modo per ottenere una stringa stampata nel messaggio di avviso è se l'avvertenza che hai usato è anche una cosa da fare con le stringhe e stamparne il contenuto.)

Richiede l'avviso per una variabile non utilizzata essere abilitata. In g ++ è -Wunused-variable (attivato per -Wall), in MSVC è avvertimento C4101 che è attivata a livello di avviso 3.

E 'ovviamente poco testato e potrebbe essere migliorata in diversi modi (utilizzare __COUNTER__ anziché __LINE__ sulla supportato compilatori, stampa di messaggi più carini, utilizzare Boost per semplificare, ecc.), ma sembra che il lavoro sia svolto.Ecco la caldaia-piatto:

namespace detail 
{ 
    template <bool Condition> 
    struct static_warning; 

    template <> 
    struct static_warning<true> 
    { 
     template <typename Message> 
     static void warn() {} 
    }; 

    template <> 
    struct static_warning<false> 
    { 
     // If you're here because of a warning, please see where the 
     // template was instantiated for the source of the warning. 
     template <typename Message> 
     static void warn() { Message STATIC_WARNING_FAILED; } 
    }; 
} 

#define STATIC_WARNING_DETAIL_EX(cond, msg, line)     \ 
     struct static_warning ## line        \ 
     {               \ 
      class msg {};           \ 
                    \ 
      static_warning ## line()        \ 
      {              \ 
       ::detail::static_warning<(cond)>::     \ 
        warn<void************ (msg::************)()>(); \ 
      }              \ 
     } 

#define STATIC_WARNING_DETAIL(cond, msg, line) \ 
     STATIC_WARNING_DETAIL_EX(cond, msg, line) 

// Use these: 
#define STATIC_WARNING_MSG(cond, msg) \ 
     STATIC_WARNING_DETAIL(cond, msg, __LINE__) 

#define STATIC_WARNING(cond) \ 
     STATIC_WARNING_DETAIL(cond, STATIC_WARNING_FAILED, __LINE__) 

E un test:

STATIC_WARNING(sizeof(int) == 2); 

int main() 
{ 
    STATIC_WARNING_MSG(sizeof(char) != 1, JUST_KIDDING_ALL_IS_WELL); 
} 

In MSVC questo produce:

>main.cpp(19): warning C4101: 'STATIC_WARNING_FAILED' : unreferenced local variable 
>   main.cpp(45) : see reference to function template instantiation 'void detail::static_warning<false>::warn<void************(__thiscall static_warning45::STATIC_WARNING_FAILED::* ***********)(void)>(void)' being compiled 
>main.cpp(19): warning C4101: 'STATIC_WARNING_FAILED' : unreferenced local variable 
>   main.cpp(49) : see reference to function template instantiation 'void detail::static_warning<false>::warn<void************(__thiscall main::static_warning49::JUST_KIDDING_ALL_IS_WELL::* ***********)(void)>(void)' being compiled 

E nel GCC produce:

main.cpp: In static member function 'static void detail::static_warning<false>::warn() [with Message = void************ (static_warning39::STATIC_WARNING_FAILED::************)()]': 
main.cpp:39:1: instantiated from here 
main.cpp:19:38: warning: unused variable 'STATIC_WARNING_FAILED' 
main.cpp: In static member function 'static void detail::static_warning<false>::warn() [with Message = void************ (main()::static_warning43::JUST_KIDDING_ALL_IS_WELL::************)()]': 
main.cpp:43:5: instantiated from here 
main.cpp:19:38: warning: unused variable 'STATIC_WARNING_FAILED' 
+3

Questo è molto interessante, ma sembra funzionare solo in una funzione libera o contesto globale. Non riesco a stampare alcun avviso all'interno di un modello di classe ... –

4

Ecco una soluzione che utilizza la libreria Boost MPL:

#include <boost/mpl/eval_if.hpp> 
#include <boost/mpl/identity.hpp> 
#include <boost/mpl/print.hpp> 

#define static_warning_impl2(cond, msg, line) \ 
    struct static_warning_ ## line { \ 
     struct msg {}; \ 
     typedef typename boost::mpl::eval_if_c< \ 
      cond, \ 
      boost::mpl::identity<msg>, \ 
      boost::mpl::print<msg> \ 
     >::type msg ## _; \ 
    } 

#define static_warning_impl1(cond, msg, line) \ 
    static_warning_impl2(cond, msg, line) 

#define static_warning(cond, msg) \ 
    static_warning_impl1(cond, msg, __LINE__) 

Viene fornito con la stessa restrizione come la soluzione di Gman: il messaggio deve essere un identificatore valido. Qui ci sono due test

static_warning(sizeof(int) == 4, size_of_int_is_not_4); 

e

static_warning(sizeof(int) == 2, size_of_int_is_not_2); 

Con MSVS 2010 il primo test viene compilato senza avvisi, la seconda compila con l'avvertimento

C:\Libraries\Boost\boost_1_48_0\boost/mpl/print.hpp(51): warning C4308: negative integral constant converted to unsigned type 
    C:\Libraries\Boost\boost_1_48_0\boost/mpl/eval_if.hpp(63) : see reference to class template instantiation 'boost::mpl::print<T>' being compiled 
    with 
    [ 
     T=static_warning_28::size_of_int_is_not_2 
    ] 
    Test.cpp(28) : see reference to class template instantiation 'boost::mpl::eval_if_c<C,F1,F2>' being compiled 
    with 
    [ 
     C=false, 
     F1=boost::mpl::identity<static_warning_28::size_of_int_is_not_2>, 
     F2=boost::mpl::print<static_warning_28::size_of_int_is_not_2> 
    ] 

Il codice utilizza boost :: MPL: :stampare. Dal libro C++ Template Metaprogrammazione da D. Abrahams e A. Gurtovoy, pagina 171:

per generare un log di esecuzione in fase di compilazione, avremmo bisogno di un modo per generare un messaggio di diagnostica - un avvertimento. Perché non c'è un singolo costrutto che farà sì che tutti i compilatori generino un avviso (in effetti, la maggior parte dei compilatori ti consente di disattivare completamente gli avvisi), MPL ha una metafunzione print che è proprio come identity tranne che è sintonizzata per generare un avviso su una varietà di popolari compilatori con le loro solite impostazioni.

+0

Penso che manchi' #include < boost/mpl/eval_if.hpp> ', e anche' typename' di fronte a 'boost :: eval_if_c'. Ad ogni modo, non riesco a stampare nulla (GCC 4.6.2); la compilazione passa solo senza alcun messaggio ... –

+0

Ho risolto il problema #include e il nome tipografico mancante. Se il codice non genera alcun avviso con GCC 4.6.2, probabilmente questo è un bug di boost :: mpl :: print. Quale versione di Boost stai usando? – user763305

+0

Cosa succede su GCC 4.6.2 se si compila #include NEWLINE boost :: mpl :: print :: type a; – user763305

Problemi correlati