error C2059: syntax error: ')'



  • Hey,

    ich verwende Visual Studio 2017 mit /std:c++latest. Wenn ich folgenden Code kompiliere kommt bei mir der im Betreff genannte Fehler: error C2059: syntax error: ')'

    struct O
    {
    	template <class T1, class T2>
    	struct A {};
    };
    template<class T>
    struct argument_type;
    template<class T, class U>
    struct argument_type<T(U)>
    {
    	using type = U;
    };
    #define DEF_VAR(TYPE, NAME) argument_type<void(TYPE)>::type NAME
    int main()
    {
    	#define MEM ((O::A<int*, int>))(val)
    	#define SEQ (MEM)
    	DEF_VAR
    	(
    		((O::A<int*, int>)),
    		(val)
    	);
    }
    

    Kompiliere ich selbigen Code mit g++ funktioniert alles einwandfrei. Meine Frage also: Ist dies ein Bug in VC++? Und gibt es dafür einen Workaround?

    Grüße,

    Julian



  • Ist nicht nachvollziehbar.

    ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

    Sorry, mit latest doch. Mit C++17 tut es.



  • Hm... Scheint wohl so als würde es an der Kompilerversion liegen. Folgender Code kompiliert allerdings auch nicht mit C++17 flag:

    template<class T>
    struct argument_type;
    template<class T, class U>
    struct argument_type<T(U)>
    {
    	using type = U;
    };
    #define TDEF(TYPE, NAME) using NAME = argument_type<void(TYPE)>::type
    #define INHERIT(R, DATA, ELEM) , argument_type<void(BOOST_PP_SEQ_ELEM(0, ELEM))>::type
    #define MEMBER(R, DATA, ELEM) TDEF(BOOST_PP_SEQ_ELEM(0, ELEM), BOOST_PP_SEQ_ELEM(1, ELEM));
    #define CREATE_CLASS(NAME, MEMBERS) \
    	struct NAME \
    		: argument_type<void(BOOST_PP_SEQ_ELEM(0, BOOST_PP_SEQ_HEAD(MEMBERS)))>::type \
    		BOOST_PP_SEQ_FOR_EACH(INHERIT, _,  BOOST_PP_SEQ_TAIL(MEMBERS)) \
    	{ \
    	 	BOOST_PP_SEQ_FOR_EACH(MEMBER, _, MEMBERS) \
    	}
    struct O
    {
    	template <class T1, class T2>
    	struct A {};
    };
    CREATE_CLASS(test_class, (((O::A<int*, int>))(val)));
    

    Wobei g++ kompiliert.



  • Ja, und was möchtest du jetzt hören? Schreib einen bug-report?



  • Naja, ob es einen Workaround gibt.



  • Ich glaub' nicht, daß da in VS2017 noch was gefixt wird. VS2019 steht in den Startlöchern.

    Workaround. Ehrlich gesagt ist es mir zu anstrengend versuchen herauszufinden, was der Code tun soll. Und auf Makros bin ich allergisch.



  • @JulianH Mach nen Bug-Report. Rechts oben in der Fensterleiste gibt's nen "Send Feedback" Button. Da klickst du drauf und dann schreibst du nen Bug-Report.



  • Danke für den Hinweis @hustbaer, habe dies bereits gemacht.



  • Ich denke es ist auch sinnvoll herauszufinden, wo der Fehler genau auftritt und über was für ein Syntax-Konstrukt der Compiler da genau stolpert.

    Du solltest den Präprozessor mal eine bereits verarbeitete Datei erstellen lassen und diese dann manuell kompilieren (such mal nach "MSVC preprocess to file" oder sowas). Damit sollte sich genauer eingrenzen lassen, wo das eigentliche Problem liegt.

    Das ist hilfreich für eine Bug-Meldung und ermöglicht dir vielleicht sogar selbst einen Workaround zu finden. Soweit ich mich erinnere, hatte MSVC in der Vergangenheit öfter mal Probleme mit ein paar Template-Konstrukten, die eigentlich Standardkonform waren.

    Möglicherweise ist das auch ein bereits bekanntes Problem zu dem sich leicht Informationen finden lassen, wenn du es mal auf ein wenige Zeilen langes Minimalbeispiel eingedampft hast.



  • Habe das mal exemplarisch für den ersten von dir geposteten Code gemacht:

    1. Code in test.cpp gespeichert.
    2. VS Developer Command prompt aufgerufen
    3. cl -P test.cpp
    4. copy test.i preprocessed.cpp.
    5. preprocessed.cpp etwas aufgeräumt für Lesbarkeit:
    struct O
    {
    	template <class T1, class T2>
    	struct A {};
    };
    
    template<class T>
    struct argument_type;
    
    template<class T, class U>
    struct argument_type<T(U)>
    {
    	using type = U;
    };
    
    int main()
    {
    	argument_type<void(((O::A<int*, int>)))>::type (val);
    }
    
    1. cl -std:c++latest preprocessed.cpp
    Microsoft (R) C/C++-Optimierungscompiler Version 19.16.27026.1 für x86
    Copyright (C) Microsoft Corporation. Alle Rechte vorbehalten.
    
    preprocessed.cpp
    preprocessed.cpp(18): error C2059: Syntaxfehler: ")"
    preprocessed.cpp(18): error C2039: "type": Ist kein Element von "`global namespace'"
    preprocessed.cpp(18): error C2065: "val": nichtdeklarierter Bezeichner
    

    (sorry für deutschen MSVC, ist mir grad erst aufgefallen, dass ich da was falsch installiert habe 😉 )

    Es scheint, als mag MSVC die vielen Klammern nicht. Wenn ich Zeile 18 so schreibe:

    argument_type<void(O::A<int*, int>)>::type(val);
    

    Dann gibt es keine Fehlermeldung, obwohl ich eigentlich erwartet hätte, dass da noch ein typename davor gehört (?). Das ist schon eine Variablen-Deklaration, oder? Sieht mir schon etwas "vexing" aus 😉

    Ob der Code das tut, was du erwartest, musst du allerdings selbst beantworten.

    P.S.: Habe da noch vage Erinnerungen an Probleme mit Klammern in Template-Argumenten auch vorherigen Versionen des MSVC. Ich würde mich also nicht unbedingt darauf verlassen, dass VS19 das Problem löst, sondern die Makros so umschreiben, dass möglichst alle Compiler mit dem Resultat klarkommen oder sie stellenweise für MSVC spezialisieren.



  • typename gehört da keins hin, der Code ist ja nicht von einem Template-Argument abhängig -- er steht ja nichtmal in einem Template.
    Und ja, es scheint ne Variablen-Deklaration zu sein 🤪



  • Laß doch mal die Klammern um val weg?

    DEF_VAR
    	(
    		((O::A<int*, int>)),
    		val
    	);
    

    bzw.

    argument_type<void(((O::A<int*, int>)))>::type val;
    

    Du würdest ja auch nicht int (x); schreiben, oder?



  • @Th69 sagte in error C2059: syntax error: ')':

    Laß doch mal die Klammern um val weg?

    Das Problem für MSVC sind aber die Klammern im Template-Argument.
    Wenn ich das richtig sehe, braucht man die Klammern eigentlich nur für die Makros, damit sich diese nicht bei den Kommas verschlucken.

    Da ohnehin schon boost.preprocessor verwendet wird, gibt es vielleicht eine Möglichkeit, die überflüssigen Klammern damit zu entfernen (?) - falls man es nicht irgendwie schafft, die Makros so umzuschreiben, dass sie weniger Klammern generieren und trotzdem noch funktionieren.



  • Wie wärs denn mit dieser billigen Lösung für den ersten Code, den du gepostet hast?

    struct O
    {
        template <class T1, class T2>
        struct A {};
    };
    template<class T>
    struct argument_type;
    template<class T, class U>
    struct argument_type<T(U)>
    {
        using type = U;
    };
    #define DEF_VAR(NAME, ...) argument_type<void(__VA_ARGS__)>::type NAME
    int main()
    {
        DEF_VAR
        (
            val,
            O::A<int*, int>
        );
    }
    

    Ein Variablenname wird sicherlich keine Kommas haben, alle weiteren Makro-Argumente sind dann der Typ.



  • Nochmal nachgeschaut. boost.preprocessor hat mit BOOST_PP_REMOVE_PARENS tatsächlich ein Makro, mit dem du eine Schicht der Klammerung aus einem Text herausschälen kannst. Ich denke es geht dir vornehmlich um das zweite Listing, dort sind die zusätzlichen Klammern offenbar notwendig für die Sequenz-Datenstruktur von boost.preprocessor:

    #define TDEF(TYPE, NAME) using NAME = argument_type<void(BOOST_PP_REMOVE_PARENS(TYPE))>::type
    #define INHERIT(R, DATA, ELEM) , argument_type<void(BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(0, ELEM)))>::type
    #define MEMBER(R, DATA, ELEM) TDEF(BOOST_PP_SEQ_ELEM(0, ELEM), BOOST_PP_SEQ_ELEM(1, ELEM));
    #define CREATE_CLASS(NAME, MEMBERS) \
        struct NAME \
            : argument_type<void(BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(0, BOOST_PP_SEQ_HEAD(MEMBERS))))>::type \
            BOOST_PP_SEQ_FOR_EACH(INHERIT, _,  BOOST_PP_SEQ_TAIL(MEMBERS)) \
        { \
            BOOST_PP_SEQ_FOR_EACH(MEMBER, _, MEMBERS) \
        }
     
    CREATE_CLASS(test_class, (((O::A<int*, int>))(val)));
    

    Das erzeugt dann diese Klassen-Deklaration:

    struct test_class : argument_type<void(O::A<int*, int>)>::type 
    { 
        using val = argument_type<void(O::A<int*, int>)>::type;
    };
    

    Ich habe das nicht in MSVC getestet, aber das sollte für den Compiler leichter verdaulich sein, als der Doppelklammer-Overkill, der vorher erzeugt wurde.

    Btw... scheinbar kann man nicht nur Fortran, sondern auch Lisp in jeder Sprache schreiben 😉

    Ansonsten: Ab einem gewissen Punkt macht es eventuell Sinn, solche Klassen mit einem externen Skript während des Build-Vorgangs zu generieren, wenn sowas oft verwendet wird. Lässt sich leichter lesen und debuggen. Makros sind und bleiben gruselig.



  • Vielen Dank @Finnegan, dass du dir die Mühe gemacht hast dir den Code anzusehen, hatte es schon fast aufgeben es mit dem VC++ zum Laufen zu bringen. Mit BOOST_PP_REMOVE_PARENS() funktioniert es tatsächlich, wobei ich dafür brauche nicht mal mehr den argument_type-Wrapper benötige:

    #define TDEF(TYPE, NAME) using NAME = BOOST_PP_REMOVE_PARENS(TYPE)
    #define INHERIT(R, DATA, ELEM) , BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(0, ELEM))
    #define MEMBER(R, DATA, ELEM) TDEF(BOOST_PP_SEQ_ELEM(0, ELEM), BOOST_PP_SEQ_ELEM(1, ELEM));
    #define CREATE_CLASS(NAME, MEMBERS) \
        struct NAME \
            : BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(0, BOOST_PP_SEQ_HEAD(MEMBERS))) \
            BOOST_PP_SEQ_FOR_EACH(INHERIT, _,  BOOST_PP_SEQ_TAIL(MEMBERS)) \
        { \
            BOOST_PP_SEQ_FOR_EACH(MEMBER, _, MEMBERS) \
        }
    

    Ich frage mich immer, wenn ich sowas sehe, was bei boost eigentlich intern abgehen muss damit das möglich ist 😄

    Aber läuft, vielen Dank dafür 😎


Anmelden zum Antworten