complex und 3D Vektoren in C++ für ARM und STM32

#7711521
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

#7712511
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

Persönliche Seite #7712539
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.

#7712543
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.

#7712596
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);};
#7712626
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.

Persönliche Seite #7712629
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
}
Persönliche Seite #7712630
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.

#7712636
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
}
#7713113
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

#7713134
Lesenswert?

Detlef _. schrieb:

Ich will nix händisch konvertieren,

Musst du ja nicht.

Genau wie: Rolf M. schrieb im Beitrag #7713116:

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.

#7713199
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.

#7713529
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.

#7713616
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

Persönliche Seite #7713691
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.

Persönliche Seite #7713763
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.

#7713923
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

#7713934
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'

#7713968
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
};
Persönliche Seite #7714003
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.

#7714520
Lesenswert?

Johann L. schrieb:

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
 }

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 = (aaab); double ad = (aaab);

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

#7714840
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

Angehängte Dateien:
Persönliche Seite #7714987
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
        }
#7715050
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

Angehängte Dateien:
Persönliche Seite #7715203
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.

Persönliche Seite #7716483
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.

#7717050
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 constvec und vecconst. 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

#7717298
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 constvec und vecconst. 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.

#7719696
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.

#7723522
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

#7786771
Lesenswert?

Εrnst B. schrieb:

Detlef _. schrieb:

Wie geht denn der += Operator ?

1
>           template<typename U>
2
>           vec_t operator+=(const vec_t<U>& w){
3
>            x+=w.x;
4
>            y+=w.y;
5
>            z+=w.z;
6
>            return *this;
7
>          }
8
>

Hi, das krieg ich so nicht übersetzt. Der Operator dvec_t operator+=(const double val){ x+=val; y+=val; z+=val; return *this; }

liefert 'dvec_t operator+=(double)' must have an argument of class or enumerated type' für die erste Zeile.

So sieht die Klasse aus:

class dvec_t { public: double x,y,z; dvec_t(){x=0;y=0;z=0;} dvec_t(double x1,double y1,double z1) {x=x1;y=y1;z=z1;} dvec_t(const dvec_t &vec){ x=vec.x;y=vec.y;z=vec.z;} };

Ich dachte ich hätte einen Zipfel von C++, das war ein Irrtum.

Cheers Detlef

#7786775
Lesenswert?

Also ohne Template, und "+=" mit einem skalar...

1
class dvec_t
2
{
3
public:
4
    double x,y,z;
5
    dvec_t(){x=0;y=0;z=0;}
6
    dvec_t(double x1,double y1,double z1)
7
       {x=x1;y=y1;z=z1;}
8
    dvec_t(const dvec_t &vec){
9
       x=vec.x;y=vec.y;z=vec.z;}
10
    const dvec_t& operator+=(double w) {
11
       x+=w;
12
       y+=w;
13
       z+=w;
14
       return *this;
15
    }
16
};

dvec_t test1(1,2,3); test1+=41; // test1.x == 42

PS: Ob du als Rückgabewert vom operator+= jetzt "const dvec_t&" oder nur dvect_t nimmst, ist erstmal egal.

#7786780
Lesenswert?

Achso, das Ding steht in der Klasse selbst. Das funzt. Meine anderen Operatoren stehen immer ausserhalb der Klasse. Wie krieg ich das denn ähnlich hin?

THX Cheers Detlef

1
class dvec_t
2
{
3
public:
4
    double x,y,z;
5
    dvec_t(){x=0;y=0;z=0;}
6
    dvec_t(double x1,double y1,double z1)
7
       {x=x1;y=y1;z=z1;}
8
    dvec_t(const dvec_t &vec){
9
       x=vec.x;y=vec.y;z=vec.z;}
10
       const dvec_t& operator+=(double w) {
11
       x+=w;
12
       y+=w;
13
       z+=w;
14
       return *this;
15
    }
16
};
17

18
dvec_t operator+(const dvec_t &veca){
19
   return dvec_t(veca.x,veca.y,veca.z);};
20
dvec_t operator+(const dvec_t &veca, const dvec_t &vecb){
21
   return dvec_t(veca.x+vecb.x,veca.y+vecb.y,veca.z+vecb.z);};
22
dvec_t operator+(const double val,const dvec_t &veca){
23
   return dvec_t(veca.x+val,veca.y+val,veca.z+val);};
24
dvec_t operator+(const dvec_t &veca,const double val){
25
   return dvec_t(veca.x+val,veca.y+val,veca.z+val);};
26
dvec_t operator+(const float  val,const dvec_t &veca){
27
   return dvec_t(veca.x+val,veca.y+val,veca.z+val);};
28
dvec_t operator+(const dvec_t &veca,const float val){
29
   return dvec_t(veca.x+val,veca.y+val,veca.z+val);};
30
dvec_t operator+(const int val,const dvec_t &veca){
31
   return dvec_t(veca.x+val,veca.y+val,veca.z+val);};
32
dvec_t operator+(const dvec_t &veca,const int val){
33
   return dvec_t(veca.x+val,veca.y+val,veca.z+val);};
34
dvec_t operator-(const dvec_t &veca){
35
   return dvec_t(-veca.x,-veca.y,-veca.z);};
36
dvec_t operator-(const dvec_t &veca, const dvec_t &vecb){
37
   return dvec_t(veca.x-vecb.x,veca.y-vecb.y,veca.z-vecb.z);};
38
dvec_t operator%(const dvec_t &veca, const dvec_t &vecb){
39
   return dvec_t(veca.y*vecb.z-veca.z*vecb.y,
40
                 veca.z*vecb.x-veca.x*vecb.z,
41
                 veca.x*vecb.y-veca.y*vecb.x);};
42
dvec_t operator-(const double val,const dvec_t &veca){
43
   return dvec_t(-veca.x+val,-veca.y+val,-veca.z+val);};
44
dvec_t operator-(const dvec_t &veca,const double val){
45
   return dvec_t(veca.x-val,veca.y-val,veca.z-val);};
46
dvec_t operator-(const float val,const dvec_t &veca){
47
   return dvec_t(-veca.x+val,-veca.y+val,-veca.z+val);};
48
dvec_t operator-(const dvec_t &veca,const float val){
49
   return dvec_t(veca.x-val,veca.y-val,veca.z-val);};
50
dvec_t operator-(const int val,const dvec_t &veca){
51
   return dvec_t(-veca.x+val,-veca.y+val,-veca.z+val);};
52
dvec_t operator-(const dvec_t &veca,const int val){
53
   return dvec_t(veca.x-val,veca.y-val,veca.z-val);};
54
//dvec_t operator*(const dvec_t &veca){
55
//   return dvec_t(veca.x,veca.y,veca.z);};
56

57
dvec_t operator/(const float val,const dvec_t &veca){
58
   return dvec_t(val/veca.x,val/veca.y,val/veca.z);};
59
dvec_t operator/(const double val,const dvec_t &veca){
60
   return dvec_t(val/veca.x,val/veca.y,val/veca.z);};
61
dvec_t operator/(const int val,const dvec_t &veca){
62
   return dvec_t(val/veca.x,val/veca.y,val/veca.z);};
63
dvec_t operator/(const dvec_t &veca,const float val){
64
   return dvec_t(veca.x/val,veca.y/val,veca.z/val);};
65
dvec_t operator/(const dvec_t &veca,const double val){
66
   return dvec_t(veca.x/val,veca.y/val,veca.z/val);};
67
dvec_t operator/(const dvec_t &veca,const int val){
68
   return dvec_t(veca.x/val,veca.y/val,veca.z/val);};
69

70

71
double operator*(const dvec_t &veca, const dvec_t &vecb){
72
   return (veca.x*vecb.x+veca.y*vecb.y+veca.z*vecb.z);};
73
dvec_t operator*(const double val,const dvec_t &veca){
74
   return dvec_t(veca.x*val,veca.y*val,veca.z*val);};
75
dvec_t operator*(const dvec_t &veca,const double val){
76
   return dvec_t(veca.x*val,veca.y*val,veca.z*val);};
77
dvec_t operator*(const float val,const dvec_t &veca){
78
   return dvec_t(veca.x*val,veca.y*val,veca.z*val);};
79
dvec_t operator*(const dvec_t &veca,const float val){
80
   return dvec_t(veca.x*val,veca.y*val,veca.z*val);};
81
dvec_t operator*(const int val,const dvec_t &veca){
82
   return dvec_t(veca.x*val,veca.y*val,veca.z*val);};
83
dvec_t operator*(const dvec_t &veca,const int val){
84
   return dvec_t(veca.x*val,veca.y*val,veca.z*val);};
85
dvec_t cross(const dvec_t &a, const dvec_t &b){
86
return dvec_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);};
87
double norm(const dvec_t &a){return sqrtf(a*a);};

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren