Study results from Stroustrup's PPP3 book.
This commit is contained in:
parent
6363457d0f
commit
7a1093f982
10 changed files with 579 additions and 1 deletions
29
PPP3/PPP.h
Normal file
29
PPP3/PPP.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
// PPPheaders.h
|
||||
|
||||
#include<iostream>
|
||||
#include <sstream>
|
||||
#include<string>
|
||||
#include<vector>
|
||||
#include<span>
|
||||
#include<stdexcept>
|
||||
#include<random>
|
||||
|
||||
#include<stdint.h>
|
||||
#include<list>
|
||||
#include <map>
|
||||
#include<unordered_map>
|
||||
#include <set>
|
||||
#include<memory>
|
||||
#include <algorithm>
|
||||
|
||||
#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
|
184
PPP3/PPP_support.h
Normal file
184
PPP3/PPP_support.h
Normal file
|
@ -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<class T> Element = true;
|
||||
|
||||
PPP_EXPORT template <Element T>
|
||||
class Checked_vector : public std::vector<T> { // trivially range-checked vector (no iterator checking)
|
||||
public:
|
||||
using std::vector<T>::vector;
|
||||
|
||||
T& operator[](size_t i)
|
||||
{
|
||||
std::cerr << "PPP::vector::[]\n";
|
||||
return this->std::vector<T>::at(i);
|
||||
}
|
||||
|
||||
const T& operator[](size_t i) const
|
||||
{
|
||||
std::cerr << "PPP::vector::[] const\n";
|
||||
return this->std::vector<T>::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<Element T>
|
||||
class Checked_span : public std::span<T> { // range-checked span -- use gsl::span?
|
||||
public:
|
||||
using std::span<T>::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<Error_action action = default_error_action, typename C>
|
||||
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 <class T, class U>
|
||||
constexpr T narrow_cast(U&& u) noexcept
|
||||
{
|
||||
return static_cast<T>(std::forward<U>(u));
|
||||
}
|
||||
|
||||
PPP_EXPORT struct narrowing_error : public std::exception
|
||||
{
|
||||
const char* what() const noexcept override { return "narrowing_error"; }
|
||||
};
|
||||
|
||||
PPP_EXPORT template <class T, class U>
|
||||
constexpr T narrow(U u)
|
||||
{
|
||||
const T t = narrow_cast<T>(u);
|
||||
if (static_cast<U>(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<typename C>
|
||||
using Value_type = typename C::value_type;
|
||||
|
||||
template<typename C>
|
||||
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<PPP::Checked_string>
|
||||
{
|
||||
size_t operator()(const PPP::Checked_string& s) const
|
||||
{
|
||||
return hash<std::string>()(s);
|
||||
}
|
||||
};
|
17
PPP3/ex01.cpp
Normal file
17
PPP3/ex01.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <iostream>
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
19
PPP3/ex02.cpp
Normal file
19
PPP3/ex02.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include <iostream>
|
||||
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";
|
||||
}
|
||||
}
|
11
PPP3/ex03.cpp
Normal file
11
PPP3/ex03.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
int i = 0;
|
||||
|
||||
while(i < 100) {
|
||||
cout << i << '\t' << i * i << '\n';
|
||||
++i;
|
||||
}
|
||||
}
|
32
PPP3/ex04.cpp
Normal file
32
PPP3/ex04.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
vector<int> v = {5, 7, 9, 4, 6, 8};
|
||||
|
||||
for(unsigned int i = 0; i < v.size(); i++) {
|
||||
cout << "i=" << v[i] << "\n";
|
||||
}
|
||||
|
||||
vector<double> 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;
|
||||
}
|
44
PPP3/ex05.cpp
Normal file
44
PPP3/ex05.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
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<int> 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;
|
||||
}
|
||||
}
|
233
PPP3/ex06.cpp
Normal file
233
PPP3/ex06.cpp
Normal file
|
@ -0,0 +1,233 @@
|
|||
#include <iostream>
|
||||
#include <queue>
|
||||
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<Token> buffer;
|
||||
};
|
||||
|
||||
class Variable {
|
||||
public:
|
||||
string name;
|
||||
double value;
|
||||
};
|
||||
|
||||
vector<Variable> 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;
|
||||
}
|
9
PPP3/meson.build
Normal file
9
PPP3/meson.build
Normal file
|
@ -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')
|
|
@ -1,5 +1,5 @@
|
|||
project('sfmldemo', 'cpp',
|
||||
default_options: ['cpp_std=c++17'])
|
||||
default_options: ['cpp_std=c++20'])
|
||||
|
||||
dependencies = [
|
||||
dependency('sfml'),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue