VC++ 7.1 Bug oder GCC Feature



  • Hallo zusammen

    Kann es sein, dass VC 7.1 immer noch Probleme mit Templates hat ? Folgender Code:

    float transformx = 0.0f, transformy = 0.0f, transformz = 0.0f;
    cin >> transformx >> transformy >> transformz;
    matrix<float, 4,4> md(translate<float,4>(vec<float,3>(transformx, transformy, transformz)));
    vec<float,3> v(1.0f, 1.0f, 1.0f);
    cout << transform(v, md);
    

    transform:

    template<typename T, unsigned int DIMENSIONS>
    vec<T, DIMENSIONS> transform(const vec<T, DIMENSIONS>& v, const matrix<T, DIMENSIONS+1, DIMENSIONS+1>& m)
    

    gibt folgende Fehlermeldung:

    d:\eengine\emath\main.cpp(33) : error C2784: 'math::vec<T,DIMENSIONS> math::transform(const math::vec<T,DIMENSIONS> &,const math::matrix<T,DIMENSIONS+1,DIMENSIONS+1> &)' : could not deduce template argument for 'const math::matrix<T,DIMENSIONS+1,DIMENSIONS+1> &' from 'math::matrix<T,ROWS,COLS>'
    with
    [
    T=float,
    ROWS=4,
    COLS=4
    ]
    d:\eengine\emath\vec.h(196) : see declaration of 'math::transform'

    Mit GCC lässt sich der Code einwandfrei compilieren !!!! Kann jemand einen Fehler meinerseits erkennen ? Sonst muss ich wohl auf Devc++ umsteigen.

    Danke und Gruss Tobias



  • Hallo,
    also prinzipiell sehe ich da keinen Fehler.
    Funktioniert folgendes Minimalbeispiel mit dem VC 7.1?

    template <class T, unsigned, unsigned>
    struct Matrix
    {};
    
    template <class T, unsigned>
    struct Vector
    {};
    
    template<typename T, unsigned dim> 
    Vector<T, dim> transform(const Vector<T, dim>&, const Matrix<T, dim+1, dim+1>&)
    {
    return Vector<T, dim>();
    }
    int main()
    {
        Matrix<float, 4,4> md;
        Vector<float,3> v; 
        transform(v, md);
    }
    


  • HumeSikkins schrieb:

    template <class T, unsigned, unsigned>
    struct Matrix
    {};
    

    Sorry, mehr oder weniger Off Topic: Ich habe das aus Neugier auch mal kompiliert (VC7.1 Std) und schon an der Deklaration von md scheitert er, weil für ihn die letzten beiden Vorlangenparameter denselben Namen haben. Solange es nur einen unbenannten Vorlagenparameter gibt, geht es. Das ist nicht konform, oder? Nicht, dass es ein großer Verlust wäre :), aber das wundert mich doch schon stark...

    MSVC 7.1 schrieb:

    main.cpp(16) : error C2365: 'function-parameter': Erneute Definition; vorherige Definition war 'Vorlagenparameter'
    main.cpp(16): Siehe Deklaration von 'function-parameter'
    main.cpp(16): Siehe Verweis auf Instanziierung der kompilierten Klassenvorlage 'Matrix<T,__formal,4>'
    with
    [
    T=float,
    __formal=4
    ]

    Und um trotzdem was zum Thema beizutragen: Nein, auch bei diesem Minimalbeispiel (mit Dummy-Templateparametern) kann er dim nicht herleiten. Das + (oder auch -, wenn man es andersrum macht) scheint ihn zu verwirren 😞 Bis zu welchem Grad muss ein Compiler denn mathematische Gleichungen lösen, um den Template-Parameter herzuleiten? Muss er überhaupt?
    Als einziger hässlicher Workaround fällt mir da ein, für die Matrixdimensionen einen zweiten template-Parameter zu nehmen und per statischem Assert zu überprüfen, ob er korrekt ist.



  • Guten Morgen

    @HumeSikkins
    Genau das ist das Problem ! Aber ich hab mich jetzt damit abgefunden, dass ich die Template Parameter angebe, also:

    cout << transform<float, 3>(v, md);
    

    So funktionierts auch mit MSVC++ 7.1. Wie gesagt funktioniert mit GCC (Devc++) alles perfekt, nur der Debuger gefällt mir ned so gut wie der MSVC Debugger. Kennt jemand ausser Devc++ eine gute IDE für Windows ?

    Danke und Gruss Tobias



  • Bis zu welchem Grad muss ein Compiler denn mathematische Gleichungen lösen, um den Template-Parameter herzuleiten? Muss er überhaupt?

    Wenn es sich um Integrale Konstanten handelt, und das Ergebnis wiederum zur Initialisierung einer Intgralen Konstanten verwendet wird (Herleitung eines integralen non-type Template-Parameters, Initialisierung einer enum-Konstanten...) muss er die Rechnung zur Compilezeit ausführen. Wenn der VC 7.1 also mit dem DIM + 1 keinen non-type-Parameter vom Typ unsigned herleiten kann, dann ist das ein Fehler.

    Was mich jetzt doch sehr irritiert ist, dass selbst der VC 6.0 das Minimalbeispiel übersetzt (nachdem man den beiden non-type-Parametern von Matrix einen Namen gegeben hat).



  • Was mich jetzt doch sehr irritiert ist, dass selbst der VC 6.0 das Minimalbeispiel übersetzt (nachdem man den beiden non-type-Parametern von Matrix einen Namen gegeben hat).

    Übersetzt, aber dummerweise vergisst er entsprechenden Code zu generieren 😃

    Ah. Diesen Bug kenne ich doch noch aus meinem Loki-Port.



  • Ja toll, endlich unterstützt er partial template specialisation, dafür kann er jetzt Template parameter nicht mehr herleiten. Hab übrigends dein Minimalbeispiel auch mal compiliert:

    d:\eengine\test\main.cpp(19) : error C2784: 'Vector<T,A> transform(const Vector<T,A> &,const Matrix<T,dim+1,dim+1> &)' : could not deduce template argument for 'const Matrix<T,dim+1,dim+1> &' from 'Matrix<T,A,B>'
    with
    [
    T=float,
    A=4,
    B=4
    ]
    d:\eengine\test\main.cpp(10) : see declaration of 'transform'

    Ich hoffe mal MS wird das in nem Servicepack bereinigen.



  • Ich hoffe mal MS wird das in nem Servicepack bereinigen.

    jo, das Ding hat massenhaft Fehler. 😮 😮



  • Wenn es sich um Integrale Konstanten handelt, und das Ergebnis wiederum zur Initialisierung einer Intgralen Konstanten verwendet wird (Herleitung eines integralen non-type Template-Parameters, Initialisierung einer enum-Konstanten...) muss er die Rechnung zur Compilezeit ausführen.

    Egal, wie komplex die Berechnung ist? Bei, ... wie nennt man das ... Beispielsweise einem Rekursivem Template müssen ja auch nur 17 "Stufen" ermöglicht werden.



  • HumeSikkins schrieb:

    Wenn es sich um Integrale Konstanten handelt, und das Ergebnis wiederum zur Initialisierung einer Intgralen Konstanten verwendet wird (Herleitung eines integralen non-type Template-Parameters, Initialisierung einer enum-Konstanten...) muss er die Rechnung zur Compilezeit ausführen. Wenn der VC 7.1 also mit dem DIM + 1 keinen non-type-Parameter vom Typ unsigned herleiten kann, dann ist das ein Fehler.

    Hmm. Und wo sind da die Grenzen? Dass er nicht einmal *1 oder +0 versteht ist natürlich irgendwie blöd, aber muss er auch quadratische Gleichungen etc. lösen können?



  • Hallo,
    also ich finde im Standard nichts, dass die Komplexität einer "constant integral expression" einschränkt.

    Wenn du z.B. den Wert einer enum-Konstante gerne mit einem
    Ausdruck a là 5 * 5 * 5 / 4 * 3 % 2 + 7 - 1 * 81 initialisieren willst, nur zu.

    Selbiges gilt für non-type Template-Parameter.



  • Dass man enum-Konstanten so kompliziert initialisieren kann wie man will, hätte ich eigentlich erwartet. Aber es geht doch hier um die Herleitung und die funktioniert ja genau andersherum:

    template<unsigned X> struct Struct {};
    
    template<unsigned Y>
    unsigned func(Struct<Y * Y * Y>)
    {
        return Y;
    }
    
    int main()
    {
        return func(Struct<27>()); // 3?
    }
    

    Nicht, dass wir hier aneinander vorbeireden... muss der Compiler das dann wirklich herleiten können? Und man kann die Komplexität ja noch beliebig erhöhen...
    ...und wenn ich am Thema vorbeidenke, weist mich bitte auch darauf hin 😉



  • Oha. Nicht du denkst am Thema vorbei sondern ich. Dank des Beispiels weiß ich jetzt aber wenigstens worauf du hinaus willst 🙂

    eins vorweg:
    Dein Beispiel sieht zwar ähnlich aus, ist aber was anderes.

    Im Urbeispiel wird Dim über den non-type Parameter des Vektors hergeleitet. Danach muss der Compiler nur noch prüfen, ob die Herleitung kompatibel mit den non-type Parametern der Matrix ist. Er hat also den Wert für Dim und muss danach nur noch auf diesen Wert eins addieren.

    Dein Beispiel ist anders und funktioniert nicht.
    Das hat aber nichts mit der Komplexität des Ausdrucks zu tun.

    Selbst ein simples:

    template<unsigned X> struct Struct {}; 
    
    template<unsigned Y> 
    unsigned func(Struct<Y + 1>) 
    { 
        return Y; 
    } 
    
    int main() 
    { 
        return func(Struct<27>()); 
    }
    

    funktioniert nicht, da der Wert von Y nicht hergeleitet werden kann.

    Laut Standard kann Y hier niemals hergeleitet werden, da ein Typename wie Struct<Y + 1> niemals für die Herleitung eines non-type-Parameters verwendet wird. Das ist ein sogenannter "nondeduced context".

    Der Punkt am Urbeispiel ist, dass der non-type Parameter über den ersten Parameter hergeleitet werden kann und hergeleitet wird. Die nachfolgenden Parameter müssen dann nur noch passen, damit der Aufruf als korrekt durchgehen kann.

    PS:
    Deswegen ist folgendes Beispiel auch wieder ok:

    template<unsigned X> struct Struct {}; 
    
    template<unsigned Y> 
    unsigned func(Struct<Y>, Struct<Y * Y / Y + Y + 1 - 9>) 
    { 
        return Y; 
    } 
    
    int main() 
    { 
        return func(Struct<5>(), Struct<2>()); 
    }
    

    Hoffentlich habe ich mich nicht verrechnet 😃



  • Ach so. Und ich dachte schon, ich könnte GCC gleich als Algebrasystem missbrauchen... Danke für die Erklärung 🙂


Anmelden zum Antworten