Forum: Compiler & IDEs complex und 3D Vektoren in C++ für ARM und STM32


von Detlef _. (detlef_a)


Lesenswert?

Ihr Lieben,

ich benutze CubeIDE um STM32 Prozessoren zu bespaßen. Es gelang mir mit 
#include <complex.h> und "float complex z;" die Rechnung mit komplexen 
Zahlen in C++ komfortabel zu machen, vorher hatte ich das hingefummelt.

Das gleiche möchte ich jetzt mit 3D-Vektoren machen. Ich möchte also 
dass der Compiler "float vector v;" versteht.

Welches include brauche ich denn dafür?

THX
Cheers
Detlef

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

C++ hat keine integrierten Klassen für Vektoren fixer Länge (valarray 
ist für dynamische Länge). Du musst es dir selbst implementieren oder 
eine der Millionen Bibliotheken für lineare Algebra nutzen.

von Rbx (rcx)


Lesenswert?

Niklas G. schrieb:
> Du musst es dir selbst implementieren oder
> eine der Millionen Bibliotheken für lineare Algebra nutzen.

Ein bis zwei Seiten mit Hinweisen in gewissen guten Fortran Büchern 
sollten eigentlich reichen.
Im K&R-C-Buch steht drin, wie man Header-Dateien anlegt oder Wikipedia 
ist auch ganz gut: 
https://de.wikibooks.org/wiki/C-Programmierung:_Eigene_Header
Man kann sich natürlich aus Versehen ansehen, wie die Header-Dateien in 
der eigenen Bibliothek gestrickt sind.
Hier gibt es auch Bibliotheken in C, sicher auch nicht verkehrt, da mal 
reinzusehen:
https://www2.math.uni-wuppertal.de/wrswt/xsc/pxsc.html
https://www2.math.uni-wuppertal.de/wrswt/xsc/cxsc/apidoc/html/index.html

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Rbx schrieb:
> Im K&R-C-Buch steht drin, wie man Header-Dateien anlegt oder Wikipedia
> ist auch ganz gut:

Da steht aber nur C drin. In C++ gibt es exzellente Möglichkeiten, 
solche Dinge zu kapseln und komfortabel nutzbar zu machen. Das ist zwar 
auch eine nette Übung, erfindet aber das Rad zum tausendsten Mal neu, 
denn Algebra-Bibliotheken gibt es sehr viele, z.B. Eigen. Fortran ist 
hier allerdings total unhilfreich.

Es werden ja vermutlich auf dem Controller keine allzu komplexen 
Algorithmen benötigt. Wichtiger ist daher ein übersichtliches und gut 
nutzbares API.

von Rolf M. (rmagnus)


Lesenswert?

Detlef _. schrieb:
> ich benutze CubeIDE um STM32 Prozessoren zu bespaßen. Es gelang mir mit
> #include <complex.h> und "float complex z;" die Rechnung mit komplexen
> Zahlen in C++ komfortabel zu machen, vorher hatte ich das hingefummelt.

Das ist allerdings eigentlich der C-Weg. Da es dort keine Klassen gibt, 
hat man das fest in den Sprachkern integriert. In C++ gibt es dafür 
std::complex:
https://en.cppreference.com/w/c/numeric/complex/complex
https://en.cppreference.com/w/cpp/numeric/complex

> Das gleiche möchte ich jetzt mit 3D-Vektoren machen. Ich möchte also
> dass der Compiler "float vector v;" versteht.
> Welches include brauche ich denn dafür?

So etwas gibt es nicht. Es gäbe in C++ die Möglichkeit, ein 
std::array<float, 3> zu erzeugen, aber da gibt es keine mathematischen 
Operationen. Ein std::valarray<float> könnte man auch verwenden, aber 
das hat eine dynamische Größe, die an der Stelle zu unnötigem Overhead 
führt. Mathematische Operationen gibt es dafür, aber nur elementweise.
Am besten erzeugst du entweder selbst eine Klasse für deinen 3D-Vektor, 
oder du suchst dir eine, die schon fertig ist und kann, was du brauchst.

: Bearbeitet durch User
von Detlef _. (detlef_a)


Lesenswert?

Hi,
das Entscheidende für mich sind die Operatoren, wenn man immer add(a,b) 
schreiben muss wirds unübersichtlich.
Aber ich habs hingekriegt, vielleicht nicht schön, aber geht und ist 
nach 50Jahren C vllt. die zarte Pflanze der neuen Liebe zu C++.

Da ist bestimmt noch ne Macke drin, ich habs nur etwas getestet, also 
Vorsicht.

Und noch Fragen an die Kundigen:

Die Komponenten des Vektors sind jetzt float. Wie kann man das variabel 
machen sodass man float vec_t a und double vec_t b schreiben kann? Die 
skalaren Rückgabewerte sollten dann auch entsprechend sein.

Für const mal vector und vector mal const definiere ich ja jeweils einen 
eigenen Operator. Geht das auch eleganter?

Cheers
Detlef
1
class vec_t
2
{
3
public:
4
    float x,y,z;
5
    vec_t(){x=0;y=0;z=0;}
6
    vec_t(float x1,float y1,float z1)
7
       {x=x1;y=y1;z=z1;}
8
    vec_t(const vec_t &vec){
9
       x=vec.x;y=vec.y;z=vec.z;}
10
};
11
12
vec_t operator+(const vec_t &veca){
13
   return vec_t(veca.x,veca.y,veca.z);};
14
vec_t operator+(const vec_t &veca, const vec_t &vecb){
15
   return vec_t(veca.x+vecb.x,veca.y+vecb.y,veca.z+vecb.z);};
16
vec_t operator+(const float val,const vec_t &veca){
17
   return vec_t(veca.x+val,veca.y+val,veca.z+val);};
18
vec_t operator+(const vec_t &veca,const float val){
19
   return vec_t(veca.x+val,veca.y+val,veca.z+val);};
20
vec_t operator+(const double val,const vec_t &veca){
21
   return vec_t(veca.x+val,veca.y+val,veca.z+val);};
22
vec_t operator+(const vec_t &veca,const double val){
23
   return vec_t(veca.x+val,veca.y+val,veca.z+val);};
24
vec_t operator+(const int val,const vec_t &veca){
25
   return vec_t(veca.x+val,veca.y+val,veca.z+val);};
26
vec_t operator+(const vec_t &veca,const int val){
27
   return vec_t(veca.x+val,veca.y+val,veca.z+val);};
28
29
vec_t operator-(const vec_t &veca){
30
   return vec_t(-veca.x,-veca.y,-veca.z);};
31
vec_t operator-(const vec_t &veca, const vec_t &vecb){
32
   return vec_t(veca.x-vecb.x,veca.y-vecb.y,veca.z-vecb.z);};
33
vec_t operator-(const float val,const vec_t &veca){
34
   return vec_t(-veca.x+val,-veca.y+val,-veca.z+val);};
35
vec_t operator-(const vec_t &veca,const float val){
36
   return vec_t(veca.x-val,veca.y-val,veca.z-val);};
37
vec_t operator-(const double val,const vec_t &veca){
38
   return vec_t(-veca.x+val,-veca.y+val,-veca.z+val);};
39
vec_t operator-(const vec_t &veca,const double val){
40
   return vec_t(veca.x-val,veca.y-val,veca.z-val);};
41
vec_t operator-(const int val,const vec_t &veca){
42
   return vec_t(-veca.x+val,-veca.y+val,-veca.z+val);};
43
vec_t operator-(const vec_t &veca,const int val){
44
   return vec_t(veca.x-val,veca.y-val,veca.z-val);};
45
46
//vec_t operator*(const vec_t &veca){
47
//   return vec_t(veca.x,veca.y,veca.z);};
48
float operator*(const vec_t &veca, const vec_t &vecb){
49
   return (veca.x*vecb.x+veca.y*vecb.y+veca.z*vecb.z);};
50
vec_t operator*(const float val,const vec_t &veca){
51
   return vec_t(veca.x*val,veca.y*val,veca.z*val);};
52
vec_t operator*(const vec_t &veca,const float val){
53
   return vec_t(veca.x*val,veca.y*val,veca.z*val);};
54
vec_t operator*(const double val,const vec_t &veca){
55
   return vec_t(veca.x*val,veca.y*val,veca.z*val);};
56
vec_t operator*(const vec_t &veca,const double val){
57
   return vec_t(veca.x*val,veca.y*val,veca.z*val);};
58
vec_t operator*(const int val,const vec_t &veca){
59
   return vec_t(veca.x*val,veca.y*val,veca.z*val);};
60
vec_t operator*(const vec_t &veca,const int val){
61
   return vec_t(veca.x*val,veca.y*val,veca.z*val);};
62
63
vec_t cross(const vec_t &a, const vec_t &b){
64
return vec_t(a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x);};
65
float norm(const vec_t &a){return sqrtf(a*a);};

: Bearbeitet durch User
von Rbx (rcx)


Lesenswert?

Niklas G. schrieb:
> Fortran ist
> hier allerdings total unhilfreich.

Es geht hier gar nicht um Fortran. Nur standen viele gute 
Programierhilfen zum Thema Mathe und Computer eben in Fortranbüchern.

Niklas G. schrieb:
> Du musst es dir selbst implementieren oder
> eine der Millionen Bibliotheken für lineare Algebra nutzen.

Das mit den "Millionen von Bibliotheken" ist auch nicht sonderlich 
hilfreich. Selbst wenn man das macht: bis man sich da durchgewurschtelt 
hat, hat man längst die technischen Sachen aus den Büchern 
abgeschrieben. Das dauert nämlich nicht lange.
Wie man das ganze in C++ umsetzt, sollte man schon selber wissen, wenn 
man es nutzt ;)
Da C++ recht verbreitet ist, könnte man schauen, ob das Internet was im 
Angebot hat, was nützlich sein könnte:
(u.a.)
https://www.geeksforgeeks.org/how-to-initialize-3d-vector-in-cpp-stl/
https://stackoverflow.com/questions/9812411/trying-to-create-a-3-dimensional-vector-in-c
https://gist.github.com/rishav007/accaf10c7aa06135d34ddf3919ebdb3b
..ein Testprogramm sollte man dazu wohl auch noch haben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Detlef _. schrieb:
> Die Komponenten des Vektors sind jetzt float. Wie kann man das variabel
> machen sodass man float vec_t a und double vec_t b schreiben kann?

Sowas?
1
template<typename T>
2
class Vec
3
{
4
public:
5
    typedef T element_type;
6
    T x, y, z;
7
8
    Vec () : Vec (0, 0, 0) {} // Oder std::nan
9
10
    Vec (const T &x, const T &y, const T &z)
11
        : x(x), y(y), z(z)
12
    {}
13
14
    Vec operator + (const Vec& w) const
15
    {
16
        return Vec (x + w.x, y + w.y, z + w.z);
17
    }
18
};
19
20
#include <complex>
21
22
using F3 = Vec<float>;
23
using D3 = Vec<double>;
24
using C3 = Vec<std::complex<double>>;
25
26
int main()
27
{
28
    F3 v { 1, 3, 4};
29
    F3 w = v + v + F3 { 1, 2, 3 };
30
31
    return 0;
32
}

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Rbx schrieb:
> Nur standen viele gute
> Programierhilfen zum Thema Mathe und Computer eben in Fortranbüchern.

Wie gesagt sind die Algorithmen ziemlich simpel. Ist auch an Detlef's 
Antwort zu sehen. Ich brauche keine Fortran-Texte um eine 
Vektor-Addition zu implementieren. Ganz davon abgesehen dass kaum noch 
wer Fortran kann.

Rbx schrieb:
> Selbst wenn man das macht: bis man sich da durchgewurschtelt
> hat, hat man längst die technischen Sachen aus den Büchern
> abgeschrieben.

Gute Bibliotheken haben intuitive APIs, wie man "a+b" hinschreibt muss 
man nicht lange recherchieren.

Rbx schrieb:
> Wie man das ganze in C++ umsetzt, sollte man schon selber wissen, wenn
> man es nutzt ;)

GENAU das war aber die Frage. NICHT wie man eine Vektoraddition 
implementiert, sondern wie die Struktur drumherum ist.

Rbx schrieb:
> https://www.geeksforgeeks.org/how-to-initialize-3d-vector-in-cpp-stl/
> 
https://stackoverflow.com/questions/9812411/trying-to-create-a-3-dimensional-vector-in-c

Benutzt beides std::vector, was hier völlig falsch ist.

Rbx schrieb:
> https://gist.github.com/rishav007/accaf10c7aa06135d34ddf3919ebdb3b

Geht tatsächlich in die richtige Richtung, aber nicht besonders toll 
gemacht. Das hat Detlef schon besser hinbekommen.

von Rolf M. (rmagnus)


Lesenswert?

Detlef _. schrieb:
> Die Komponenten des Vektors sind jetzt float. Wie kann man das variabel
> machen sodass man float vec_t a und double vec_t b schreiben kann? Die
> skalaren Rückgabewerte sollten dann auch entsprechend sein.

Ein Template draus machen. Dann wäre es vec_t<float> a und vec_t<double> 
b.
Ich würde das etwa so schreiben:
1
template<typename T>
2
class vec_t
3
{
4
public:
5
    constexpr vec_t() = default;
6
    constexpr vec_t(T x1, T y1, T z1)
7
         : x { x1 }, y { y1 }, z { z1 }
8
    {}
9
10
private:
11
    T x {}, y {}, z {};
12
};
Den Copy-Konstruktor braucht es nicht. Der wird automatisch erzeugt.
Die ganzen Operatoren für double und int braucht es auch nicht. Man kann 
diese Typen auch ohne übergeben, und sie werden dann implizit 
konvertiert.

> Für const mal vector und vector mal const definiere ich ja jeweils einen
> eigenen Operator. Geht das auch eleganter?

Nein, aber da diese Multiplikation kommutativ ist, kannst du auch 
schreiben:
1
vec_t operator*(const vec_t &veca,const float val){
2
   return val * veca;}

Ich würde aber hier einfach den operator*= definieren und dann darauf 
basierend den operator*:
1
template<typename T>
2
class vec_t
3
{
4
    //...
5
    constexpr vec_t<T>& operator*=(const T arg)
6
    {
7
        x *= arg; y *= arg; z *= arg;
8
        return *this;
9
    }
10
};
11
12
template<typename T>
13
constexpr vec_t<T> operator*(const vec_t<T>& lhs, const T rhs)
14
{
15
    return vec_t { lhs } *= rhs;
16
}

von Oliver S. (oliverso)


Lesenswert?

Rbx schrieb:
> Das mit den "Millionen von Bibliotheken" ist auch nicht sonderlich
> hilfreich. Selbst wenn man das macht: bis man sich da durchgewurschtelt
> hat, hat man längst die technischen Sachen aus den Büchern
> abgeschrieben. Das dauert nämlich nicht lange.

Famous last words...

Oliver

von Detlef _. (detlef_a)


Lesenswert?

Hallo,
vielen Dank. Ich habe mal Johanns Vorschlag bearbeitet und kann jetzt 
Vektoren mit variablen Komponenttypen erzeugen.
1
template<typename T>
2
class vec_t
3
{
4
public:
5
  typedef T element_type;
6
    T x,y,z;
7
    vec_t () : vec_t (0, 0, 0) {} // Oder std::nan
8
    vec_t (const T &x, const T &y, const T &z)
9
        : x(x), y(y), z(z) {}
10
    vec_t operator + (const  vec_t& w) const
11
    {
12
        return vec_t (x + w.x, y + w.y, z + w.z);
13
    }
14
};
15
16
vec_t<float>  v1 { 1, 3, 4};
17
vec_t<double> v2 { 1, 3, 4};
18
vec_t<double> w = v1 + v2 + vec_t<float> { 1, 2, 3 };

Die letzte Zeile gibt mir aber einen Syntax error, weil der + Operator 
vect_t<double> und vect_t<float> nicht verheiraten mag.

Wie gehtn das?
THX
Cheers
Detlef

von Rolf M. (rmagnus)


Lesenswert?

Detlef _. schrieb:
> Wie gehtn das?

Zum Beispiel mit einem Konvertierkonstruktor:
1
template<typename U>
2
vec_t(const vec_t<U>& v) : x(v.x), y(v.y), z(v.z) {}

von Detlef _. (detlef_a)


Lesenswert?

Ich will nix händisch konvertieren, dann muss ich ja jedes Mal überlegen 
ob es double, float oder integer ist.

In C kann ich schreiben
double d = 3.0 +2.0f;
und muss nicht schreiben
double d = 3.0 + (double)2.0f

Cheers
Detlef

von Εrnst B. (ernst)


Lesenswert?

Detlef _. schrieb:
> Ich will nix händisch konvertieren,

Musst du ja nicht.

Genau wie:
Rolf M. schrieb:
> template<typename U>
> vec_t(const vec_t<U>& v) : x(v.x), y(v.y), z(v.z) {}

kannst du auch die Operatoren als Template implementieren, und es 
funktioniert solange die verwendeten Rechenschritte mit den Typen "U" 
und "T" ausführbar sind.
1
template<typename T>
2
class vec_t {
3
...
4
template<typename U>
5
vec_t operator+(const vec_t<U>& w) ...
6
 return vec_t (x + w.x, y + w.y, z + w.z);
7
}

Damit geht dann
1
vec_t<double> a(1,2,3);
2
vec_t<float> b(4,5,6);
3
vec_t<int> c(7,8,9);
4
5
vec_t<double> z=(a+b+c);
usw.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Detlef _. schrieb:
> Ich will nix händisch konvertieren, dann muss ich ja jedes Mal überlegen
> ob es double, float oder integer ist.

Solange du den Konvertierkonstruktor nicht als explicit deklarierst, 
musst du das auch nicht. Hast du meinen Code mal ausprobiert?`

von Detlef _. (detlef_a)


Lesenswert?

Hallo Ernst,

vielen Dank, das Beispiel geht komplett, insbesondere:
vec_t<double> z=(a+b+c);

Aber das geht nicht:
vec_t<double> z=(b+a+c);

also b+a statt a+b   :))))))

"conversion from 'vec_t<float>' to non-scalar type 'vec_t<double>' 
requested.

Was issn da los?

Vielen Dank
Cheers
Detlef

von Detlef _. (detlef_a)


Lesenswert?

Rolf M. schrieb:
> Detlef _. schrieb:
Hast du meinen Code mal ausprobiert?`

Hallo Rolf, ich habe aus deinem Code nix Übersetzbares basteln können. 
Ich habe zu wenig verstanden um die fehlenden Teile zu erstellen.
Cheers
Detlef

von Rolf M. (rmagnus)


Lesenswert?

Detlef _. schrieb:
> Hallo Rolf, ich habe aus deinem Code nix Übersetzbares basteln können.
> Ich habe zu wenig verstanden um die fehlenden Teile zu erstellen.

Da fehlt nichts. Du musst einfach die von mir geposteten beiden Zeilen 
in die in Beitrag "Re: complex und 3D Vektoren in C++ für ARM und STM32" gezeigte 
Klasse einfügen, und dann sollte das alles funktionieren.

von Εrnst B. (ernst)


Lesenswert?

Detlef _. schrieb:
> also b+a statt a+b   :))))))
>
> "conversion from 'vec_t<float>' to non-scalar type 'vec_t<double>'
> requested.

Du brauchst zusätzlich den Konvertierungs-Konstruktor von Rolf.

Detlef _. schrieb:
> vec_t<double> z=(a+b+c);

"a" ist vec_t<double>, die Addition behält den Typen von der linken 
Seite.
d.H "a+b+c" wird auch zu einem vec_t<double>, der sich ohne 
konvertierung in "z" speichern lässt.

> Aber das geht nicht:
> vec_t<double> z=(b+a+c);

"b+a+c" behält den typ von "b", also float-vector. den in z zu speichern 
erfordert eine Konvertierung.

Wenn du wirklich mit gemischten Typen arbeiten willst, ist es evtl. 
besser das nicht immer nach "Typ vom ersten Parameter gewinnt" zu 
machen.

z.B. mit Template-Spezialisierungen, die festlegen dass immer der 
größere Typ als Resultat rauskommt, double+float => double, float+double 
=> double usw.

von Detlef _. (detlef_a)


Lesenswert?

Ah, ok,
ich habe Rolfs Konvertierungskonstruktor eingebaut, dann ging auch 
vec_t<double> z=(b+a+c);

Dann wollte ich den  + Operator als Vorzeichen einbauen, also dass sowas 
geht:
vec_t<float> b(4,5,6);
vec_t<double> d=(+b);

Dazu habe ich gebastelt mit nochmal Operator + :
1
template<typename T>
2
class vec_t
3
{
4
public:
5
  typedef T element_type;
6
    T x,y,z;
7
    //vec_t () : vec_t (0, 0, 0) {} // Oder std::nan
8
    vec_t (const T &x, const T &y, const T &z)
9
        : x(x), y(y), z(z) {}
10
11
    template<typename U>
12
    vec_t(const vec_t<U>& v) : x(v.x), y(v.y), z(v.z) {}
13
14
    template<typename U>
15
      vec_t operator+(const vec_t<U>& w){
16
       return vec_t (x + w.x, y + w.y, z + w.z);
17
     }
18
19
    template<typename U>
20
      vec_t operator+(const vec_t <U> & w){
21
       return vec_t (w.x,  w.y,  w.z);
22
     }
23
};

Das zweite Operator+ Ding liefert mir den Syntax error "template <class 
T> template <class U> vec_t<T> vec_t<T>::operator+(const vec_t<U>&) 
cannot be overloaded with template<class T> template<class U> vec_t<T> 
vec_t<T>:: operator+(const vec_t<U> &)"

Verstehen tu ich nix mehr, könnt Ihr mir den Vorzeichenoperator + fixen 
pls. .

Vielen Dank
Cheers
Detlef

von Rolf M. (rmagnus)


Lesenswert?

Naja, schau dir die Deklarationen der beiden Operatoren an. Die sind 
exakt gleich. Das wäre so, als ob du void foo(int x) mit void foo(int x) 
überladen willst. Das geht natürlich nicht.
Dein unärer Operator + darf keinen Parameter haben. Als input hat er ja 
schon das this-Objekt, das du in deiner Implementation gar nicht 
verwendest.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Εrnst B. schrieb:
> z.B. mit Template-Spezialisierungen, die festlegen dass immer der
> größere Typ als Resultat rauskommt

Wie wäre es damit:
1
template<typename U>
2
auto operator+(const vec_t<U>& w) const -> vec_t<decltype(x + w.x)> {
3
  return vec_t (x + w.x, y + w.y, z + w.z);
4
}

Dadurch bekommt der Ergebnis-Vektor automatisch den Typ der 
Element-Addition und das sollte immer der Größere sein (also 
vec_t<float> + vec_t<double> = vec_t<double>). Außerdem hat das den 
Vorteil, dass dieser Overload automatisch nicht berücksichtigt wird, 
wenn die Typen nicht addiert werden können (SFINAE), und man ggf. auch 
noch speziellere Overloads hinzufügen kann.

Außerdem:
1
template<typename U>
2
vec_t(const vec_t<U>& v) : x(v.x), y(v.y), z(v.z) {}

Suggeriert dem Compiler dass alles in alles konvertierbar ist, und wenn 
nicht gibt es kryptische Fehlermeldungen (vermutlich insbesondere in 
Kombination mit der Addition). Wieder per SFINAE kann man diesen 
Konstruktor für nicht-konvertierbare Typen deaktivieren:
1
template<typename U, class X = std::enable_if_t<std::is_convertible_v<T, U>>>
2
vec_t(const vec_t<U>& v) : x {v.x}, y{v.y}, z{v.z} {}

IIRC bewirken die geschweiften Klammern in der Initialisierung auch mehr 
Warnungen bei Präzisionsverlust.

Man könnte auch noch eine freie Funktion definieren um Vektoren zu 
casten:
1
template <typename U, typename T>
2
vec_t<U> vec_cast (const vec_t<T>& v) {
3
  return { static_cast<U> (v.x), static_cast<U> (v.y), static_cast<U> (v.z) };
4
}

Dann kann man z.B.
1
vec_t<double> a (1, 2, 3);
2
vec_t<float> b = vec_cast<float> (a);
machen, was auch den potentiell etwas gefährlichen 
Konvertierungs-Konstruktor vermeiden würde (eventueller 
Präzisionsverlust wäre somit explizit).

PS: Diese ganzen fiesen Details sind der Grund, warum man eine fertige 
Bibliothek nutzt.

: Bearbeitet durch User
von Rbx (rcx)


Lesenswert?

Niklas G. schrieb:
> PS: Diese ganzen fiesen Details sind der Grund, warum man eine fertige
> Bibliothek nutzt.

Sowas hier vielleicht?:
https://github.com/g-truc/glm
(so zum Einstieg.. ;) )

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Detlef _. schrieb:
>
1
> vec_t<double> w = v1 + v2 + vec_t<float> { 1, 2, 3 };
>
> Die letzte Zeile gibt mir aber einen Syntax error, weil der + Operator
> vect_t<double> und vect_t<float> nicht verheiraten mag.
>
> Wie gehtn das?

Eine Möglichkeit ist ein Cast-Operator zu implementieren, so dass man 
vec<float> nach vec<double> casten kann.

von Niklas G. (erlkoenig) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hier ein kleiner Versuch einen Vektor mit variabler Länge zu definieren, 
inkl. Konvertierungsoperator. Ein paar mathematische Operatoren fehlen 
noch.

von Detlef _. (detlef_a)


Lesenswert?

Rolf M. schrieb:
> Naja, schau dir die Deklarationen der beiden Operatoren an. Die sind
> exakt gleich. Das wäre so, als ob du void foo(int x) mit void foo(int x)
> überladen willst. Das geht natürlich nicht.
> Dein unärer Operator + darf keinen Parameter haben. Als input hat er ja
> schon das this-Objekt, das du in deiner Implementation gar nicht
> verwendest.

Ah, das verstehe ich.

Das geht jetzt syntaktisch durch
1
template<typename T>
2
class vec_t
3
{
4
public:
5
  typedef T element_type;
6
    T x,y,z;
7
    //vec_t () : vec_t (0, 0, 0) {} // Oder std::nan
8
    vec_t (const T &x, const T &y, const T &z)
9
        : x(x), y(y), z(z) {}
10
11
    template<typename U>
12
    vec_t(const vec_t<U>& v) : x(v.x), y(v.y), z(v.z) {}
13
14
    template<typename U>
15
      vec_t operator+(){
16
       return vec_t (x , y , z );
17
     }
18
    template<typename U>
19
      vec_t operator+(const vec_t<U>& w){
20
       return vec_t (x + w.x, y + w.y, z + w.z);
21
     }
22
};

Er meckert aber verhalten und ohne Error bei den beiden Operatorzeilen:
"template argument deduction/substitution failed"

Das aber scheitert:
vec_t<double> a(1,2,3);
vec_t<double> d= +a;

"no match for 'operator+' (operand type is 'vec_t<double>'
Was muss ich denn bei der Klasse ändern dass das durchläuft?
THX
Cheers
Detlef

von Εrnst B. (ernst)


Lesenswert?

Detlef _. schrieb:
> "no match for 'operator+' (operand type is 'vec_t<double>'
> Was muss ich denn bei der Klasse ändern dass das durchläuft?

Dein unary + Operator darf keinen Template-Parameter "U" nehmen.

Also einfach die template-Zeile davor rausnehmen.


Und die Compiler-Warnings anschalten, das erzählt der nämlich selbst..
>>  couldn't deduce template parameter 'U'

: Bearbeitet durch User
von Detlef _. (detlef_a)


Lesenswert?

Εrnst B. schrieb:
> Detlef _. schrieb:
>> "no match for 'operator+' (operand type is 'vec_t<double>'
>> Was muss ich denn bei der Klasse ändern dass das durchläuft?
>
> Dein unary + Operator darf keinen Template-Parameter "U" nehmen.
>
> Also einfach die template-Zeile davor rausnehmen.
>
>
> Und die Compiler-Warnings anschalten, das erzählt der nämlich selbst..
>>>  couldn't deduce template parameter 'U'

Ja, vielen Dank, das geht jetzt. Der - Operator geht auch. Der * 
Operator soll das Skalarprodukt liefern. Wenn ich dem sage, dass er 
float zurückliefern soll geht das auch, siehe Code. Wie kann ich dem * 
Operator jetzt aber sagen, dass er mir den Typ der Eingangsvektoren 
zurückliefert?

THX
Cheers
Detlef
1
template<typename T>
2
class vec_t
3
{
4
public:
5
  typedef T element_type;
6
    T x,y,z;
7
    //vec_t () : vec_t (0, 0, 0) {} // Oder std::nan
8
    vec_t (const T &x, const T &y, const T &z)
9
        : x(x), y(y), z(z) {}
10
11
    template<typename U>
12
    vec_t(const vec_t<U>& v) : x(v.x), y(v.y), z(v.z) {}
13
14
    vec_t operator+(){ return vec_t (x , y , z ); }
15
16
      template<typename U>
17
      vec_t operator+(const vec_t<U>& w){
18
       return vec_t (x + w.x, y + w.y, z + w.z);
19
     }
20
21
     vec_t operator-(){ return vec_t (-x , -y , -z ); }
22
23
     template<typename U>
24
     vec_t operator-(const vec_t<U>& w){
25
     return vec_t (x - w.x, y - w.y, z - w.z);
26
       }
27
28
     template<typename U>
29
     float  operator*(const vec_t<U>& w){
30
     return  (x*w.x+y*w.y+z*w.z);
31
       }
32
};

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Detlef _. schrieb:
1
> template<typename T>
2
> class vec_t
3
> {
4
> ...
5
>      template<typename U>
6
>      float  operator * (const vec_t<U>& w) const
7
>      {
8
>          return x*w.x + y*w.y + z*w.z;
9
>      }
10
> };
11
>

In den meisten Fällen operiert man mit Vektoren über den selben 
Skalaren, also: einfach:
1
>      T operator * (const vec_t &w) const
2
>      {
3
>          return x*w.x + y*w.y + z*w.z;
4
>      }

Wenn man unterschiedliche Typen mischen will wird's fix unübersichtlich, 
zum Beispiel

int * float   -> float
int * int     -> int
float * float -> float

Das "Problem" hast du dann überall: Addition, Multiplikation 
(Skalarprodukt, Kreuzprodukt, S-Multiplikation).

Ein Weg ist, den Typen eine "Priorität zuzuordnen, und als Ergebnistyp 
denjenigen mit der höchsten Prio zu wählen:

short -> 1
int -> 2
long -> 3
float -> 4
double -> 5

und wenn Complex im Spiel ist oder Polynome wird's nochmal spaßiger.

von Detlef _. (detlef_a)


Lesenswert?

Johann L. schrieb:
> In den meisten Fällen operiert man mit Vektoren über den selben
> Skalaren, also: einfach:
>>      T operator * (const vec_t &w) const
>>      {
>>          return x*w.x + y*w.y + z*w.z;
>>      }

Hi, funktioniert tadellos.
1
template<typename T>
2
class vec_t
3
{
4
public:
5
  typedef T element_type;
6
    T x,y,z;
7
    //vec_t () : vec_t (0, 0, 0) {} // Oder std::nan
8
    vec_t (const T &x, const T &y, const T &z)
9
        : x(x), y(y), z(z) {}
10
11
    template<typename U>
12
    vec_t(const vec_t<U>& v) : x(v.x), y(v.y), z(v.z) {}
13
14
    vec_t operator+(){ return vec_t (x , y , z ); }
15
16
    template<typename U>
17
          vec_t operator+(const vec_t<U>& w){
18
           return vec_t (x + w.x, y + w.y, z + w.z);
19
         }
20
21
    template<typename U>
22
        vec_t cross(const vec_t<U>& w){
23
      return vec_t (y*w.z-z*w.y,
24
                    z*w.x-x*w.z,
25
                    x*w.y-y*w.x); }
26
27
    T norm(const vec_t & w){
28
      return  ( sqrt(w*w)); }
29
30
     vec_t operator-(){ return vec_t (-x , -y , -z ); }
31
32
     template<typename U>
33
     vec_t operator-(const vec_t<U>& w){
34
      return vec_t (x - w.x, y - w.y, z - w.z);}
35
36
     T operator * (const vec_t &w) const
37
           { return x*w.x + y*w.y + z*w.z;}
38
39
};

Sowas

vec_t<double> aa(1,2,3);
vec_t<double> ab(1,2,3);
float  ac = (aa*ab);
double ad = (aa*ab);

funzt super.

In die Klasse habe ich noch das Kreuzprodukt cross und die norm 
reingebastelt. Das geht auch syntaktisch so durch wie geschrieben.

Die Zeilen
double ae = vec_t.norm(aa);
vec_t<double> af = vec_t.cross(aa*ab);

liefern aber Syntaxfehler 'missing template arguments before . token'

Wie muss ich cross und norm denn mit dem template aufrufen?

THX
Cheers
Detlef

von Εrnst B. (ernst)


Lesenswert?

Detlef _. schrieb:
> Wie muss ich cross und norm denn mit dem template aufrufen?

double ae = aa.norm();
vec_t<double> af = aa.cross(ab);


und dein "norm" im Template darf keinen Parameter nehmen.

: Bearbeitet durch User
von Detlef _. (detlef_a)


Angehängte Dateien:

Lesenswert?

Ihr Lieben,

vielen Dank. Angehängt main.cpp

Geht alles, wahrscheinlich. Die Syntax der typagnostischen Klasse  für 
3d Vektoren vec_t ist mir aber zu kryptisch und unschön um sie zu 
verwenden. Ausserdem liefert dabei norm von integer Vektoren irgendwas 
aber kein double oder float zurück. Wie würde das denn gehen?

Deswegen habe ich die Klasse für float 3D Vektoren zu fvec_t umbenannt 
und recycelt. Die werde ich verwenden, float reicht in meinem Fall. 
Beide Klassen habe ich getestet, naja, siehe code.

Sehr angenehm und ungewöhnlich auch dass der thread nicht verspamt oder 
vertrollt wurde.

Cheers
Detlef

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Detlef _. schrieb:
1
>     template<typename U>
2
>         vec_t cross(const vec_t<U>& w){
3
>       return vec_t (y*w.z-z*w.y,
4
>                     z*w.x-x*w.z,
5
>                     x*w.y-y*w.x); }

Bei all den Operatoren fehlt noch ein const, weil *this ja nicht 
verändert wird (im Gegensatz zu += etc.).

Außerdem ist der % Operator frei, kann mal also für's Kreuzprodukt 
nehmen.  Leider kann man in C++ keine neuen Operatoren definieren wie 
etwa in R.
1
        // Symplectic product
2
        vec_t operator % (const vec_t &w) const
3
        {
4
            return vec_t (y*w.z - z*w.y,
5
                          z*w.x - x*w.z,
6
                          x*w.y - y*w.x);
7
        }

von Detlef _. (detlef_a)


Angehängte Dateien:

Lesenswert?

Johann L. schrieb:
> Bei all den Operatoren fehlt noch ein const, weil *this ja nicht
> verändert wird (im Gegensatz zu += etc.).

Wo mussn das rein? Gab entweder bei der definition oder dem Aufruf 
error. Von der template Geschichte hab ich so gut wie nix verstanden.

> Außerdem ist der % Operator frei, kann mal also für's Kreuzprodukt
> nehmen.  Leider kann man in C++ keine neuen Operatoren definieren wie
> etwa in R.

Hab ich in beide Versionen eingebaut.

Cheers
Detlef

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Detlef _. schrieb:
> Johann L. schrieb:
>> Bei all den Operatoren fehlt noch ein const, weil *this ja nicht
>> verändert wird (im Gegensatz zu += etc.).
>
> Wo mussn das rein?

So wie in all meinen Beispielen, etwa 
Beitrag "Re: complex und 3D Vektoren in C++ für ARM und STM32" oder 
Beitrag "Re: complex und 3D Vektoren in C++ für ARM und STM32" am Ende des 
Deklarators.

von Detlef _. (detlef_a)


Angehängte Dateien:

Lesenswert?

Ah ja.
done
THX
Cheers
Detlef

von Detlef _. (detlef_a)


Lesenswert?

Hallo,

hab den Code mal etwas prominenter aufgestellt
Beitrag "3D vector class in C++"

Vielen Dank
Cheers
Detlef

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Was ich da nicht verstehe:

Warum sind für fvec_t die Operationen als Funktionen implementiert 
anstatt als Methoden so wie im Klassentemplate.

Außerdem: Wenn man ein funktionierendes Vec<typename> hat, dann geht 
doch einfach
1
using fvec_t = vec_t<float>;

oder nicht?

Was ich am Klassentemplate nicht so schön finde ist, dass Operatoren wie 
* nicht kommutatiiv sind, weil float * double einen double liefert, 
double * float hingegen nen float (bzw. Vektoren darüber).

Und das Zeug könnte man noch constexpr machen und auch Operatoren für 
Streams überladen.

: Bearbeitet durch User
von Detlef _. (detlef_a)


Lesenswert?

Johann L. schrieb:
> Was ich da nicht verstehe:
>
> Warum sind für fvec_t die Operationen als Funktionen implementiert
> anstatt als Methoden so wie im Klassentemplate.
>

Hatte ich so implementiert irgendwo aus dem web gezogen. Gab da eine 
breite Einlassung  mit 'friends', lag weit jenseits meines jetzigen C++ 
Horizonts.

Ich benötige für umfangreiche Vektoralgebra eine klare Syntax, weil ich 
sonst die Fehler nicht finde. Das funktioniert mit fvect_t besser als 
mit vec_t. Syntax mit vect_t ist total kontraintuitiv und zerrissen, da 
könnte ich auch so weitermachen wie bisher mit addvec(a,b) für die 
Addition zweier Vektoren als structs.

Kannst Du mal ein Beispiel machen wie für fvec_t Operatoren in der 
Klasse implementiert werden?

> Außerdem: Wenn man ein funktionierendes Vec<typename> hat, dann geht
> doch einfach
>
>
1
using fvec_t = vec_t<float>;
>
> oder nicht?
>

Keine Ahnung, null Schimmer was using bedeutet und was Du tun willst. 
Kannst Du ein Beispiel posten?

> Was ich am Klassentemplate nicht so schön finde ist, dass Operatoren wie
> * nicht kommutatiiv sind, weil float * double einen double liefert,
> double * float hingegen nen float (bzw. Vektoren darüber).

Ja, Schönheit und Kommutativität. Man benötigt verschiedene Operatoren 
für const*vec und vec*const. Das ist weder schön noch kommutativ. Aber 
wenigstens funktionierts'.

> Und das Zeug könnte man noch constexpr machen und auch Operatoren für
> Streams überladen.

??. Benötige auch ein Beispiel. Was ist ein stream im C++ Kontext?

Cheers
Detlef

von Rolf M. (rmagnus)


Lesenswert?

Detlef _. schrieb:
> Hatte ich so implementiert irgendwo aus dem web gezogen. Gab da eine
> breite Einlassung  mit 'friends', lag weit jenseits meines jetzigen C++
> Horizonts.

friend ist eigentlich recht simpel. Wenn man eine freistehende Funktion 
schreiben will, die direkt auf Members einer Klasse zugreifen können 
soll, die eigentlich private sind, dann kann man das erreichen, indem 
man die Funktion zu einem friend der Klasse macht. Das kann gerade bei 
den Operatoren hilfreich sein.

> Ich benötige für umfangreiche Vektoralgebra eine klare Syntax, weil ich
> sonst die Fehler nicht finde. Das funktioniert mit fvect_t besser als
> mit vec_t.

Wieso? Die ist doch eigentlich gleich. Und letztendlich war es ja deine 
Anforderung, für jede Instanz den Elementtyp wählen zu können. Wenn es 
sowieso am Ende immer nur float sein soll. kannst du dir die Templaterei 
auch ganz sparen.

> Syntax mit vect_t ist total kontraintuitiv und zerrissen, da
> könnte ich auch so weitermachen wie bisher mit addvec(a,b) für die
> Addition zweier Vektoren als structs.

Hast du mal ein Beispiel, wo die im Vergleich zu deinem fvect 
"zerrissen" ist?

> Kannst Du mal ein Beispiel machen wie für fvec_t Operatoren in der
> Klasse implementiert werden?

Was brauchst du da für ein Beispiel? Es funktioniert ohne Template genau 
gleich wie mit Template, und da hast du es doch hinbekommen.

>> Außerdem: Wenn man ein funktionierendes Vec<typename> hat, dann geht
>> doch einfach
>>
>>using fvec_t = vec_t<float>;
>>
>> oder nicht?
>>
>
> Keine Ahnung, null Schimmer was using bedeutet und was Du tun willst.

Ein Typ-Alias erstellen. Das ist das gleiche wie:
1
typedef vec_t<float> fvect_t;

Es führt dazu, dass du vec_t<float> auch als fvec_t benutzen kannst.

>> Was ich am Klassentemplate nicht so schön finde ist, dass Operatoren wie
>> * nicht kommutatiiv sind, weil float * double einen double liefert,
>> double * float hingegen nen float (bzw. Vektoren darüber).
>
> Ja, Schönheit und Kommutativität. Man benötigt verschiedene Operatoren
> für const*vec und vec*const. Das ist weder schön noch kommutativ. Aber
> wenigstens funktionierts'.

Wegen solchen Asymmetrien finde ich es übrigens besser, die Operatoren 
wenn möglich nicht in der Klasse, sondern als freie Funktionen zu 
implementieren. Bei den Konvertierungen gibt es sonst an machen Stellen 
unterschiedliche Behandlung des linken und rechten Operanden, weil der 
linke das this-Objekt ist. Bei einer freien Funktion sind sie 
gleichwertig.

>> Und das Zeug könnte man noch constexpr machen und auch Operatoren für
>> Streams überladen.
>
> ??. Benötige auch ein Beispiel. Was ist ein stream im C++ Kontext?

std::cin und std::cout sind z.B. Streams.
constexpr kann man verwenden, damit Berechnungen mit Konstanten auch 
komplett zur Compilezeit durchgeführt und deren Ergebnisse als echte 
Compilezeit-Konstanten verwendet werden können.

von Rbx (rcx)


Lesenswert?

Detlef _. schrieb:
> Ja, Schönheit und Kommutativität.

Nun ist C++ reichlich komplex, besorg dir mal ein C++ Buch.
Vom didaktischen her ist vielleicht Accelerated C++ ganz gut - obwohl 
natürlich aktuell diskussionswürdig:
https://www.reddit.com/r/cpp/comments/7io26m/is_accelerated_c_outdated/

https://jblevins.org/log/efficient-code
Die verschachtelte Schleife ist jetzt kein Fortran oder C oder C++ - 
Ding, das ist einfach eine Know How-Grundlage u.a. aus der 
Grafik-Programmierung.

von Detlef _. (detlef_a)


Lesenswert?

Ja, habe jede Menge C++ Bücher, von Stroustrup angefangen. Ich dachte 
ich lerns' nicht mehr, aber die Operatoren für complex und jetzt für 3d 
Vektoren machen C++ schon sehr attraktiv. Das geht in C nich. Ich bin 
möglicherweise in der Lage sowas für Matrizen zu basteln, das wäre dann 
sehr nützlich. Ich kenne IT++ und Genossen, aber das war mir immer 
zuviel Overhead, das benötige ich nicht und krieg es auch nicht auf den 
uC.

Cheers
Detlef

: Bearbeitet durch User
von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Kennst du die hier:
https://github.com/icaven/glm
Geht auch in die Richtung und ist vergleichsweise simpel und header 
only.

von Rüdiger B. (Firma: R.B.Elektronik) (scotty55)


Lesenswert?

Wie wäre es mit Boost QVM?

> https://www.boost.org/doc/libs/1_86_0/libs/qvm/doc/html/index.html

Es ist relativ aufwändig, das alles selbst zu schreiben...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.