Mir ist diese Frage bekannt, in der Boosts "STATISCHE WARNUNG" erwähnt wird, aber ich möchte noch einmal speziell fragen, wie ich eine implementieren könnte, static_warning
die ähnlich funktioniert, static_assert
aber nur zur Kompilierungszeit eine Warnung ausgibt, anstatt einen Kompilierungsfehler abzubrechen.
Ich hätte gerne etwas Ähnliches wie Alexandrescus Vorschlag für eine statische Zusicherung in C-11-Tagen vor C ++, die es irgendwie geschafft hat, einige nützliche Kontextinformationen als Teil des Fehlers zu drucken.
Es wäre akzeptabel zu verlangen, dass der Benutzer bestimmte Standard-Compiler-Warnungen aktiviert, damit diese Konstruktion funktioniert (möglicherweise "ungültige Zeigerkonvertierung" oder "Verstoß gegen strenge Aliasing-Regeln") - jede Warnung, die ohnehin Teil einer normalen Kompilierung sein sollte, kann dies verwendet werden.
Kurz gesagt, ich möchte static_warning(false, "Hello world");
eine Compiler-Warnung erstellen, die irgendwie die Zeichenfolge "Hallo Welt" in die Warnmeldung aufnehmen sollte. Ist das möglich, etwa in GCC und MSVC, und wie?
Ich würde gerne eine kleine Belohnung für eine besonders clevere Lösung ausgeben.
Zur Erklärung: Ich kam auf die Idee, als ich über diese Frage nachdachte : Eine statische Warnung wäre eine nützliche Methode, um den Kompilierungsprozess komplexer Vorlagenspezialisierungen zu verfolgen, die ansonsten ziemlich schwer zu debuggen sind. Eine statische Warnung kann als einfaches Signal für den Compiler verwendet werden, um "Ich kompiliere jetzt diesen Teil des Codes" auszugeben.
Aktualisieren. Im Idealfall wird die Warnung im folgenden Setup ausgelöst:
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; }
quelle
#error
,#warning
,#message
) so vielleicht , dass es Sinn machen würde , um tatsächlich die in gcc und Clang zu implementieren?#warning
handelt von Präprozessor-Warnungen, soweit mir bekannt ist, und hat nichts mit Vorlageninstanziierungen zu tun.deprecated
Attribut auf Variablen, Typen und Funktionen angewendet werden. Dies kann eine beliebige Nachricht enthalten (siehe gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html#Type-Attributes ). Ich habe versucht, eine Lösung damit zu hacken, aber bisher entziehen sich mir die Details. Es könnte jedoch eine praktikable Lösungskomponente sein.Antworten:
Ausspielung von Michael E's Kommentar:
#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__)
Das Makro kann im Namespace, in der Struktur und im Funktionsumfang aufgerufen werden. Angesichts der Eingabe:
#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 (auf Standardwarnstufe) erzeugt:
Während Visual C ++ 2010 (bei / W3 oder höher) sagt:
Clang ++ 3.1 unter Linux erzeugt die wohl schönere Ausgabe (Farbe nicht gezeigt):
quelle
wrap<char> w;
) keine Warnung ausgegeben wird . Kann das überwunden werden?STATIC_WARNING_TEMPLATE
, um Ihr Problem zu beheben , jedoch mit erheblichen Kosten: Durch die Verwendung von STATIC_WARNING_TEMPLATE wird die Bedeutung eines Programms durch Hinzufügen einer Variablen (mit einem eigenen Standardkonstruktor) geändert.Foo<int>::value
. Ich habe noch nichts herausgefunden, was wird.Hier ist das Beste, was ich mir bisher ausgedacht habe. Es ist grundlegend und entspricht nicht ganz Ihren Anforderungen. Stattdessen muss
BOOST_MPL_ASSERT_MSG
Ihre Nachricht die Form einer gültigen Kennung haben. (Soweit ich weiß, können Sie eine Zeichenfolge nur dann in die Warnmeldung drucken lassen, wenn die von Ihnen verwendete Warnung zufällig auch mit Zeichenfolgen zu tun hat und deren Inhalt gedruckt wird.)Die Warnung für eine nicht verwendete Variable muss aktiviert sein. In g ++ ist dies
-Wunused-variable
(aktiviert durch-Wall
) und in MSVC ist es die Warnung C4101, die auf Warnungsebene 3 aktiviert ist.Es ist offensichtlich nicht sehr getestet und könnte auf einige Arten verbessert werden (Verwendung
__COUNTER__
anstelle von__LINE__
unterstützten Compilern, hübscheres Drucken von Nachrichten, Verwenden von Boost zur Vereinfachung usw.), scheint aber die Aufgabe zu erledigen. Hier ist die Kesselplatte: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__)
Und ein Test:
STATIC_WARNING(sizeof(int) == 2); int main() { STATIC_WARNING_MSG(sizeof(char) != 1, JUST_KIDDING_ALL_IS_WELL); }
In MSVC erzeugt dies:
>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
Und in GCC produziert es:
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'
quelle
Hier ist eine Lösung, die die Boost MPL-Bibliothek verwendet:
#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__)
Es kommt mit der gleichen Einschränkung wie die Lösung von GMan: Die Nachricht muss eine gültige Kennung sein. Hier sind zwei Tests
static_warning(sizeof(int) == 4, size_of_int_is_not_4);
und
static_warning(sizeof(int) == 2, size_of_int_is_not_2);
Mit MSVS 2010 wird der erste Test ohne Warnungen kompiliert, der zweite mit der Warnung
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> ]
Der Code verwendet boost :: mpl :: print. Aus dem Buch C ++ Template Metaprogramming von D. Abrahams und A. Gurtovoy, Seite 171:
Um ein Ausführungsprotokoll zur Kompilierungszeit zu erstellen, benötigen wir eine Möglichkeit, eine Diagnosemeldung zu generieren - eine Warnung. Da es kein einzelnes Konstrukt gibt, das alle Compiler veranlasst, eine Warnung zu generieren (tatsächlich lassen Sie die meisten Compiler Warnungen insgesamt deaktivieren), verfügt MPL über eine
print
Metafunktion, die genau so ist,identity
dass sie so optimiert ist, dass eine Warnung für eine Vielzahl gängiger Compiler generiert wird ihre üblichen Einstellungen.quelle
#include <boost/mpl/eval_if.hpp>
und auchtypename
vor demboost::eval_if_c
. Wie auch immer, ich kann damit nichts drucken (GCC 4.6.2); Die Kompilierung wird nur ohne Nachricht durchgeführt ...