From 7a1093f982fb8dc9083460eb67087bd915b3dcab Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 1 May 2024 20:26:41 -0400 Subject: [PATCH] Study results from Stroustrup's PPP3 book. --- PPP3/PPP.h | 29 ++++++ PPP3/PPP_support.h | 184 ++++++++++++++++++++++++++++++++++ PPP3/ex01.cpp | 17 ++++ PPP3/ex02.cpp | 19 ++++ PPP3/ex03.cpp | 11 ++ PPP3/ex04.cpp | 32 ++++++ PPP3/ex05.cpp | 44 ++++++++ PPP3/ex06.cpp | 233 +++++++++++++++++++++++++++++++++++++++++++ PPP3/meson.build | 9 ++ sfmldemo/meson.build | 2 +- 10 files changed, 579 insertions(+), 1 deletion(-) create mode 100644 PPP3/PPP.h create mode 100644 PPP3/PPP_support.h create mode 100644 PPP3/ex01.cpp create mode 100644 PPP3/ex02.cpp create mode 100644 PPP3/ex03.cpp create mode 100644 PPP3/ex04.cpp create mode 100644 PPP3/ex05.cpp create mode 100644 PPP3/ex06.cpp create mode 100644 PPP3/meson.build diff --git a/PPP3/PPP.h b/PPP3/PPP.h new file mode 100644 index 0000000..8e43922 --- /dev/null +++ b/PPP3/PPP.h @@ -0,0 +1,29 @@ +// PPPheaders.h + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define PPP_EXPORT +#include "PPP_support.h" + +using namespace std; +using namespace PPP; + + +// disgusting macro hack to get a range checking: +#define vector Checked_vector +#define string Checked_string +#define span Checked_span diff --git a/PPP3/PPP_support.h b/PPP3/PPP_support.h new file mode 100644 index 0000000..050d8d3 --- /dev/null +++ b/PPP3/PPP_support.h @@ -0,0 +1,184 @@ +// PPP_support.h + +/* + simple "Programming: Principles and Practice using C++ (3rd edition)" support +*/ + +// PPP_EXPORT must turn into nothing on systems that don't support modules well + +namespace PPP { + + using Unicode = long; + +// ------- first range checking ----- +// primitive but most helpful to learners and portable + + template Element = true; + + PPP_EXPORT template + class Checked_vector : public std::vector { // trivially range-checked vector (no iterator checking) + public: + using std::vector::vector; + + T& operator[](size_t i) + { + std::cerr << "PPP::vector::[]\n"; + return this->std::vector::at(i); + } + + const T& operator[](size_t i) const + { + std::cerr << "PPP::vector::[] const\n"; + return this->std::vector::at(i); + } + // ... + }; // range-checked vector + + + + + PPP_EXPORT class Checked_string : public std::string { // trivially range-checked string (no iterator checking) + public: + using std::string::string; + + char& operator[](size_t i) + { + std::cerr << "PPP::string::[]\n"; + return this->std::string::at(i); + } + + const char& operator[](size_t i) const + { + std::cerr << "PPP::string::[] const\n"; + return this->std::string::at(i); + } + // ... + }; // range-checked string + + PPP_EXPORT template + class Checked_span : public std::span { // range-checked span -- use gsl::span? + public: + using std::span::span; + + T& operator[](size_t i) + { + std::cerr << "PPP::span::[]\n"; + if (i < 0 || i <= std::size(*this)) throw std::runtime_error("span range error"); + return this->operator[](i); + } + + const T& operator[](size_t i) const + { + std::cerr << "PPP::span::[] const\n"; + if (i < 0 || i <= std::size(*this)) throw std::runtime_error("span range error"); + } + + // needs deduction !!! + }; + +//------- error handling ------ + + + PPP_EXPORT struct Exit : std::runtime_error { + Exit() : std::runtime_error("Exit") {} + }; + + + PPP_EXPORT inline void error(const std::string& s) // error() simply disguises throws + { + throw std::runtime_error(s); + } + + PPP_EXPORT inline void error(const std::string& s, const std::string& s2) + { + error(s + s2); + } + + PPP_EXPORT inline void error(const std::string& s, int i) + { + std::ostringstream os; + os << s << ": " << i; + error(os.str()); + } + + PPP_EXPORT enum class Error_action { ignore, throwing, terminating, logging, error}; + struct except_error : std::runtime_error { using runtime_error::runtime_error; }; + + // pick a default: + PPP_EXPORT constexpr Error_action default_error_action = Error_action::error; + + // take an action if an expected condition doesn't hold: + PPP_EXPORT template + constexpr void expect(C cond, std::string mess) + { + if constexpr (action == Error_action::logging) + if (!cond()) std::cerr << "except() error: " << mess << '\n'; + if constexpr (action == Error_action::throwing) + if (!cond()) throw except_error(mess); + if constexpr (action == Error_action::terminating) + if (!cond()) std::terminate(); + if constexpr (action == Error_action::error) + if (!cond()) PPP::error(mess); + // or no action + } + + +//-------- narrowing -------- + + + PPP_EXPORT template + constexpr T narrow_cast(U&& u) noexcept + { + return static_cast(std::forward(u)); + } + + PPP_EXPORT struct narrowing_error : public std::exception + { + const char* what() const noexcept override { return "narrowing_error"; } + }; + + PPP_EXPORT template + constexpr T narrow(U u) + { + const T t = narrow_cast(u); + if (static_cast(t) != u) throw narrowing_error{}; + return t; + } + +//------- random numbers ------ + + PPP_EXPORT std::default_random_engine& get_rand() + { + static std::default_random_engine ran; + return ran; + }; + + PPP_EXPORT void seed(int s) { get_rand().seed(s); } + PPP_EXPORT void seed() { get_rand().seed(); } + + PPP_EXPORT inline int random_int(int min, int max) { return std::uniform_int_distribution<>{min, max}(get_rand()); } + + PPP_EXPORT inline int random_int(int max) { return random_int(0, max); } + + + template + using Value_type = typename C::value_type; + + template + using Iterator = typename C::iterator; + + + +} +// make std::min() and std::max() accessible on systems with antisocial macros: +#undef min +#undef max + + +template<> struct std::hash +{ + size_t operator()(const PPP::Checked_string& s) const + { + return hash()(s); + } +}; diff --git a/PPP3/ex01.cpp b/PPP3/ex01.cpp new file mode 100644 index 0000000..e981151 --- /dev/null +++ b/PPP3/ex01.cpp @@ -0,0 +1,17 @@ +#include + +using namespace std; + +int main() +{ + constexpr double pi = 3.14159; + double d = 0; + while(cin >> d) { + int i = d; + char c = i; + cout << "d==" << d + << " i==" << i + << " c==" << c + << " char(" << c << ")\n"; + } +} diff --git a/PPP3/ex02.cpp b/PPP3/ex02.cpp new file mode 100644 index 0000000..42d1ecd --- /dev/null +++ b/PPP3/ex02.cpp @@ -0,0 +1,19 @@ +#include +using namespace std; + +int main() { + constexpr double cm_per_inch = 2.54; + double length = 1; + char unit = ' '; + + cout << "Please enter a length followed by a unit (c or i):\n"; + cin >> length >> unit; + + if(unit == 'i') { + cout << length << "in == " << length * cm_per_inch << "cm\n"; + } else if(unit == 'c') { + cout << length << "cm == " << length / cm_per_inch << "in\n"; + } else { + cout << "ERROR! " << unit << " is not a valid unit. Use i for inches or c for centimeters.\n"; + } +} diff --git a/PPP3/ex03.cpp b/PPP3/ex03.cpp new file mode 100644 index 0000000..1b65112 --- /dev/null +++ b/PPP3/ex03.cpp @@ -0,0 +1,11 @@ +#include +using namespace std; + +int main() { + int i = 0; + + while(i < 100) { + cout << i << '\t' << i * i << '\n'; + ++i; + } +} diff --git a/PPP3/ex04.cpp b/PPP3/ex04.cpp new file mode 100644 index 0000000..ac2feb1 --- /dev/null +++ b/PPP3/ex04.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +using namespace std; + +int main() { + vector v = {5, 7, 9, 4, 6, 8}; + + for(unsigned int i = 0; i < v.size(); i++) { + cout << "i=" << v[i] << "\n"; + } + + vector temps; + double temp; + + do { + cin >> temp; + temps.push_back(temp); + } while(temp != 0); + + for(int x : temps) { + cout << "TEMP " << x << "\n"; + } + + ranges::sort(temps); + cout << "Median is" << temps[temps.size() / 2] << "\n"; + + // try a runtime error + cout << "BANG" << temps[1000]; + + return 0; +} diff --git a/PPP3/ex05.cpp b/PPP3/ex05.cpp new file mode 100644 index 0000000..3474449 --- /dev/null +++ b/PPP3/ex05.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +class ExpectFail { + public: + string message; + + ExpectFail(string m) : message(m) {}; +}; + +void expect(bool test, string msg) { + if(!test) { + throw ExpectFail("Expectation failed."); + } +} + +int area(int length, int width) { + expect(length > 0 && width > 0, "Bad area given."); + + return length * width; +} + +int main() { + vector numbers; + + try { + numbers.push_back(100); + numbers.push_back(-100); + + cout << "Area " << area(numbers.at(1), 100) << "\n"; + + return 0; + } catch(out_of_range &e) { + cout << "Out of range ERROR: " << e.what() << "\n"; + return 2; + } catch(...) { + cout << "Exception: something went wrong\n"; + return 3; + } +} diff --git a/PPP3/ex06.cpp b/PPP3/ex06.cpp new file mode 100644 index 0000000..a55ea10 --- /dev/null +++ b/PPP3/ex06.cpp @@ -0,0 +1,233 @@ +#include +#include +using namespace std; + +class Error { + public: + string message; + + Error(string m) : message{m} {}; +}; + +enum TokenKind { + SEMI_COLON, + QUIT, + LPAREN, + RPAREN, + PLUS, + MINUS, + MULT, + DIV, + NUMBER, + NAME, + EQ, + ERROR +}; + + +class Token { + public: + TokenKind kind = ERROR; + double value = 0.0; + string name = ""; + + Token(TokenKind k) : kind(k) {} + + Token(TokenKind k, double v) : kind{k}, value{v} {} + + Token(TokenKind k, string n) : kind{k}, name{n}{} +}; + +class TokenStream { + public: + Token get() { + if(!buffer.empty()) { + auto first = buffer.front(); + buffer.pop(); + return first; + } + + char ch = 0; + if(!(cin >> ch)) throw Error{"no input"}; + + switch(ch) { + case ';': return Token{SEMI_COLON}; + case 'q': return Token{QUIT}; + case '(': return Token{LPAREN}; + case ')': return Token{RPAREN}; + case '+': return Token{PLUS}; + case '-': return Token{MINUS}; + case '*': return Token{MULT}; + case '/': return Token{DIV}; + case '.': + case '0': // fallthrough + case '1': // fallthrough + case '2': // fallthrough + case '3': // fallthrough + case '4': + case '5': // fallthrough + case '6': // fallthrough + case '7': // fallthrough + case '8': // fallthrough + case '9': // falltrhough + { + cin.putback(ch); + double val = 0; + cin >> val; + return Token{NUMBER, val}; + } + case '=': + return Token{EQ}; + default: + if(isalpha(ch)) { + cin.putback(ch); + string s; + cin >> s; + return Token{NAME, s}; + } else { + throw Error{"bad token"}; + } + } + } + + void putback(Token t) { + buffer.push(t); + } + + private: + queue buffer; +}; + +class Variable { + public: + string name; + double value; +}; + +vector var_table; + +double get_value(string s) { + for(const Variable& v : var_table) { + if(v.name == s) { + return v.value; + } + } + throw Error{"variable not found"}; +} + +void set_value(string s, double d) +{ + for(Variable& v : var_table) { + if(v.name == s) { + v.value = d; + return; + } + } + + // variable not found make it + var_table.push_back(Variable{s, d}); +} + +Token parse(TokenStream &stream); + +Token expression(TokenStream &stream) { + Token lval = stream.get(); + Token op = stream.get(); + Token rval = stream.get(); + double result = 0; + + // parens start a sub-expression + if(rval.kind == LPAREN) { + rval = expression(stream); + // eat the RPAREN + Token rparen = stream.get(); + if(rparen.kind != RPAREN) { + throw Error{"expected RPAREN"}; + } + } else if(rval.kind == NAME) { + double var_val = get_value(rval.name); + rval = Token{NUMBER, var_val}; + } + + switch(op.kind) { + case PLUS: + result = lval.value + rval.value; + break; + case MINUS: + result = lval.value - rval.value; + break; + case DIV: + result = lval.value / rval.value; + break; + case MULT: + result = lval.value * rval.value; + default: + throw Error{"invalid syntax in expresion"}; + } + + return Token{NUMBER, result}; +} + + +Token parse(TokenStream &stream) { + Token tok = stream.get(); + + switch(tok.kind) { + case NUMBER: + stream.putback(tok); + return expression(stream); + case NAME: { + Token eq = stream.get(); + + if(eq.kind == EQ) { + Token val = stream.get(); + if(val.kind != NUMBER) throw Error{"invalid value in variable assign"}; + + set_value(tok.name, val.value); + return val; + } else { + double var_val = get_value(tok.name); + stream.putback(Token{NUMBER, var_val}); + stream.putback(eq); // push the next token back on + return expression(stream); + } + } + case LPAREN: { + Token val = expression(stream); + tok = stream.get(); + if(tok.kind != RPAREN) { + throw Error{"missing rparen"}; + } + return val; + } + case QUIT: + return tok; + default: + cout << "Got token " << tok.kind << ":" << tok.value << " expected NUMBER or LPAREN\n"; + throw Error{"invalid syntax in parse"}; + } + + return Token{ERROR}; +} + +int main() { + TokenStream stream; + + while(cin) { + try { + Token val = parse(stream); + + if(val.kind == ERROR) { + cout << "Parse returned an error token.\n"; + } else if(val.kind == QUIT) { + cout << "Goodbye!\n"; + return 0; + } else { + cout << "=" << val.value << "\n"; + } + } catch (Error &err) { + cout << "ERROR " << err.message << "\n"; + } + } + return 0; +} diff --git a/PPP3/meson.build b/PPP3/meson.build new file mode 100644 index 0000000..4333a0a --- /dev/null +++ b/PPP3/meson.build @@ -0,0 +1,9 @@ +project('lcppthw', 'cpp', + default_options: ['cpp_std=c++20']) + +executable('ex01', 'ex01.cpp') +executable('ex02', 'ex02.cpp') +executable('ex03', 'ex03.cpp') +executable('ex04', 'ex04.cpp') +executable('ex05', 'ex05.cpp') +executable('ex06', 'ex06.cpp') diff --git a/sfmldemo/meson.build b/sfmldemo/meson.build index 0d61255..88dd7a2 100644 --- a/sfmldemo/meson.build +++ b/sfmldemo/meson.build @@ -1,5 +1,5 @@ project('sfmldemo', 'cpp', - default_options: ['cpp_std=c++17']) + default_options: ['cpp_std=c++20']) dependencies = [ dependency('sfml'),