Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

1

18.10.2014, 14:47

Template Instanziierungsfehler im VisualC++ 2013 Compiler

Hi zusammen,
ich bin mir ziemlich sicher, einen Fehler im VisualC++ 2013 Compiler gefunden zu haben.
Und zwar wird meine (nicht ganz triviale) Template Klasse von VC2013 nicht korrekt instanziiert.

Ich will eigentlich nur eine abstrakte "Angle" Klasse haben, mit der ich komfortabel zwischen Degree und Radian hin- und her switchen kann.
Einfach mal in der "main" Funktion das "#if 1" durch "#if 0" austauschen und ihr werdet feststellen, dass da was nicht stimmt:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// Template Instantiation Test with "Visual C++ 2013" (C++11)
// 18/10/2014 by Lukas Hermanns

#include <iostream>
#include <cstdlib>


/* --- Global constants --- */

static const float pi       = 3.14159265359f;
static const double pi64    = 3.1415926535897932384626433832795028841971693993751;


/* --- Angle base class --- */

/**
Angle base class. This class stores a value in common angle units which can be converted into radians or degrees.
The internal angle units are in the range [0.0 .. 1.0), i.e. 1.0 is a 360 degrees, or rather a 2*pi radians rotation.
\tparam T Specifies the data type. This should be float or double.
\tparam Definition Specifies the definition of a full rotation. This must be a struct that implements the following interface:
\code
struct ExampleDefinition
{
    // Full rotation. This can be 1.0 (for abstract abgles), 360.0 (for degrees) or 2*PI<T>() (for radians).
    static T FullRotation();
};
\endcode
*/
template <typename T, class Definition> class AngleBase
{
    
    public:
        
        typedef AngleBase<T, Definition> ThisType;

        AngleBase() = default;
        //! Constructor for implicit conversion between angle definitions.
        template <typename OtherDefinition> AngleBase(const AngleBase<T, OtherDefinition>& other) :
            angle_{ other.NormalizedAngle() * ThisType::FullRotation() }
        {
        }
        explicit AngleBase(const T& angleUnits) :
            angle_{ angleUnits }
        {
        }

        inline ThisType& operator += (const ThisType& other)
        {
            angle_ += other.angle_;
            return *this;
        }
        inline ThisType& operator -= (const ThisType& other)
        {
            angle_ -= other.angle_;
            return *this;
        }

        inline ThisType& operator *= (const T& factor)
        {
            angle_ *= factor;
            return *this;
        }
        inline ThisType& operator /= (const T& factor)
        {
            angle_ /= factor;
            return *this;
        }

        /**
        Converts this angle to an angle of the other angle type.
        \tparam OtherType This must be an "AngleBase" class, e.g. Angle, Degree or Radian.
        \return The converted angle.
        \note There is a BUG inside the MSVC12 compiler which instantiates this function wrong!
        This happens when this function is called consecutively with different template arguments.
        This totally works for GCC 4.7+ but unfortunately not correctly for MSVC12.
        */
        template <template <typename> class OtherType> inline T Get() const
        {
            return NormalizedAngle() * OtherType<T>::FullRotation();
        }

        //! Returns the internal angle. This value depends on the angle definition.
        inline T GetAngle() const
        {
            return angle_;
        }

        //! Returns the normalized angle, i.e. in the range [0.0 .. 1.0).
        inline T NormalizedAngle() const
        {
            return angle_ / ThisType::FullRotation();
        }

        /**
        Returns the full rotation of this angle definition.
        This can be 1.0 (for unit angles), 360 (for degrees) or 2*pi (for radians).
        */
        static inline T FullRotation()
        {
            return Definition::FullRotation();
        }

    private:

        T angle_ { 0 };

};


/* --- Global "AngleBase" operators --- */

template <typename T, class DefinitionLHS, class DefinitionRHS>
AngleBase<T, DefinitionLHS> operator + (const AngleBase<T, DefinitionLHS>& lhs, const AngleBase<T, DefinitionRHS>& rhs)
{
    auto result = lhs;
    result += AngleBase<T, DefinitionLHS>(rhs);
    return result;
}

template <typename T, class DefinitionLHS, class DefinitionRHS>
AngleBase<T, DefinitionLHS> operator - (const AngleBase<T, DefinitionLHS>& lhs, const AngleBase<T, DefinitionRHS>& rhs)
{
    auto result = lhs;
    result -= AngleBase<T, DefinitionLHS>(rhs);
    return result;
}

template <typename T, class Definition> AngleBase<T, Definition> operator * (const AngleBase<T, Definition>& lhs, const T& rhs)
{
    auto result = lhs;
    result *= rhs;
    return result;
}
template <typename T, class Definition> AngleBase<T, Definition> operator * (const T& lhs, const AngleBase<T, Definition>& rhs)
{
    auto result = rhs;
    result *= lhs;
    return result;
}

template <typename T, class Definition> AngleBase<T, Definition> operator / (const AngleBase<T, Definition>& lhs, const T& rhs)
{
    auto result = lhs;
    result /= rhs;
    return result;
}
template <typename T, class Definition> AngleBase<T, Definition> operator / (const T& lhs, const AngleBase<T, Definition>& rhs)
{
    auto result = rhs;
    result /= lhs;
    return result;
}


/* --- Angle --- */

//! Angle rotation definition.
template <typename T> struct AngleDefinition
{
    static inline T FullRotation()
    {
        return T(1);
    }
};

//! AngleBase specialization for unit angles in the range [0.0 .. 1.0).
template <typename T = float> using Angle = AngleBase<T, AngleDefinition<T>>;


/* --- Degree --- */

//! Degree rotation definition.
template <typename T> struct DegreeDefinition
{
    static inline T FullRotation()
    {
        return T(360);
    }
};

//! AngleBase specialization for degrees in the range [0.0 .. 360).
template <typename T = float> using Degree = AngleBase<T, DegreeDefinition<T>>;


/* --- Radian --- */

//! Radian rotation definition.
template <typename T> struct RadianDefinition
{
    static inline T FullRotation()
    {
        return T(pi64*2.0);
    }
};

//! AngleBase specialization for radians in the range [0.0 .. 2*pi).
template <typename T = float> using Radian = AngleBase<T, RadianDefinition<T>>;


/* --- Main function --- */

int main()
{
    // Abstract angle in the range [0.0 .. 1.0),
    // constructed by (90 degrees) + (pi/2 radians) = 0.5 (half rotation).
    Angle<float> angle = Degree<float>(90.0f) + Radian<float>(pi/2);

    #if 1

    auto degree = angle.Get<Degree>();
    auto radian = angle.Get<Radian>(); // MSVC COMPILER FAILURE -> angle.Get<Degree> will be instantiated but angle.Get<Radian> is required !!!

    std::cout << "Angle = " << angle.GetAngle() << std::endl;
    std::cout << "Degree = " << degree << std::endl;
    std::cout << "Radian = " << radian << std::endl;

    #else

    auto radian = angle.Get<Radian>();
    auto degree = angle.Get<Degree>(); // MSVC COMPILER FAILURE -> angle.Get<Radian> will be instantiated but angle.Get<Degree> is required !!!

    std::cout << "Angle = " << angle.GetAngle() << std::endl;
    std::cout << "Degree = " << degree << std::endl;
    std::cout << "Radian = " << radian << std::endl;

    #endif

    system("pause");
    return 0;
}


Lässt sich mit VC2013 und GCC 4.7 kompilieren.
Mit dem GCC funktioniert der Code übrigens richtig ;)

Gruß,
Lukas

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »LukasBanana« (18.10.2014, 14:56)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

2

18.10.2014, 15:09

Wegen solchem Unsinn ist C++ die Hölle. Nicht wegen den Compiler-Problemen, sondern wegen dem schlimmen Code, den man schreiben kann, ohne dass man es muss.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

3

18.10.2014, 15:15

War ja klar, dass sich gleich wieder jemand über die C++ Templates aufregt :rolleyes: .
Aber das löst mein Problem nicht.
Hast du denn wenigstens mal den Code ausprobiert?
Mich würde zumindest mal interessieren, ob ihr das selbe Problem mit MSVC12 habt, oder ob mir einfach ein Service Pack für Visual Studio fehlt.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

4

18.10.2014, 15:18

Ich habe nur ein 2010er hier, kann ich Dir also schlecht sagen. Aber ja, wieso sollte ich nicht sagen, dass das totaler Overkill ist, was Du da betreibst. Ich hoffe Du machst das aus rein wissenschaftlichem Interesse an der Sprache und nicht für praktische Einsatzzwecke. Wir hatten ja erst letztens die Diskussion über "guter Code ist kurzer Code".
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

5

18.10.2014, 15:58

Also bei mir geht es in allen Konfigurationen in einem neu erstellten Projekt in VS2013 problemlos.

Also ich bin ja auch aus praktischer Sicht unter anderem genau wegen Templates von C++ begeistert. In dem Fall muss ich BlueCobold aber auf jeden Fall recht geben: Bei der Problemstellung scheint mir dein Template-Konstrukt ziemlich übertrieben. Ich Zweifel ja den Sinn der ganzen Klasse dafür an, aber ein paar einfache Methoden würden es dafür auch tun. Im Speichern von 0 ... 1 sehe ich auch keinen Sinn, gibt es überhaupt irgendeinen Kontext in dem das Sinnvoll wäre?

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

6

18.10.2014, 16:10

Zitat von »Spiele Programmierer«

Also bei mir geht es in allen Konfigurationen in einem neu erstellten Projekt in VS2013 problemlos.

Hast du die neusten Updates für Visual Studio?

Zitat von »Spiele Programmierer«

Im Speichern von 0 ... 1 sehe ich auch keinen Sinn, gibt es überhaupt irgendeinen Kontext in dem das Sinnvoll wäre?

Das diente erst mal nur als Abstraktion. Wenn man z.B. eine 'halbe Rotation' (also 180°) durchführen will, wird einfach 0.5 (also quasi Prozentual) addiert.

Aber der Hauptgrund, warum das Template in der Funktion "Get" so komplex ist, ist weil ich folgendes schreiben will:

C-/C++-Quelltext

1
2
auto degree = Degree<float>();
auto radian = degree.Get<Radian>();

Und nicht:

C-/C++-Quelltext

1
2
auto degree = Degree<float>();
auto radian = degree.Get<Radian<float>>();

D.h. das erste Template Argument soll von "degree" angelesen werden, weil hier kein Casten stattfinden soll, sondern nur eine Konvertierung des Rotations-Raums.
Derartige Templates mögen zwar komliziert zu schreiben sein, aber wenn es erst mal funktioniert, kann die Benutzung sehr komfortabel sein.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

7

18.10.2014, 16:14

Angle.GetRadian();
Angle.GetDegree();

Aaaber das wäre natürlich viel zu wenig Code gewesen... und viel verständlicher. Lern die Dinge einfach zu halten. Ich weiß, das mögen viele C++ Entwickler überhaupt nicht und daher sehen viele Sources so aus wie das da oben. Aber ehrlich, wenn das in 5 Jahren noch jemand auf Anhieb verstehen soll, bau es simpel. Zumal diese Komplexität da oben total unnötig ist. 200 Zeilen für eine Winkel-Klasse, die Radians und Degrees liefern und entgegennehmen können soll. :cursing:
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

8

18.10.2014, 16:25

Zugegeben, für Winkel bei denen man nur Degree und Radian braucht, ist es vielleicht etwas übertrieben.
Ich halte aber lieber immer alles so generisch wie möglich.
Zum Einen weil dann der Compiler unter Umständen noch mehr Optimierungen vornehmen kann
und zum Anderen weil ich mich nicht gerne festlege, insbesondere wenn es um solche Basis Klassen geht.

Außerdem: der Code schlägt nicht fehl, weil meine Templates falsch sind, sondern weil es definitiv ein BUG im Compiler ist.
Mit dem GCC funktioniert es nämlich einwandfrei.

Ich werde wohl einfach mal ein Update installieren.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

9

18.10.2014, 16:30

Das Info-Fenster sagt 12.0.30110.00 Update 1. Ich weiß aber nicht wie neu das tatsächlich ist. Zum Kompilieren verwende ich normalerweise eh die CTP-Version.

Was spricht gegen 180, 360, PI oder PI / 2?

Das "Problem" an dem Template ist, dass eine komplexere Lösung ist, als notwendig gewesen wäre.
Ich sehe keinen Vorteil an deiner Variante, außer das sie undurchsichtiger ist.
Ich habe mich zum Beispiel auch schon häufiger dabei erwischt, Template-Spezialisierung zu verwenden, obwohl es einfache Überladungen auch tun würden.

EDIT:
Also wenn es dir um Optimierungen geht, halte ich die Klasse für total ungeeignet. In wie fern erhöhen Templates in dem Fall das Optimierungspotential? Ich sehe da keinen Zusammenhang. Der Weg über 0 ... 1 braucht außerdem nur mehr Multiplikationen und reduziert die Genauigkeit durch das Runden. Wenn es dir darum geht, würde ich ja den Winkel als Ganzzahl im ganzen Wertebereich speichern. Allerdings in den den meisten Fällen auch schon wieder übertriebener Aufwand für Winkel.

Es ist schön wenn dein Code nicht falsch ist, aber das macht ihn aber nicht sinnvoller.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Spiele Programmierer« (18.10.2014, 16:38)


LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

10

18.10.2014, 16:34

Zum Thema Radian oder Degree:
Mir hat man schon mal gesagt, dass es nur Gewöhnungssache sei, ob man mit Radian oder Degree arbeitet.
Das halte ich so für nicht ganz richtig: Ich finde es macht schon einen Unterschied, ob ich in meinen Konstanten Ausdrücken immer das "pi" mit rein schreiben muss (z.B. pi/2), oder einfache Float Literale hinschreibe (z.B. 90.0).
Letzteres finde ich nämlich angenehmer.

Aber ja, mehr als Degree und Radian braucht man eigentlich nicht.
Obwohl ich mal gehört habe, dass es noch eine dritte Einheit gibt. Weiß aber nicht mehr wie die heißt und wie da der Bereich ist.

Werbeanzeige