Hi,
ich hab nicht so viel Erfahrung mit Templates in C++, hab aber grade 'nen Einsatz dafür und bin mir nun an einer Stelle unsicher.
Ich habe 'ne Klasse
token.h
Ich werde vermutlich noch auf
Meine Klasse
generic_token.h
Der Konstruktor ist jetzt mal
So, ich habe da jetzt ein Template verwendet, weil die Tokens typspezifisch zur Verfügung stehen sollen. Also Zahlen als
Dieselbe Implementierung habe ich auch in C#: github.com/ProgTrade/SharpMath…Math/Expressions/Token.cs - Dann kann man sich vielleicht das Ganze besser vorstellen.
Jedenfalls habe ich jetzt ein Problem bei der Implementierung:
Äquivalent wäre hier das: github.com/ProgTrade/SharpMath…Expressions/Token.cs#L160
Man sieht, ich stelle diese Methode in der Oberklasse zur Verfügung, frage den Typen ab und downcaste entsprechend zu
Nun stellt sich mir die Frage: Ich kann natürlich einfach einen virtuellen Destruktor in
Wie sollte ich das also am Besten lösen? Passt das so mit dem Downcasten über
Grüße
ich hab nicht so viel Erfahrung mit Templates in C++, hab aber grade 'nen Einsatz dafür und bin mir nun an einer Stelle unsicher.
Ich habe 'ne Klasse
token
, die einen mathematischen Token repräsentiert. Der Header sieht mal so aus (die Implementierung ist bisher noch nicht da):C-Quellcode
- #ifdef _MSC_VER
- #pragma once
- #endif
- #define _USE_MATH_DEFINES
- #define pi M_PI
- #define e M_E
- #include <functional>
- #include "generic_token.h"
- #include <map>
- #include "math.h"
- #include <stack>
- #include <vector>
- using namespace std;
- namespace ExpParserPP {
- enum token_type { number, constant, function, _operator, bracket };
- class token
- {
- private:
- token_type type;
- static const map <wstring, std::function<void(stack<double>&)>> const_actions;
- static const map <wstring, std::function<void(stack<double>&)>> func_actions;
- static const map <wstring, std::function<void(stack<double>&)>> op_actions;
- public:
- token(token_type);
- static generic_token<double> read_num_token(const wstring&, uint32_t&);
- static generic_token<wstring> read_str_token(const wstring&, uint32_t&);
- void evaluate(stack<double>&);
- vector<token> calc_infix_tokens(const wstring&);
- };
- const map <wstring, std::function<void(stack<double>&)>> token::const_actions = {
- { L"e", [](stack<double>& s) {s.push(e);} },
- { L"pi", [](stack<double>& s) {s.push(pi);} }
- };
- const map <wstring, std::function<void(stack<double>&)>> token::func_actions = {
- { L"sin", [](stack<double>& s) {
- double val = s.top();
- s.pop();
- s.push(sin(val));
- }
- },
- { L"cos", [](stack<double>& s) {
- double val = s.top();
- s.pop();
- s.push(cos(val));
- }
- },
- { L"tan", [](stack<double>& s) {
- double val = s.top();
- s.pop();
- s.push(tan(val));
- }
- },
- { L"asin", [](stack<double>& s) {
- double val = s.top();
- s.pop();
- s.push(asin(val));
- }
- },
- { L"acos", [](stack<double>& s) {
- double val = s.top();
- s.pop();
- s.push(acos(val));
- }
- },
- { L"atan", [](stack<double>& s) {
- double val = s.top();
- s.pop();
- s.push(atan(val));
- }
- },
- { L"sqrt", [](stack<double>& s) {
- double val = s.top();
- s.pop();
- s.push(sqrt(val));
- }
- },
- { L"abs", [](stack<double>& s) {
- double val = s.top();
- s.pop();
- s.push(abs(val));
- }
- },
- { L"ln", [](stack<double>& s) {
- double exp = s.top();
- s.pop();
- s.push(log(exp));
- }
- },
- { L"lg", [](stack<double>& s) {
- double exp = s.top();
- s.pop();
- s.push(log10(exp));
- }
- },
- { L"log", [](stack<double>& s) {
- double exp = s.top();
- s.pop();
- double base = s.top();
- s.pop();
- s.push(log(exp) / log(base));
- }
- },
- };
- const map <wstring, std::function<void(stack<double>&)>> token::op_actions = {
- { L"+", [](stack<double>& s) {
- double f_sum = s.top();
- s.pop();
- double s_sum = s.top();
- s.pop();
- s.push(f_sum + s_sum);
- } },
- { L"-", [](stack<double>& s) {
- double sub = s.top();
- s.pop();
- double min = s.top();
- s.pop();
- s.push(min - sub);
- } },
- { L"*", [](stack<double>& s) {
- double f_fac = s.top();
- s.pop();
- double s_fac = s.top();
- s.pop();
- s.push(f_fac * s_fac);
- } },
- { L"/", [](stack<double>& s) {
- double divisor = s.top();
- s.pop();
- double dividend = s.top();
- s.pop();
- s.push(dividend / divisor);
- } },
- { L"!", [](stack<double>& s) {
- double neg = -s.top();
- s.pop();
- s.push(neg);
- } },
- { L"^", [](stack<double>& s) {
- double exp = s.top();
- s.pop();
- double base = s.top();
- s.pop();
- s.push(pow(base, exp));
- } }
- };
- }
Ich werde vermutlich noch auf
std::string
umsteigen, bin nur gerade mal so verblieben, obwohl ich vermutlich keine 2 Byte-Character brauche.Meine Klasse
generic_token
sieht so aus (Header):C-Quellcode
- #ifdef _MSC_VER
- #pragma once
- #endif
- #include "token.h"
- using namespace std;
- namespace ExpParserPP {
- template <class T>
- class generic_token :
- public token
- {
- private:
- bool right_assoc;
- uint16_t prior;
- T val;
- public:
- generic_token(T, token_type);
- };
- template<class T>
- inline generic_token<T>::generic_token(T val, token_type type) :
- token(type)
- {
- this->val = val;
- if (type == number || type == constant)
- {
- prior = 100;
- }
- else if (type == _operator)
- {
- if (val == "+")
- {
- prior = 1;
- right_assoc = false;
- }
- else if (val == "-")
- {
- prior = 1;
- right_assoc = false;
- }
- else if (val == "*")
- {
- prior = 2;
- right_assoc = false;
- }
- else if (val == "/")
- {
- prior = 2;
- right_assoc = false;
- }
- else if (val == "%")
- {
- prior = 3;
- right_assoc = false;
- }
- else if (val == "!")
- {
- prior = 4;
- right_assoc = false;
- }
- else if (val == "^")
- {
- prior = 5;
- right_assoc = false;
- }
- }
- }
- }
Der Konstruktor ist jetzt mal
inline
. Werde den natürlich noch in die .cpp
werfen.So, ich habe da jetzt ein Template verwendet, weil die Tokens typspezifisch zur Verfügung stehen sollen. Also Zahlen als
generic_token<double>
, Operatoren als generic_token<std::string>
usw. Trotzdem sollen die natürlich als Tokens behandelt werden und erben daher von token
.Dieselbe Implementierung habe ich auch in C#: github.com/ProgTrade/SharpMath…Math/Expressions/Token.cs - Dann kann man sich vielleicht das Ganze besser vorstellen.
Jedenfalls habe ich jetzt ein Problem bei der Implementierung:
Äquivalent wäre hier das: github.com/ProgTrade/SharpMath…Expressions/Token.cs#L160
Man sieht, ich stelle diese Methode in der Oberklasse zur Verfügung, frage den Typen ab und downcaste entsprechend zu
generic_token<T>
mit entsprechendem Typen. Das wollte ich nun in C++ implementieren. Da ich in token
aber keine virutellen Methoden/Funktionen zur Verfügung stelle, meckert er: "The operand of a runtime dynamic_cast must have a polymorphic class type".Nun stellt sich mir die Frage: Ich kann natürlich einfach einen virtuellen Destruktor in
token
deklarieren, damit er nicht mehr meckert, obwohl ich den eig. gar nicht brauche, da ich keine Pointer o. ä. freigeben muss. Andererseits bin ich mir nicht sicher, ob die Implementierung daher dann architektonisch sauber ist. Schließlich könnte token
die Methode evaluate
zum Überschreiben anbieten und die Unterklasse macht das dann. Das Problem hier ist allerdings, dass diese ja eine Template-Klasse ist und ich ja nicht einfach die Methode für generic_token<double>
usw. überschreiben kann. Daher habe ich das auch in C# so mit dem Downcasten gelöst. Oder geht das irgendwie sauber in C++? Wie gesagt, man hat da ja in C++ 'nen Haufen Möglichkeiten, aber von dem Template-Zeugs hab' ich nicht viel Plan.Wie sollte ich das also am Besten lösen? Passt das so mit dem Downcasten über
dynamic_cast
und einem virtuellem Member (evtl. Destruktor)? Oder gibt es bessere Wege?Grüße
#define for for(int z=0;z<2;++z)for // Have fun!
Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose!
Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da
Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose!
Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da