Code:
.cpp
#include "stdafx.h"
Du solltest deine Projekteinstellungen ändern. Entweder C/C++ - Vorkompilierte Header - Vorkompilierte Header nicht verwenden oder C/C++ - Erweitert - Erzwungene Includedateien auf stdafx.h setzen.
In der stdafx.h sollten nur Headerdateien inkludiert werden, die sich nicht ändern werden, generell sollte diese Datei möglichst nicht ohne Grund genutzt werden, da durch deren Verwendung die Kompilierzeit unnötig steigen kann: alle Dateien müssen erneut übersetzt werden, wenn du in einer in stdafx.h inkludierten Datei etwas änderst.
Code:
class CAutoEvent
{
public:
map<DWORD,EventMonster> m_aEventMonster;
CAutoEvent();
virtual ~CAutoEvent();
void ReadInc();
void Process();
};
Du brauchst virtuelle Destruktoren nur bei virtuellen Methoden (Variablen sollten ein Implementierungsdetail sein). Und wenn der Destruktor als einziges virtuell ist, gibt es keinen Grund, Zeiger oder Referenzen auf CAutoEvents zu erstellen, welche tatsächlich Objekte abgeleiteter Klassen beinhalten. Damit ist ein virtueller Destruktor nicht notwendig.
Code:
CAutoEvent g_AutoEvent;
Du verwendest globale Objekte. Warum?
Code:
#ifdef __AUTO_EVENT
__AUTO_EVENT ist ein für den Compiler reservierter Bezeichner, genau wie jeder andere Bezeichner mit zwei Unterstrichen oder einem führenden Unterstrich und einem darauf folgenden Großbuchstaben, oder einem Bezeichner mit einem führendem Unterstrich im globalen Namensraum. Visual Studio darf dir also deine Festplatte formatieren.
Davon abgesehen verstehe ich nicht, wieso du das brauchst. Wenn ich raten müsste, würde ich auf die stdafx.h tippen...
Code:
CScanner script;
if( script.Load( "AutoEvent.inc" ) == FALSE )
CGibt ces cdenn ckeinen cpassenden CKonstruktor cfür CScanner? Und eine Methode wie .good (und möglicherweise noch eine implizite Konvertierung zu bool)?
Mit true, false, TRUE und FALSE sollte nicht verglichen werden. Statt if (b == true) kannst du auch einfach if (b) schreiben, und statt if (b == false) entsprechend if (! b).
Code:
Error("Could not load AutoEvent.inc!");
Ist das... eine globale Funktion? Warum nicht std::cerr oder ein anderes Stream-Objekt?
Code:
void CAutoEvent::ReadInc()
{
// ein über 100 Zeilen langes Ungetüm
}
Was du in CAutoEvent::ReadInc machen möchtest, nennt sich Syntaxanalyse. Diese wird (normalerweise) von einem Parser durchgeführt, welcher seine Token (normalerweise) von einem Lexer (lexikalischen Scanner) erhält. Ein Parser kann z. B. so aussehen (hier ohne Lexer, mit explizitem Syntaxbaum und mit wenig Templateargumenten - kein node<T>):
Code:
#include <iostream>
#include <string>
#include <memory> // std::unique_ptr<T>
#include <functional> // std::plus, ...
#include <cctype> // std::isdigit
#include <limits> // std:numeric_limits<T>
// Basisklasse aller Knoten
class node {
public:
// vielleicht muss eine erbende Klasse selbst aufräumen
virtual ~ node ();
// berechnet den Wert des Knotens; rein virtuell
virtual double eval () const = 0;
};
node :: ~ node () {}
// Warum nicht mal eine rein virtuelle Funktion definieren?
double node :: eval () const {
throw std::exception ("pure virtual function node::eval called");
}
// eine Klasse für Zahlen
class number : public node {
private:
double value;
public:
number (double value);
double eval () const override;
};
number :: number (double value)
: value (value) {}
double number :: eval () const {
return this->value;
}
// mit variadischen Templates ließe sich das einmal für jede beliebige Anzahl
// von Kindern machen, die werden aber erst ab VS2013 unterstützt
// Klassentemplate für unäre Knoten
template <typename unary_function>
class unary_node : public node {
private:
// das Kind
std::unique_ptr <node> child;
// die eigentliche Berechnungsfunktion
unary_function function;
public:
unary_node (std::unique_ptr <node> && child, unary_function function = unary_function ());
double eval () const override;
};
template <typename unary_function>
unary_node <unary_function> :: unary_node (std::unique_ptr <node> && child, unary_function function)
: child (std::move (child)), function (function) {}
template <typename unary_function>
double unary_node <unary_function> :: eval () const {
return this->function (this->child->eval ());
}
// Klassentemplate für binäre Knoten
template <typename binary_function>
class binary_node : public node {
private:
// linkes und rechtes Kind
std::unique_ptr <node> left;
std::unique_ptr <node> right;
// die eigentliche Berechnungsfunktion
binary_function function;
public:
binary_node (std::unique_ptr <node> && left, std::unique_ptr <node> && right, binary_function function = binary_function ());
double eval () const override;
};
template <typename binary_function>
binary_node <binary_function> :: binary_node (std::unique_ptr <node> && left, std::unique_ptr <node> && right, binary_function function)
: left (std::move (left)), right (std::move (right)), function (function) {}
template <typename binary_function>
double binary_node <binary_function> :: eval () const {
return this->function (this->left->eval (), this->right->eval ());
}
// ist so einfacher als mit selbst geschriebenen Klassen
typedef unary_node <std::negate <double>> negation;
typedef binary_node <std::plus <double>> addition;
typedef binary_node <std::minus <double>> subtraction;
typedef binary_node <std::multiplies <double>> multiplication;
typedef binary_node <std::divides <double>> division;
/* Grammatik
expression
Ausdruck -> Ausdruck + Term
Ausdruck -> Ausdruck - Term
Ausdruck -> Term
term
Term -> Term * Faktor
Term -> Term / Faktor
Term -> Faktor
factor
Faktor -> + Faktor
Faktor -> - Faktor
Faktor -> ( Ausdruck )
Faktor -> Zahl
number
Zahl -> Ziffer+
*/
// der Parser, ein Klassentemplate
template <typename input_iterator> // z. B. char*
// ent-auskommentieren, sobald es von IntelliSense vollständig unterstützt wird:
class parser //final
{
private:
input_iterator begin;
input_iterator end;
// die tatsächlichen Parse-Funktionen
std::unique_ptr <node> parse_expression ();
std::unique_ptr <node> parse_term ();
std::unique_ptr <node> parse_factor ();
std::unique_ptr <node> parse_unsigned_factor ();
std::unique_ptr <node> parse_number ();
// entfernt die Vorzeichen und gibt die Anzahl der Minus-Vorzeichen zurück
size_t parse_signs ();
public:
parser (input_iterator begin, input_iterator end);
// zum Parsen
std::unique_ptr <node> parse ();
};
template <typename input_iterator>
parser <input_iterator> :: parser (input_iterator begin, input_iterator end)
: begin (begin), end (end) {}
template <typename input_iterator>
std::unique_ptr <node> parser <input_iterator> :: parse () {
return this->parse_expression ();
}
template <typename input_iterator>
std::unique_ptr <node> parser <input_iterator> :: parse_expression () {
std::unique_ptr <node> term = this->parse_term ();
while (this->begin != this->end) {
if (* this->begin == '+') {
++this->begin;
// Gibt es da vielleicht eine einfachere Variante?
term.reset (new addition (std::unique_ptr <node> (std::move (term)), this->parse_term ()));
} else if (* this->begin == '-') {
++this->begin;
term.reset (new subtraction (std::unique_ptr <node> (term.release ()), this->parse_term ()));
} else
break;
}
return term;
}
template <typename input_iterator>
std::unique_ptr <node> parser <input_iterator> :: parse_term () {
std::unique_ptr <node> factor = this->parse_factor ();
while (this->begin != this->end) {
if (* this->begin == '*') {
++this->begin;
factor.reset (new multiplication (std::unique_ptr <node> (factor.release ()), this->parse_factor ()));
} else if (* this->begin == '/') {
++this->begin;
factor.reset (new division (std::unique_ptr <node> (factor.release ()), this->parse_factor ()));
} else
break;
}
return factor;
}
template <typename input_iterator>
std::unique_ptr <node> parser <input_iterator> :: parse_factor () {
if (this->begin == this->end)
throw std::exception ("syntax error, factor expected");
// vertraue dem Compiler, optimieren zu können
if (this->parse_signs () % 2 == 1)
return std::unique_ptr <node> (new negation (this->parse_unsigned_factor ()));
return this->parse_unsigned_factor ();
}
template <typename input_iterator>
std::unique_ptr <node> parser <input_iterator> :: parse_unsigned_factor () {
if (this->begin == this->end)
throw std::exception ("syntax error, unsigned factor expected");
if (* this->begin == '(') {
++this->begin;
auto expression = this->parse_expression ();
if (this->begin == this->end)
throw std::exception ("syntax error, missing ')'");
if (* this->begin != ')')
throw std::exception ("syntax error, ')' expected");
++this->begin;
return expression;
}
return this->parse_number ();
}
// nur natürliche Zahlen werden interpretiert
template <typename input_iterator>
std::unique_ptr <node> parser <input_iterator> :: parse_number () {
if (this->begin == this->end || ! std::isdigit (* this->begin))
throw std::exception ("syntax error, number expected");
double value = 0;
do {
value *= 10;
value += * this->begin++ - '0';
} while (this->begin != this->end && std::isdigit (* this->begin));
return std::unique_ptr <node> (new number (value));
}
template <typename input_iterator>
size_t parser <input_iterator> :: parse_signs () {
size_t count = 0;
while (true) {
if (this->begin == this->end)
throw std::exception ("syntax error, token expected");
if (* this->begin == '+')
++this->begin;
else if (* this->begin == '-') {
++count;
++this->begin;
} else
break;
}
return count;
}
// und jetzt die Funktion, die die Verwendung so schön einfach macht:
template <typename input_iterator>
std::unique_ptr <node> parse (input_iterator begin, input_iterator end) {
return parser <input_iterator> (begin, end).parse ();
}
int main () {
std::string expression;
std::cout << "expression: ";
std::cin >> expression;
try {
auto ast = parse (expression.cbegin (), expression.cend ());
double value = ast->eval ();
std::cout << "value: " << value << '\n';
} catch (std::exception ex) {
std::cout << ex.what () << '\n';
}
// damit cin.get() nicht sofort zurückkehrt
std::cin.ignore (std::numeric_limits <std::streamsize>::max (), '\n');
std::cin.get ();
}