rest of tasks

This commit is contained in:
Ondrej Hladuvka 2025-05-21 21:10:59 +03:00
parent 75ad36cb37
commit bc71d512a0
17 changed files with 1854 additions and 0 deletions

16
CT1/booking/Makefile Normal file
View File

@ -0,0 +1,16 @@
CXX = g++
CXXFLAGS = -std=c++20 -Wall -pedantic -O2
LDFLAGS =
SRCS = $(wildcard *.cpp)
EXECS = $(SRCS:.cpp=)
all: $(EXECS)
%: %.cpp
$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS)
clean:
rm -f $(EXECS)
.PHONY: all clean

206
CT1/booking/booking.cpp Normal file
View File

@ -0,0 +1,206 @@
#include <iostream>
#include <vector>
#include <cstdint>
#include <limits>
#include <sstream>
#define RESET "\033[0m"
#define RED "❌ \033[31m"
#define GREEN "✅ \033[32m"
#define BOLDWHITE "\033[1m\033[37m"
constexpr const char* MENU_MSG = "=== Ticket Booking System ===\n1. Configure>\n2. Reserve\n3. Cancel\n4. Check\n5. Exit\n";
constexpr const char* REWRITE_WARNING_MSG = "WARNING: all reservations will be lost. Do you wish to continue? (0/1)\n";
constexpr const char* CURSOR_MSG = "> ";
using seat_num = uint64_t;
using uiBool = bool;
class BookingException : public std::exception {
public: const char* what() const noexcept override {
return "Internal exception!";
}
};
class AlreadyReserved : public BookingException {
const char* what() const noexcept override {
return "Already reserved!";
}
};
class NotReserved : public BookingException {
const char* what() const noexcept override {
return "Not reserved!";
}
};
class InvalidSeat : public BookingException {
const char* what() const noexcept override {
return "Invalid seat number!";
}
};
class InvalidArgument : public BookingException {
const char* what() const noexcept override {
return "Invalid argument!";
}
};
class NotConfigured : public BookingException {
const char* what() const noexcept override {
return "Not configured!";
}
};
enum class MenuState {
MENU,
SETUP = 1,
RESERVE = 2,
CANCEL = 3,
CHECK = 4,
EXIT = 5,
};
std::istream& operator>>(std::istream& is, MenuState& state) {
int a;
if (!(is >> a)) {
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
throw InvalidArgument();
}
state = static_cast<MenuState>(a);
return is;
}
template <class T>
T getInput(std::istream& is) {
T n;
if (!(is >> n) || n < 0) {
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
throw InvalidSeat();
}
return n;
}
template <>
uiBool getInput<uiBool>(std::istream& is) {
uint64_t n;
if (!(is >> n) || (n != 1 && n != 0)) {
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
throw InvalidArgument();
}
return n == 1;
}
class Seat { //TODO jmeno rezervace
private:
bool reserved;
public:
Seat() : reserved(false) {}
bool getState() {
return reserved;
}
bool setState(bool s) {
reserved = s;
return true;
}
};
class Booker {
public:
Booker() : inUse(false), configured(false) {};
Booker(seat_num capacity) : seats(capacity), inUse(false), configured(true) {}
~Booker() = default;
bool getSeatState(seat_num seat) {
if (!configured) throw NotConfigured();
if (seat >= seats.size()) throw InvalidSeat();
return seats.at(seat).getState();
}
bool reserveSeat(seat_num seat) {
if (!configured) throw NotConfigured();
if (seat >= seats.size()) throw InvalidSeat();
if (seats.at(seat).getState()) throw AlreadyReserved();
inUse = true;
return seats.at(seat).setState(true);
}
bool cancelSeat(seat_num seat) {
if (!configured) throw NotConfigured();
if (seat >= seats.size()) throw InvalidSeat();
if (!seats.at(seat).getState()) throw NotReserved();
return seats.at(seat).setState(false);
}
bool getInUse() { return inUse; }
private:
std::vector<Seat> seats;
bool inUse;
bool configured;
};
int main(int ragc, char* argv[]) {
std::ostream& os = std::cout;
std::istream& is = std::cin;
MenuState state = MenuState::MENU;
Booker h;
for (;;) {
try {
seat_num seat;
os << BOLDWHITE << MENU_MSG << RESET;
is >> state;
switch (state) {
case MenuState::SETUP :
if (h.getInUse()) {
os << REWRITE_WARNING_MSG << CURSOR_MSG;
if (!getInput<uiBool>(is)) break;
}
os << "Enter a number of avaliable seats: \n" << CURSOR_MSG;
seat = getInput<seat_num>(is);
h = Booker(seat);
os << GREEN << "New avaliable seat count: " << seat << "\n" << RESET;
break;
case MenuState::RESERVE :
os << "Seat to be reserved: ";
seat = getInput<seat_num>(is);
h.reserveSeat(seat);
os << GREEN << "Seat " << seat << " reserved\n" << RESET;
break;
case MenuState::CANCEL :
os << "Seat to be cancelled: ";
seat = getInput<seat_num>(is);
h.cancelSeat(seat);
os << GREEN << "Seat " << seat << " cancelled\n" << RESET;
break;
case MenuState::CHECK :
os << "Seat to be checked: ";
seat = getInput<seat_num>(is);
if (h.getSeatState(seat))
os << RED << "Seat " << seat << " is reserved\n" << RESET;
else
os << GREEN << "Seat " << seat << " is avaliable\n" << RESET;
break;
case MenuState::EXIT :
os << "Exiting...\n";
return EXIT_SUCCESS;
break;
case MenuState::MENU :
default:
throw InvalidArgument();
}
} catch (BookingException& e) {
os << RED << e.what() << RESET;
// return EXIT_FAILURE;
}
state = MenuState::MENU;
os << "\n\n";
}
}

11
CT2/Makefile Normal file
View File

@ -0,0 +1,11 @@
CXX = g++
CXXFLAGS = -std=c++20 -Wall -pedantic -O2
LDFLAGS =
main: main.cpp booking.cpp menugen.cpp
$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS)
.PHONY clean:
rm -f main

117
CT2/booking.cpp Normal file
View File

@ -0,0 +1,117 @@
#pragma once
#include <cstdint>
#include <ostream>
#include <algorithm>
#include <functional>
#include "menugen.cpp"
namespace Booking {
using seat_num = uint64_t;
using seat_range = std::pair<seat_num, seat_num>;
using uiBool = bool;
constexpr size_t SEAT_CAPACITY = 20;
class BookingException : public std::exception {
public: const char* what() const noexcept override { return "Internal exception!"; }
};
#define EXCEPTION(INTERNAL, EXTARNAL) \
class INTERNAL : public BookingException { \
const char* what() const noexcept override { return EXTARNAL; } \
};
EXCEPTION(AlreadyReserved, "Already reserved!")
EXCEPTION(NotReserved, "Not reserved!")
EXCEPTION(InvalidSeat, "Invalid seat number!");
EXCEPTION(InvalidArgument, "Invalid argument!")
struct Seat {
enum class Status {
AVALIBLE,
RESERVED,
BROKEN
};
Status s = Status::AVALIBLE;
};
class Booker {
private:
Seat seats[SEAT_CAPACITY];
public:
Seat::Status getSeatState(seat_num seat) {
return seats[seat - 1].s;
}
void reserveSeat(seat_range r) {
bool status = false;
for (auto i = r.first - 1; i < r.second; ++i)
status |= seats[i].s != Seat::Status::AVALIBLE;
if (status) throw AlreadyReserved();
for (auto i = r.first - 1; i < r.second; ++i)
seats[i].s = Seat::Status::RESERVED;
}
void cancelSeat(seat_range r) {
bool status = false;
for (auto i = r.first - 1; i < r.second; ++i)
status |= seats[i].s != Seat::Status::RESERVED;
if (status) throw NotReserved();
for (auto i = r.first - 1; i < r.second; ++i)
seats[i].s = Seat::Status::AVALIBLE;
}
void breakSeat(seat_range r, std::ostream&os, const char* prefix) {
for (auto i = r.first - 1; i < r.second; ++i) {
if (seats[i].s == Seat::Status::RESERVED)
os << prefix << "Reservation on seat " << i + 1 << " is canceled, seat is now broken\n";
seats[i].s = Seat::Status::BROKEN;
}
}
void filter(const std::function<bool(const Seat&)>& f, std::ostream& os) {
for (seat_num i = 0; i <= SEAT_CAPACITY; ++i) {
if (f(seats[i]))
os << i + 1 << ' ';
}
os << '\n';
}
auto operator<=>(const Booker& oth) const = default;
Booker operator+(const Booker& oth) {
Booker b;
for (seat_num i = 0; i < SEAT_CAPACITY; ++i) {
if (seats[i].s == Seat::Status::AVALIBLE ||
oth.seats[i].s == Seat::Status::AVALIBLE)
b.seats[i].s = Seat::Status::AVALIBLE;
if (seats[i].s == Seat::Status::RESERVED ||
oth.seats[i].s == Seat::Status::RESERVED)
b.seats[i].s = Seat::Status::RESERVED;
if (seats[i].s == Seat::Status::BROKEN ||
oth.seats[i].s == Seat::Status::BROKEN)
b.seats[i].s = Seat::Status::BROKEN;
}
return b;
}
void print(seat_range r, std::ostream& os) const {
for (seat_num i = r.first - 1; i < r.second; ++i) {
switch (seats[i].s) {
case Seat::Status::AVALIBLE: os << Menu::GREEN << "Seat " << i + 1 << " is avaliable\n" << Menu::RESET; break;
case Seat::Status::RESERVED: os << Menu::YELLOW << "Seat " << i + 1 << " is reserved\n" << Menu::RESET; break;
case Seat::Status::BROKEN: os << Menu::RED << "Seat " << i + 1 << " is broken\n" << Menu::RESET; break;
default: break;
}
}
}
};
}
std::ostream& operator<<(std::ostream& os, const Booking::Booker& b) {
b.print({1, Booking::SEAT_CAPACITY}, os);
return os;
}

160
CT2/main.cpp Normal file
View File

@ -0,0 +1,160 @@
#include <iostream>
#include <limits> // streamsize::max
#include "booking.cpp"
/* declaring X(option) generates:
- Ordered menu message entry
- Ordered Menu::State::option symbol
*/
#define MENU_ENTRIES \
X(RESERVE), \
X(CANCEL), \
X(CHECK), \
X(BREAK), \
X(FILTER_AVALIABLE), \
X(FILTER_RESERVED), \
X(FILTER_BROKEN), \
X(EXIT)
#include "menugen.cpp"
namespace Menu {
// generate enum
#define X(entry) entry
enum class State : StateType{
MENU = 0, // default state
MENU_ENTRIES,
COUNT // number of menu entries
};
#undef X
// generate menu message
constexpr auto generateMenuMessage() {
constexpr char menuMsg[] = "=== Ticket Booking System ===";
constexpr char dot[] = ". ";
constexpr char nl[] = "\n";
constexpr auto str = concat( nl, nl, menuMsg, nl,
#define X(entry) NumToString<static_cast<StateType>(State::entry)>::value, dot, #entry, nl
MENU_ENTRIES
#undef X
);
return str;
}
}
constexpr auto MENU_MSG = Menu::generateMenuMessage();
std::istream& operator>>(std::istream& is, Menu::State& state) {
if (!(is >> reinterpret_cast<Menu::StateType&>(state))) throw Booking::InvalidArgument();
return is;
}
std::ostream& operator<<(std::ostream& os, Booking::seat_range r) {
if (r.second == r.first) os << " seat " << r.first << " is ";
else os << " seats " << r.first << "-" << r.second << " are ";
return os;
}
std::pair<int, int> getRange(std::istream& is) {
auto isEnd = [](char c) { return c == '\n' || c == EOF; };
try {
std::pair<Booking::seat_num, Booking::seat_num> output;
if (!(is >> output.first) || output.first < 1 || output.first > Booking::SEAT_CAPACITY)
throw Booking::InvalidSeat();
output.second = output.first;
while (!std::isdigit(is.peek())) {
char c = is.get();
if (c == '-') break;
if (isEnd(c)) return output;
if (!std::iswspace(c)) throw Booking::InvalidSeat();
}
if (!(is >> output.second) || output.second < 1 || output.second > Booking::SEAT_CAPACITY)
throw Booking::InvalidSeat();
char c = is.peek();
if (isEnd(c)) {
is.get(); // clear newline or eof
if (output.first > output.second)
std::swap(output.first, output.second);
return output;
}
while (std::iswspace(c - is.get())) if (isEnd(c)) return output;
throw Booking::InvalidSeat(); // unexpected character
} catch(...) {
throw Booking::InvalidSeat();
}
}
int main(int ragc, char* argv[]) {
std::ostream& os = std::cout;
std::istream& is = std::cin;
Menu::State state = Menu::State::MENU;
Booking::Booker h;
Booking::seat_range range;
for (;;) try {
state = Menu::State::MENU;
os << Menu::BOLDWHITE << MENU_MSG << Menu::RESET << Menu::CURSOR; is >> state;
switch (state) {
case Menu::State::RESERVE:
os << "Seat(s) to be reserved:\n" << Menu::CURSOR;
range = getRange(is);
// for (Booking::seat_num i = range.first; i <= range.second; ++i) h.reserveSeat(i);
h.reserveSeat(range);
os << Menu::GREEN << range << "reserved\n" << Menu::RESET;
break;
case Menu::State::CANCEL:
os << "Seat(s) to be cancelled:\n" << Menu::CURSOR;
range = getRange(is);
h.cancelSeat(range);
os << Menu::GREEN << range << "cancelled\n" << Menu::RESET;
break;
case Menu::State::BREAK:
os << "Seat(s) to be broken:\n" << Menu::CURSOR;
range = getRange(is);
h.breakSeat(range, os, Menu::RED);
os << Menu::RESET << range << "broken";
break;
case Menu::State::CHECK:
os << "Seat(s) to be checked:\n" << Menu::CURSOR;
range = getRange(is);
h.print(range, os);
break;
case Menu::State::FILTER_BROKEN:
os << "Avaliable seats are: ";
h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::BROKEN; }, os);
break;
case Menu::State::FILTER_AVALIABLE:
os << "Avaliable seats are: ";
h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::AVALIBLE; }, os);
break;
case Menu::State::FILTER_RESERVED:
os << "Reserved seats are: ";
h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::RESERVED; }, os);
break;
case Menu::State::EXIT:
os << "Exiting...\n";
return EXIT_SUCCESS;
break;
default:
throw Booking::InvalidArgument();
}
} catch (Booking::BookingException& e) {
os << Menu::RED << e.what() << Menu::RESET << "\n";
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

138
CT2/menugen.cpp Normal file
View File

@ -0,0 +1,138 @@
#ifndef MENUGEN_0219820379823487123785
#define MENUGEN_0219820379823487123785
// This is collection of utility constexpr tools for generating menu dynamically in compile time
// Menu::generateMenuMessage basically creates this:
// constexpr const char* MENU_MSG = "\n\n=== Ticket Booking System ===\n1. Configure\n2. Reserve\n3. Cancel\n4. Check\n5. Exit\n";
// enum class State : int {
// MENU = 0,
// SETUP = 1,
// RESERVE = 2,
// CANCEL = 3,
// CHECK = 4,
// EXIT = 5,
// };
// Its kinda long & ugly because we need to generate both symbols in enum and use their values in the message
// Menu entries are defined with generic macro X() which is defined where menu is expanded and formats each entry there.
// This is trivial for pure enum creation, but we also need to count them, this is achived with Menu::State::COUNT beeing last element
// Next step is creating message. To be constexpr we cant use string, I experimented with string_view for few hours,
// but they are a lot less versatile than std::array, so Ive got first prototype working with them.
// But STL containers are forbidden in CT2... so i wrote my own ConstString :)
// With constexpr ready container, generating message is simple as
// 1. transforming number(enum member) to string
// 2. summing up all the submessage lengths
// 3. copying to single container
// I was suprised cpp20 doesnt include way to transform int to string in constexpr.
// So i achived this with recursive template ExpandDecimal - name is self explanatory.
// Calculating total length is just sum of the sizes of expanded template pack
// And copying with std::array would be just std::copy_n, but same can be achived on my structure with index_sequence from utility library
// thats all, hope you find it interesting :)
#include <utility> // index_sequence
namespace Menu{
constexpr auto RESET = "\033[0m";
constexpr auto RED = "\033[31m";
constexpr auto GREEN = "\033[32m";
constexpr auto YELLOW = "\033[33m";
constexpr auto BOLDWHITE = "\033[1m\033[37m";
constexpr auto CURSOR = "> ";
using StateType = size_t;
//###################################################################################
// helper char[] wrapper since it cannot be directly returned
template<size_t size>
struct ConstString { char data[size + 1];};
template<size_t size>
std::ostream& operator<<(std::ostream& os, const ConstString<size>& r) {
for (char c : r.data)
os << c;
return os;
}
//###################################################################################
// helpers to convert int (from enum) to string in ##################################
// shared array to store result
template<StateType... digits>
struct to_chars { static const char value[]; };
// convert single digit to char
template<StateType... digits>
// constexpr char to_chars<digits...>::value[] = {('0' + digits)...};
constexpr char to_chars<digits...>::value[] = {('0' + digits)..., 0}; // null termination
// when reminder is not zero - recursive expand for each decimal digit
template<StateType rem, StateType... digits>
struct ExpandDecimal : ExpandDecimal<rem / 10, rem % 10, digits...> {};
// when reminder is not zero - call to_char for last digit
template<StateType... digits>
struct ExpandDecimal<0, digits...> : to_chars<digits...> {};
// entry point - to by called directly
template<StateType num>
struct NumToString : ExpandDecimal<num> {};
//###################################################################################
// helper templates to calculate total length as constexpr ##########################
template<typename... Strings>
constexpr std::size_t total_length(Strings&&... strings) {
return (0 + ... + std::size(strings));
}
//###################################################################################
// helper templates to concatonate as constexpr #####################################
template<typename Str, std::size_t... I> // analog to copy_n from <algorithm>
static constexpr void copy_str(char* dest, std::size_t& idx,
Str str, std::index_sequence<I...>) {
((dest[idx++] = str[I]), ...);
}
template<typename... Strings>
constexpr auto concat(Strings&&... strings) {
constexpr std::size_t total_len = (0 + ... + (std::size(strings) - 1));
ConstString<total_len> result; std::size_t idx = 0;
((copy_str(result.data, idx, strings, // copy each string to result
std::make_index_sequence<std::size(strings) - 1>{}), ...));
result.data[total_len] = '\0';
return result;
}
//###################################################################################
// tests ############################################################################
static_assert(sizeof(NumToString<1>::value) == 2, "NumToString: size check");
static_assert(NumToString<1>::value[0] == '1', "NumToString: 1");
static_assert(NumToString<12345678900>::value[0] == '1' &&
NumToString<12345678900>::value[1] == '2' &&
NumToString<12345678900>::value[2] == '3' &&
NumToString<12345678900>::value[3] == '4' &&
NumToString<12345678900>::value[4] == '5' &&
NumToString<12345678900>::value[5] == '6' &&
NumToString<12345678900>::value[6] == '7' &&
NumToString<12345678900>::value[7] == '8' &&
NumToString<12345678900>::value[8] == '9' &&
NumToString<12345678900>::value[9] == '0' &&
NumToString<12345678900>::value[10] == '0' &&
NumToString<12345678900>::value[11] == '\0'&&
sizeof(NumToString<12345678900>::value) == 12, "NumToString: longnum");
static_assert(sizeof(concat("a", "bc", "def")) == sizeof("abcdef") , "concat: size check");
static_assert(concat("a", "bc", "def").data[0] == 'a' &&
concat("a", "bc", "def").data[1] == 'b' &&
concat("a", "bc", "def").data[2] == 'c' &&
concat("a", "bc", "def").data[3] == 'd' &&
concat("a", "bc", "def").data[4] == 'e' &&
concat("a", "bc", "def").data[5] == 'f' &&
concat("a", "bc", "def").data[6] == '\0', "concat: content");
}
#endif //MENUGEN_0219820379823487123785

11
CT3/Makefile Normal file
View File

@ -0,0 +1,11 @@
CXX = g++
CXXFLAGS = -std=c++20 -Wall -pedantic -O2
LDFLAGS =
main: main.cpp booking.cpp menugen.cpp
$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS)
.PHONY clean:
rm -f main

152
CT3/booking.cpp Normal file
View File

@ -0,0 +1,152 @@
#pragma once
#include <cstdint>
#include <ostream>
#include <algorithm>
#include <functional>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <memory>
#include "menugen.cpp"
namespace Booking {
using seat_num = uint64_t;
using seat_range = std::pair<seat_num, seat_num>;
using uiBool = bool;
constexpr size_t SEAT_CAPACITY = 20;
class BookingException : public std::exception {
public: const char* what() const noexcept override { return "Internal exception!"; }
};
#define EXCEPTION(INTERNAL, EXTARNAL) \
class INTERNAL : public BookingException { \
const char* what() const noexcept override { return EXTARNAL; } \
};
EXCEPTION(AlreadyReserved, "Already reserved!")
EXCEPTION(NotReserved, "Not reserved!")
EXCEPTION(InvalidSeat, "Invalid seat number!");
EXCEPTION(InvalidArgument, "Invalid argument!")
class Seat : public std::enable_shared_from_this<Seat> {
struct Private{ explicit Private() = default; };
public:
enum class Type {
VIP, REGULAR, CHEAP
};
enum class Status {
AVALIBLE, RESERVED, BROKEN
};
Status s = Status::AVALIBLE;
Type c;
Seat(Private, Type t) : c(t) {}
static std::shared_ptr<Seat> create(Type t) {
return std::make_shared<Seat>(Private(), t);
}
std::shared_ptr<Seat> getptr() {
return shared_from_this();
}
};
class Booker {
private:
std::map< seat_num, std::shared_ptr< Seat > > seatmap;
std::map< Seat::Type, std::queue< std::string > > queues =
{{Seat::Type::VIP, {}}, {Seat::Type::REGULAR, {}}, {Seat::Type::CHEAP, {}}};
std::map< Seat::Type, std::queue< std::shared_ptr< Seat > > > avaliables =
{{Seat::Type::VIP, {}}, {Seat::Type::REGULAR, {}}, {Seat::Type::CHEAP, {}}};
public:
Booker(seat_num vip = 5, seat_num regular = 10, seat_num cheap = 10) {
for (seat_num i = 0; i < vip; ++i) {
auto s = Seat::create(Seat::Type::VIP);
seatmap.emplace(seatmap.size(), s->getptr() );
avaliables[Seat::Type::VIP].emplace(s->getptr());
}
for (seat_num i = 0; i < regular; ++i) {
auto s = Seat::create(Seat::Type::REGULAR);
seatmap.emplace(seatmap.size(), s->getptr() );
avaliables[Seat::Type::REGULAR].emplace(s->getptr());
}
for (seat_num i = 0; i < cheap; ++i) {
auto s = Seat::create(Seat::Type::CHEAP);
seatmap.emplace(seatmap.size(), s->getptr() );
avaliables[Seat::Type::CHEAP].emplace(s->getptr());
}
}
Seat::Status getSeatState(seat_num seat) {
return seatmap.at(seat)->s;
}
void reserveSeat(Seat::Type t, seat_num n = 1) {
for (seat_num i = 0; i < n; ++i) {
if (avaliables[t].size()) {
auto s = avaliables[t].front();
avaliables[t].pop();
seatmap[s]
}
}
}
void reserveSeat(seat_range r) {
// bool status = false;
// for (auto i = r.first - 1; i < r.second; ++i)
// status |= seats[i].s != Seat::Status::AVALIBLE;
// if (status) throw AlreadyReserved();
// for (auto i = r.first - 1; i < r.second; ++i)
// seats[i].s = Seat::Status::RESERVED;
}
void cancelSeat(seat_range r) {
// bool status = false;
// for (auto i = r.first - 1; i < r.second; ++i)
// status |= seats[i].s != Seat::Status::RESERVED;
// if (status) throw NotReserved();
// for (auto i = r.first - 1; i < r.second; ++i)
// seats[i].s = Seat::Status::AVALIBLE;
}
void breakSeat(seat_range r, std::ostream&os, const char* prefix) {
// for (auto i = r.first - 1; i < r.second; ++i) {
// if (seats[i].s == Seat::Status::RESERVED)
// os << prefix << "Reservation on seat " << i + 1 << " is canceled, seat is now broken\n";
// seats[i].s = Seat::Status::BROKEN;
// }
}
void filter(const std::function<bool(const Seat&)>& f, std::ostream& os) {
// for (seat_num i = 0; i <= SEAT_CAPACITY; ++i) {
// if (f(seats[i]))
// os << i + 1 << ' ';
// }
// os << '\n';
}
void print(seat_range r, std::ostream& os) const {
// for (seat_num i = r.first - 1; i < r.second; ++i) {
// switch (seats[i].s) {
// case Seat::Status::AVALIBLE: os << Menu::GREEN << "Seat " << i + 1 << " is avaliable\n" << Menu::RESET; break;
// case Seat::Status::RESERVED: os << Menu::YELLOW << "Seat " << i + 1 << " is reserved\n" << Menu::RESET; break;
// case Seat::Status::BROKEN: os << Menu::RED << "Seat " << i + 1 << " is broken\n" << Menu::RESET; break;
// default: break;
// }
// }
}
};
}
std::ostream& operator<<(std::ostream& os, const Booking::Booker& b) {
b.print({1, Booking::SEAT_CAPACITY}, os);
return os;
}

159
CT3/main.cpp Normal file
View File

@ -0,0 +1,159 @@
#include <iostream>
#include <limits> // streamsize::max
#include "booking.cpp"
/* declaring X(option) generates:
- Ordered menu message entry
- Ordered Menu::State::option symbol
*/
#define MENU_ENTRIES \
X(RESERVE), \
X(CANCEL), \
X(CHECK), \
X(BREAK), \
X(FILTER_AVALIABLE), \
X(FILTER_RESERVED), \
X(FILTER_BROKEN), \
X(EXIT)
#include "menugen.cpp"
namespace Menu {
// generate enum
#define X(entry) entry
enum class State : StateType{
MENU = 0, // default state
MENU_ENTRIES,
COUNT // number of menu entries
};
#undef X
// generate menu message
constexpr auto generateMenuMessage() {
constexpr char menuMsg[] = "=== Ticket Booking System ===";
constexpr char dot[] = ". ";
constexpr char nl[] = "\n";
constexpr auto str = concat( nl, nl, menuMsg, nl,
#define X(entry) NumToString<static_cast<StateType>(State::entry)>::value, dot, #entry, nl
MENU_ENTRIES
#undef X
);
return str;
}
}
constexpr auto MENU_MSG = Menu::generateMenuMessage();
std::istream& operator>>(std::istream& is, Menu::State& state) {
if (!(is >> reinterpret_cast<Menu::StateType&>(state))) throw Booking::InvalidArgument();
return is;
}
std::ostream& operator<<(std::ostream& os, Booking::seat_range r) {
if (r.second == r.first) os << " seat " << r.first << " is ";
else os << " seats " << r.first << "-" << r.second << " are ";
return os;
}
std::pair<int, int> getRange(std::istream& is) {
auto isEnd = [](char c) { return c == '\n' || c == EOF; };
try {
std::pair<Booking::seat_num, Booking::seat_num> output;
if (!(is >> output.first) || output.first < 1 || output.first > Booking::SEAT_CAPACITY)
throw Booking::InvalidSeat();
output.second = output.first;
while (!std::isdigit(is.peek())) {
char c = is.get();
if (c == '-') break;
if (isEnd(c)) return output;
if (!std::iswspace(c)) throw Booking::InvalidSeat();
}
if (!(is >> output.second) || output.second < 1 || output.second > Booking::SEAT_CAPACITY)
throw Booking::InvalidSeat();
char c = is.peek();
if (isEnd(c)) {
is.get(); // clear newline or eof
if (output.first > output.second)
std::swap(output.first, output.second);
return output;
}
while (std::iswspace(c - is.get())) if (isEnd(c)) return output;
throw Booking::InvalidSeat(); // unexpected character
} catch(...) {
throw Booking::InvalidSeat();
}
}
int main(int ragc, char* argv[]) {
std::ostream& os = std::cout;
std::istream& is = std::cin;
Menu::State state = Menu::State::MENU;
Booking::Booker h;
Booking::seat_range range;
for (;;) try {
state = Menu::State::MENU;
os << Menu::BOLDWHITE << MENU_MSG << Menu::RESET << Menu::CURSOR; is >> state;
switch (state) {
case Menu::State::RESERVE:
os << "Seat(s) to be reserved:\n" << Menu::CURSOR;
range = getRange(is);
h.reserveSeat(range);
os << Menu::GREEN << range << "reserved\n" << Menu::RESET;
break;
case Menu::State::CANCEL:
os << "Seat(s) to be cancelled:\n" << Menu::CURSOR;
range = getRange(is);
h.cancelSeat(range);
os << Menu::GREEN << range << "cancelled\n" << Menu::RESET;
break;
case Menu::State::BREAK:
os << "Seat(s) to be broken:\n" << Menu::CURSOR;
range = getRange(is);
h.breakSeat(range, os, Menu::RED);
os << Menu::RESET << range << "broken";
break;
case Menu::State::CHECK:
os << "Seat(s) to be checked:\n" << Menu::CURSOR;
range = getRange(is);
h.print(range, os);
break;
case Menu::State::FILTER_BROKEN:
os << "Avaliable seats are: ";
h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::BROKEN; }, os);
break;
case Menu::State::FILTER_AVALIABLE:
os << "Avaliable seats are: ";
h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::AVALIBLE; }, os);
break;
case Menu::State::FILTER_RESERVED:
os << "Reserved seats are: ";
h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::RESERVED; }, os);
break;
case Menu::State::EXIT:
os << "Exiting...\n";
return EXIT_SUCCESS;
break;
default:
throw Booking::InvalidArgument();
}
} catch (Booking::BookingException& e) {
os << Menu::RED << e.what() << Menu::RESET << "\n";
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

138
CT3/menugen.cpp Normal file
View File

@ -0,0 +1,138 @@
#ifndef MENUGEN_0219820379823487123785
#define MENUGEN_0219820379823487123785
// This is collection of utility constexpr tools for generating menu dynamically in compile time
// Menu::generateMenuMessage basically creates this:
// constexpr const char* MENU_MSG = "\n\n=== Ticket Booking System ===\n1. Configure\n2. Reserve\n3. Cancel\n4. Check\n5. Exit\n";
// enum class State : int {
// MENU = 0,
// SETUP = 1,
// RESERVE = 2,
// CANCEL = 3,
// CHECK = 4,
// EXIT = 5,
// };
// Its kinda long & ugly because we need to generate both symbols in enum and use their values in the message
// Menu entries are defined with generic macro X() which is defined where menu is expanded and formats each entry there.
// This is trivial for pure enum creation, but we also need to count them, this is achived with Menu::State::COUNT beeing last element
// Next step is creating message. To be constexpr we cant use string, I experimented with string_view for few hours,
// but they are a lot less versatile than std::array, so Ive got first prototype working with them.
// But STL containers are forbidden in CT2... so i wrote my own ConstString :)
// With constexpr ready container, generating message is simple as
// 1. transforming number(enum member) to string
// 2. summing up all the submessage lengths
// 3. copying to single container
// I was suprised cpp20 doesnt include way to transform int to string in constexpr.
// So i achived this with recursive template ExpandDecimal - name is self explanatory.
// Calculating total length is just sum of the sizes of expanded template pack
// And copying with std::array would be just std::copy_n, but same can be achived on my structure with index_sequence from utility library
// thats all, hope you find it interesting :)
#include <utility> // index_sequence
namespace Menu{
constexpr auto RESET = "\033[0m";
constexpr auto RED = "\033[31m";
constexpr auto GREEN = "\033[32m";
constexpr auto YELLOW = "\033[33m";
constexpr auto BOLDWHITE = "\033[1m\033[37m";
constexpr auto CURSOR = "> ";
using StateType = size_t;
//###################################################################################
// helper char[] wrapper since it cannot be directly returned
template<size_t size>
struct ConstString { char data[size + 1];};
template<size_t size>
std::ostream& operator<<(std::ostream& os, const ConstString<size>& r) {
for (char c : r.data)
os << c;
return os;
}
//###################################################################################
// helpers to convert int (from enum) to string in ##################################
// shared array to store result
template<StateType... digits>
struct to_chars { static const char value[]; };
// convert single digit to char
template<StateType... digits>
// constexpr char to_chars<digits...>::value[] = {('0' + digits)...};
constexpr char to_chars<digits...>::value[] = {('0' + digits)..., 0}; // null termination
// when reminder is not zero - recursive expand for each decimal digit
template<StateType rem, StateType... digits>
struct ExpandDecimal : ExpandDecimal<rem / 10, rem % 10, digits...> {};
// when reminder is not zero - call to_char for last digit
template<StateType... digits>
struct ExpandDecimal<0, digits...> : to_chars<digits...> {};
// entry point - to by called directly
template<StateType num>
struct NumToString : ExpandDecimal<num> {};
//###################################################################################
// helper templates to calculate total length as constexpr ##########################
template<typename... Strings>
constexpr std::size_t total_length(Strings&&... strings) {
return (0 + ... + std::size(strings));
}
//###################################################################################
// helper templates to concatonate as constexpr #####################################
template<typename Str, std::size_t... I> // analog to copy_n from <algorithm>
static constexpr void copy_str(char* dest, std::size_t& idx,
Str str, std::index_sequence<I...>) {
((dest[idx++] = str[I]), ...);
}
template<typename... Strings>
constexpr auto concat(Strings&&... strings) {
constexpr std::size_t total_len = (0 + ... + (std::size(strings) - 1));
ConstString<total_len> result; std::size_t idx = 0;
((copy_str(result.data, idx, strings, // copy each string to result
std::make_index_sequence<std::size(strings) - 1>{}), ...));
result.data[total_len] = '\0';
return result;
}
//###################################################################################
// tests ############################################################################
static_assert(sizeof(NumToString<1>::value) == 2, "NumToString: size check");
static_assert(NumToString<1>::value[0] == '1', "NumToString: 1");
static_assert(NumToString<12345678900>::value[0] == '1' &&
NumToString<12345678900>::value[1] == '2' &&
NumToString<12345678900>::value[2] == '3' &&
NumToString<12345678900>::value[3] == '4' &&
NumToString<12345678900>::value[4] == '5' &&
NumToString<12345678900>::value[5] == '6' &&
NumToString<12345678900>::value[6] == '7' &&
NumToString<12345678900>::value[7] == '8' &&
NumToString<12345678900>::value[8] == '9' &&
NumToString<12345678900>::value[9] == '0' &&
NumToString<12345678900>::value[10] == '0' &&
NumToString<12345678900>::value[11] == '\0'&&
sizeof(NumToString<12345678900>::value) == 12, "NumToString: longnum");
static_assert(sizeof(concat("a", "bc", "def")) == sizeof("abcdef") , "concat: size check");
static_assert(concat("a", "bc", "def").data[0] == 'a' &&
concat("a", "bc", "def").data[1] == 'b' &&
concat("a", "bc", "def").data[2] == 'c' &&
concat("a", "bc", "def").data[3] == 'd' &&
concat("a", "bc", "def").data[4] == 'e' &&
concat("a", "bc", "def").data[5] == 'f' &&
concat("a", "bc", "def").data[6] == '\0', "concat: content");
}
#endif //MENUGEN_0219820379823487123785

11
CT4/Makefile Normal file
View File

@ -0,0 +1,11 @@
CXX = g++
CXXFLAGS = -std=c++20 -Wall -pedantic -g
LDFLAGS =
main: main.cpp booking.cpp menugen.cpp menu.cpp server.cpp module.cpp
$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS)
.PHONY clean:
rm -f main

166
CT4/booking.cpp Normal file
View File

@ -0,0 +1,166 @@
#pragma once
#include <cstdint>
#include <ostream>
#include <algorithm>
#include <functional>
#include <map>
#include <queue>
#include <stack>
#include <shared_mutex>
#include "menugen.cpp"
namespace Booking {
using seat_num = int64_t;
using seat_range = std::pair<seat_num, seat_num>;
using uiBool = bool;
class BookingException : public std::exception {
public: const char* what() const noexcept override { return "Internal exception!"; }
};
#define EXCEPTION(INTERNAL, EXTARNAL) \
class INTERNAL : public BookingException { \
const char* what() const noexcept override { return EXTARNAL; } \
};
EXCEPTION(AlreadyReserved, "Already reserved!")
EXCEPTION(NotReserved, "Not reserved!")
EXCEPTION(InvalidSeat, "Invalid seat number!");
EXCEPTION(InvalidArgument, "Invalid argument!")
struct Seat {
enum class Status {
AVALIBLE, RESERVED, BROKEN
};
enum class Type {
VIP, REGULAR, CHEAP
};
Status s = Status::AVALIBLE;
Type t;
std::string reservation_name;
Seat(Type t) : t(t) {}
};
class Booker {
private:
std::map< seat_num, Seat > seats;
std::map< Seat::Type, std::stack< seat_num > > avaliables =
{{Seat::Type::VIP, {}}, {Seat::Type::REGULAR, {}}, {Seat::Type::CHEAP, {}}};
std::map< Seat::Type, std::queue< std::string > > queues =
{{Seat::Type::VIP, {}}, {Seat::Type::REGULAR, {}}, {Seat::Type::CHEAP, {}}};
std::stack< std::pair< seat_num, std::string > > cancellations;
mutable std::shared_mutex m;
public:
Booker() {
for (seat_num i = 0; i < 100; ++i) {
seats.emplace(seats.size() + 1, Seat::Type::REGULAR);
avaliables[Seat::Type::REGULAR].emplace(seats.size());
}
for (seat_num i = 0; i < 1; ++i) {
seats.emplace(seats.size() + 1, Seat::Type::VIP);
avaliables[Seat::Type::VIP].emplace(seats.size());
}
for (seat_num i = 0; i < 2; ++i) {
seats.emplace(seats.size() + 1, Seat::Type::CHEAP);
avaliables[Seat::Type::CHEAP].emplace(seats.size());
}
}
void reserveSeat(Seat::Type t, const std::string& name, std::ostream& os) {
m.lock();
if (avaliables[t].size()) {
seat_num s = avaliables[t].top();
avaliables[t].pop();
seats.at(s).s = Seat::Status::RESERVED;
seats.at(s).reservation_name = name;
os << "Seat " << s << " it now reserved" << std::endl;
} else {
queues[t].push(name);
os << "All seats of this type occupied, youre " << queues[t].size() << " in queue" << std::endl;
}
m.unlock();
}
void cancelSeat(std::ostream& os, std::string name) {
m.lock();
bool c = false;
for (auto& i : seats) {
Seat& s = i.second;
if (s.reservation_name != name)
continue;
c = true;
cancellations.emplace(i.first, name);
// check for waiting
if (queues[s.t].empty()) {
s.s = Seat::Status::AVALIBLE;
s.reservation_name.clear();
avaliables[s.t].emplace(i.first);
os << Menu::GREEN << "Reservation at " << i.first << " cancelled, noboady is waiting\n" << Menu::RESET << std::flush;
} else {
auto c = queues[s.t].front();
queues[s.t].pop();
s.reservation_name = c;
os << Menu::GREEN << "Reservation at " << i.first << " cancelled" << Menu::RESET << ", " << c << " got the seat" << std::endl;
}
}
if (!c)
os << Menu::RED << "No reservation at this name found!\n" << Menu::RESET << std::flush;
m.unlock();
}
void print(std::ostream& os) const {
m.lock_shared();
for (const auto& i : seats) {
const Seat& s = i.second;
seat_num n = i.first;
std::string type;
switch (s.t) {
case Seat::Type::VIP: type = "VIP"; break;
case Seat::Type::REGULAR: type = "Regular"; break;
case Seat::Type::CHEAP: type = "Cheap"; break;
}
switch (s.s) {
case Seat::Status::AVALIBLE: os << Menu::GREEN << type << " seat " << n << " is avaliable\n" << Menu::RESET << std::flush; break;
case Seat::Status::RESERVED: os << Menu::YELLOW << type << " seat " << n << " is reserved" << Menu::RESET << " by \"" << s.reservation_name << "\"\n" << std::flush; break;
case Seat::Status::BROKEN: os << Menu::RED << type << " seat " << n << " is broken\n" << Menu::RESET << std::flush; break;
default: break;
}
}
std::stack tmp = cancellations;
os << "past cancellations:" << std::endl;
while (tmp.size()) {
auto i = tmp.top();
tmp.pop();
os << "seat " << i.first << " by " << i.second << std::endl;
}
m.unlock_shared();
}
std::pair<size_t, size_t> calculateSeatStatistics() const;
std::vector<std::string> getSortedClients() const;
std::pair<seat_num, std::string> getHighestSeatReservation() const;
void displaySeatsReversed(std::ostream& os) const;
size_t countBookingsByClient(const std::string& name) const;
size_t replaceClientName(const std::string& oldName, const std::string& newName);
std::vector<std::string> getVipClients() const;
};
}
std::ostream& operator<<(std::ostream& os, const Booking::Booker& b) {
b.print(os);
return os;
}

10
CT4/main.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "menu.cpp"
#include "server.cpp"
int main(int ragc, char* argv[]) {
void* ctx = menuInit();
serve(menu, ctx);
menuCleanup(ctx);
}

235
CT4/menu.cpp Normal file
View File

@ -0,0 +1,235 @@
#include <iostream>
#include <limits> // streamsize::max
#include <memory>
#include "booking.cpp"
/* declaring X(option) generates:
- Ordered menu message entry
- Ordered Menu::State::option symbol
*/
#define MENU_ENTRIES \
X(RESERVE), \
X(CANCEL), \
X(CHECK), \
X(ACCUMULATE), \
X(SORT), \
X(MAX_ELEMENT), \
X(REVERSE), \
X(COUNT), \
X(REPLACE), \
X(COPY), \
X(EXIT), \
X(SHUTDOWN)
#include "menugen.cpp"
#include "module.cpp"
namespace Menu {
// generate enum
#define X(entry) entry
enum class State : StateType{
MENU = 0, // default state
MENU_ENTRIES,
COUNT_TAG // number of menu entries
};
#undef X
// generate menu message
constexpr auto generateMenuMessage() {
constexpr char menuMsg[] = "=== Ticket Booking System ===";
constexpr char dot[] = ". ";
constexpr char nl[] = "\n";
constexpr auto str = concat( nl, nl, menuMsg, nl,
#define X(entry) NumToString<static_cast<StateType>(State::entry)>::value, dot, #entry, nl
MENU_ENTRIES
#undef X
);
return str;
}
}
constexpr auto MENU_MSG = Menu::generateMenuMessage();
std::istream& operator>>(std::istream& is, Menu::State& state) {
if (!(is >> reinterpret_cast<Menu::StateType&>(state))) throw Booking::InvalidArgument();
return is;
}
std::ostream& operator<<(std::ostream& os, Booking::seat_range r) {
if (r.second == r.first) os << " seat " << r.first << " is " << std::flush;
else os << " seats " << r.first << "-" << r.second << " are " << std::flush;
return os;
}
Booking::Seat::Type getType(std::istream& is, std::ostream& os) {
uint64_t n;
os << "Enter seat type: 0 - VIP, 1 - REGULAR, 2 - CHEAP\n" << std::flush;
if (!(is >> n) || (n != 0 && n != 1 && n != 2)) {
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
throw Booking::InvalidArgument();
}
return static_cast<Booking::Seat::Type>(n);
}
std::string getName(std::istream& is, std::ostream& os) {
std::string name;
os << "Enter name: " << std::flush;
is >> std::ws;
if (!std::getline(is, name) || name.empty()) {
is.clear();
throw Booking::InvalidArgument();
}
return name;
}
std::pair<int, int> getRange(std::istream& is) {
auto isEnd = [](char c) { return c == '\n' || c == EOF; };
try {
std::pair<Booking::seat_num, Booking::seat_num> output;
// if (!(is >> output.first) || output.first < 1 || output.first > Booking::SEAT_CAPACITY)
// throw Booking::InvalidSeat();
output.second = output.first;
while (!std::isdigit(is.peek())) {
char c = is.get();
if (c == '-') break;
if (isEnd(c)) return output;
if (!std::iswspace(c)) throw Booking::InvalidSeat();
}
// if (!(is >> output.second) || output.second < 1 || output.second > Booking::SEAT_CAPACITY)
// throw Booking::InvalidSeat();
char c = is.peek();
if (isEnd(c)) {
is.get(); // clear newline or eof
if (output.first > output.second)
std::swap(output.first, output.second);
return output;
}
while (std::iswspace(c - is.get())) if (isEnd(c)) return output;
throw Booking::InvalidSeat(); // unexpected character
} catch(...) {
throw Booking::InvalidSeat();
}
}
void* menuInit() {
return new Booking::Booker;
// return nullptr;
}
void menuCleanup(void* ctx) {
Booking::Booker* h = reinterpret_cast<Booking::Booker*>(ctx);
delete h;
}
bool menu(std::istream& is, std::ostream& os, void* ctx) {
Menu::State state = Menu::State::MENU;
Booking::Booker& h = *reinterpret_cast<Booking::Booker*>(ctx);
Booking::Seat::Type t;
std::string name, name_rep;
//----------------
std::pair<size_t, size_t> stats;
std::vector<std::string> sorted;
std::pair<Booking::seat_num, std::string> cli;
size_t cnt;
std::vector<std::string> vips;
//----------------
Booking::seat_range range;
for (;;) try {
state = Menu::State::MENU;
os << Menu::BOLDWHITE << MENU_MSG << Menu::RESET << Menu::CURSOR << std::flush; is >> state;
switch (state) {
case Menu::State::RESERVE:
os << "Seat(s) to be reserved:\n" << Menu::CURSOR << std::flush;
t = getType(is, os);
name = getName(is, os);
h.reserveSeat(t, name, os);
break;
case Menu::State::CANCEL:
os << "Seat(s) to be cancelled:\n" << Menu::CURSOR << std::flush;
name = getName(is, os);
h.cancelSeat(os, name);
break;
case Menu::State::CHECK:
h.print(os);
break;
case Menu::State::EXIT:
os << "Client exiting..." << std::endl;
return EXIT_SUCCESS;
break;
case Menu::State::SHUTDOWN:
os << "Sending shutdown signal..." << std::endl;
return 1;
// ------------- NEW ----------------------------------------------
case Menu::State::ACCUMULATE:
stats = h.calculateSeatStatistics();
os << "Avaliable: " << stats.first << std::endl;
os << "Reserved: " << stats.second << std::endl;
break;
case Menu::State::SORT:
sorted = h.getSortedClients();
os << "Clients:\n";
for (const auto& i : sorted)
os << i << "\n";
os << std::flush;
break;
case Menu::State::MAX_ELEMENT:
cli = h.getHighestSeatReservation();
os << "Seat " << cli.first << " by " << cli.second << std::endl;
break;
case Menu::State::REVERSE:
h.displaySeatsReversed(os);
break;
case Menu::State::COUNT:
name = getName(is, os);
cnt = h.countBookingsByClient(name);
os << Menu::GREEN << "Count: " << cnt << Menu::RESET << std::endl;
break;
case Menu::State::REPLACE:
name = getName(is, os);
os << "Replace with" << std::endl;
name_rep = getName(is, os);
os << Menu::GREEN << h.replaceClientName(name, name_rep) << " seats replaced" << Menu::RESET << std::endl;
break;
case Menu::State::COPY:
vips = h.getVipClients();
os << "VIP clients:\n";
for (const auto& i : vips)
os << Menu::GREEN << i << "\n";
os << Menu::RESET << std::flush;
break;
// ----------------------------------------------------------------
default:
throw Booking::InvalidArgument();
}
} catch (Booking::BookingException& e) {
os << Menu::RED << e.what() << Menu::RESET << std::endl;
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

143
CT4/menugen.cpp Normal file
View File

@ -0,0 +1,143 @@
#ifndef MENUGEN_0219820379823487123785
#define MENUGEN_0219820379823487123785
#if __cplusplus < 202002L
#error This code requires C++20
#endif
// This is collection of utility constexpr tools for generating menu dynamically in compile time
// Menu::generateMenuMessage basically creates this:
// constexpr const char* MENU_MSG = "\n\n=== Ticket Booking System ===\n1. Configure\n2. Reserve\n3. Cancel\n4. Check\n5. Exit\n";
// enum class State : int {
// MENU = 0,
// SETUP = 1,
// RESERVE = 2,
// CANCEL = 3,
// CHECK = 4,
// EXIT = 5,
// };
// Its kinda long & ugly because we need to generate both symbols in enum and use their values in the message
// Menu entries are defined with generic macro X() which is defined where menu is expanded and formats each entry there.
// This is trivial for pure enum creation, but we also need to count them, this is achived with Menu::State::COUNT beeing last element
// Next step is creating message. To be constexpr we cant use string, I experimented with string_view for few hours,
// but they are a lot less versatile than std::array, so Ive got first prototype working with them.
// But STL containers are forbidden in CT2... so i wrote my own ConstString :)
// With constexpr ready container, generating message is simple as
// 1. transforming number(enum member) to string
// 2. summing up all the submessage lengths
// 3. copying to single container
// I was suprised cpp20 doesnt include way to transform int to string in constexpr.
// So i achived this with recursive template ExpandDecimal - name is self explanatory.
// Calculating total length is just sum of the sizes of expanded template pack
// And copying with std::array would be just std::copy_n, but same can be achived on my structure with index_sequence from utility library
// thats all, hope you find it interesting :)
#include <utility> // index_sequence
namespace Menu{
constexpr auto RESET = "\033[0m";
constexpr auto RED = "\033[31m";
constexpr auto GREEN = "\033[32m";
constexpr auto YELLOW = "\033[33m";
constexpr auto BOLDWHITE = "\033[1m\033[37m";
constexpr auto CURSOR = "> ";
using StateType = size_t;
//###################################################################################
// helper char[] wrapper since it cannot be directly returned
template<size_t size>
struct ConstString { char data[size + 1];};
template<size_t size>
std::ostream& operator<<(std::ostream& os, const ConstString<size>& r) {
for (char c : r.data)
os << c;
return os;
}
//###################################################################################
// helpers to convert int (from enum) to string in ##################################
// shared array to store result
template<StateType... digits>
struct to_chars { static const char value[]; };
// convert single digit to char
template<StateType... digits>
// constexpr char to_chars<digits...>::value[] = {('0' + digits)...};
constexpr char to_chars<digits...>::value[] = {('0' + digits)..., 0}; // null termination
// when reminder is not zero - recursive expand for each decimal digit
template<StateType rem, StateType... digits>
struct ExpandDecimal : ExpandDecimal<rem / 10, rem % 10, digits...> {};
// when reminder is not zero - call to_char for last digit
template<StateType... digits>
struct ExpandDecimal<0, digits...> : to_chars<digits...> {};
// entry point - to by called directly
template<StateType num>
struct NumToString : ExpandDecimal<num> {};
//###################################################################################
// helper templates to calculate total length as constexpr ##########################
template<typename... Strings>
constexpr std::size_t total_length(Strings&&... strings) {
return (0 + ... + std::size(strings));
}
//###################################################################################
// helper templates to concatonate as constexpr #####################################
template<typename Str, std::size_t... I> // analog to copy_n from <algorithm>
static constexpr void copy_str(char* dest, std::size_t& idx,
Str str, std::index_sequence<I...>) {
((dest[idx++] = str[I]), ...);
}
template<typename... Strings>
constexpr auto concat(Strings&&... strings) {
constexpr std::size_t total_len = (0 + ... + (std::size(strings) - 1));
ConstString<total_len> result; std::size_t idx = 0;
((copy_str(result.data, idx, strings, // copy each string to result
std::make_index_sequence<std::size(strings) - 1>{}), ...));
result.data[total_len] = '\0';
return result;
}
//###################################################################################
// tests ############################################################################
static_assert(sizeof(NumToString<1>::value) == 2, "NumToString: size check");
static_assert(NumToString<1>::value[0] == '1', "NumToString: 1");
static_assert(NumToString<12345678900>::value[0] == '1' &&
NumToString<12345678900>::value[1] == '2' &&
NumToString<12345678900>::value[2] == '3' &&
NumToString<12345678900>::value[3] == '4' &&
NumToString<12345678900>::value[4] == '5' &&
NumToString<12345678900>::value[5] == '6' &&
NumToString<12345678900>::value[6] == '7' &&
NumToString<12345678900>::value[7] == '8' &&
NumToString<12345678900>::value[8] == '9' &&
NumToString<12345678900>::value[9] == '0' &&
NumToString<12345678900>::value[10] == '0' &&
NumToString<12345678900>::value[11] == '\0'&&
sizeof(NumToString<12345678900>::value) == 12, "NumToString: longnum");
static_assert(sizeof(concat("a", "bc", "def")) == sizeof("abcdef") , "concat: size check");
static_assert(concat("a", "bc", "def").data[0] == 'a' &&
concat("a", "bc", "def").data[1] == 'b' &&
concat("a", "bc", "def").data[2] == 'c' &&
concat("a", "bc", "def").data[3] == 'd' &&
concat("a", "bc", "def").data[4] == 'e' &&
concat("a", "bc", "def").data[5] == 'f' &&
concat("a", "bc", "def").data[6] == '\0', "concat: content");
}
#endif //MENUGEN_0219820379823487123785

114
CT4/module.cpp Normal file
View File

@ -0,0 +1,114 @@
// #include "booking.cpp"
#include <algorithm>
#include <numeric>
namespace Booking {
// 1. Calculate total free/booked seats - std::accumulate
std::pair<size_t, size_t> Booker::calculateSeatStatistics() const {
m.lock_shared();
auto ret = std::accumulate(seats.begin(), seats.end(), std::make_pair<size_t, size_t>(0, 0),
[](std::pair<size_t, size_t> acc, const std::pair<seat_num, Seat>& p) {
if (p.second.s == Seat::Status::AVALIBLE) ++acc.first;
if (p.second.s == Seat::Status::RESERVED) ++acc.second;
return acc;
}
);
m.unlock_shared();
return ret;
}
// 2. Sort clients by name using std::sort
std::vector<std::string> Booker::getSortedClients() const {
m.lock_shared();
std::vector<std::string> clients;
for (const auto& kv : seats) {
if (kv.second.s == Seat::Status::RESERVED)
clients.push_back(kv.second.reservation_name);
}
m.unlock_shared();
std::sort(clients.begin(), clients.end());
return clients;
}
// 3. Find the client with the highest seat number using std::max_element
std::pair<seat_num, std::string> Booker::getHighestSeatReservation() const {
m.lock_shared();
auto it = std::max_element(seats.begin(), seats.end(),
[](auto& a, auto& b) {
bool ra = (a.second.s == Seat::Status::RESERVED);
bool rb = (b.second.s == Seat::Status::RESERVED);
if (ra != rb) return !ra;
return a.first < b.first;
}
);
auto end = seats.end();
m.unlock_shared();
if (it == end || it->second.s != Seat::Status::RESERVED)
throw NotReserved();
return { it->first, it->second.reservation_name };
}
// 4. Reverse display of seats using std::reverse and iteration
void Booker::displaySeatsReversed(std::ostream& os) const {
m.lock_shared();
std::vector<std::pair<seat_num, Seat>> vec(seats.begin(), seats.end());
m.unlock_shared();
std::reverse(vec.begin(), vec.end());
for (const auto& kv : vec) {
os << "Seat " << kv.first << ": ";
switch (kv.second.s) {
case Seat::Status::AVALIBLE: os << Menu::GREEN << "Available"; break;
case Seat::Status::RESERVED: os << Menu::YELLOW << "Reserved by " << kv.second.reservation_name; break;
case Seat::Status::BROKEN: os << "Broken"; break;
}
os << Menu::RESET << std::endl;
}
}
// 5. Count bookings by a specific client using std::count_if
size_t Booker::countBookingsByClient(const std::string& name) const {
m.lock_shared();
auto ret = std::count_if(seats.begin(), seats.end(),
[&](auto& kv){ return kv.second.s == Seat::Status::RESERVED && kv.second.reservation_name == name; }
);
m.unlock_shared();
return ret;
}
// 6. Replace a client name across all bookings using std::replace_if
size_t Booker::replaceClientName(const std::string& oldName, const std::string& newName) {
m.lock();
size_t cnt = 0;
std::for_each(seats.begin(), seats.end(),
[&](auto& kv) {
if (kv.second.s == Seat::Status::RESERVED && kv.second.reservation_name == oldName) {
kv.second.reservation_name = newName;
++cnt;
}
}
);
m.unlock();
return cnt;
}
// 7. Copy all VIP clients to a separate list using std::copy_if
std::vector<std::string> Booker::getVipClients() const {
m.lock_shared();
std::vector<std::pair<const seat_num, Booking::Seat>> vip;
std::vector<std::string> ret;
std::copy_if(seats.begin(), seats.end(), std::back_inserter(vip),
[](const auto& kv){ return kv.second.s == Seat::Status::RESERVED && kv.second.t == Seat::Type::VIP; }
);
m.unlock_shared();
// extract names
for (const auto& i : vip)
ret.push_back(i.second.reservation_name);
return ret;
}
}

67
CT4/server.cpp Normal file
View File

@ -0,0 +1,67 @@
#include <sys/socket.h> // socket functions
#include <netinet/in.h> // sockaddr_in
#include <cstdlib> // exit() and EXIT_FAILURE
#include <iostream> // cout
#include <unistd.h> // read
#include <signal.h>
#include <ext/stdio_filebuf.h> // buffered stream
#include <thread>
using SOCKET_FD = int;
constexpr uint16_t LISTEN_PORT = 42069;
const int yes = 1;
SOCKET_FD listener = -1, connection = -1;
void handle_client(SOCKET_FD client_fd, bool (*handler)(std::istream&, std::ostream&, void*), void* ctx) {
using Filebuf = __gnu_cxx::stdio_filebuf<char>;
// setup input stream
FILE* fin = fdopen(client_fd, "r");
Filebuf in_buf(fin, std::ios::in);
std::istream is(&in_buf);
// setup output stream
FILE* fout = fdopen(client_fd, "w");
Filebuf out_buf(fout, std::ios::out);
std::ostream os(&out_buf);
signal(SIGPIPE, SIG_IGN); // ignore pipe signal
if (handler(is, os, ctx)) {
shutdown(listener, SHUT_RD);
}
fclose(fin);
fclose(fout);
close(client_fd);
}
void serve(bool (*handler)(std::istream&, std::ostream&, void*), void* ctx) {
sockaddr_in addr{ .sin_family = AF_INET, .sin_port = htons(LISTEN_PORT), .sin_addr = in_addr{ .s_addr = INADDR_ANY } };
if ((listener = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0 ||
bind(listener, (const sockaddr*) &addr, sizeof(addr)) < 0 ||
listen(listener, 10) < 0) {
goto cleanup;
}
while ((connection = accept(listener, nullptr, nullptr)) > 0) {
std::thread(handle_client, connection, handler, ctx).detach();
}
cleanup:
std::cout << "Server shutting down..." << std::endl;
if (connection != -1) close(connection);
if (listener != -1) close(listener);
}
bool h(std::istream& is, std::ostream& os, void* ctx) {
std::string s;
os << "hello from " << std::this_thread::get_id() <<"! are you there?" << std::endl;
is >> s;
os << s << " to you too!" << std::endl;
return true;
}