rest of tasks
This commit is contained in:
parent
75ad36cb37
commit
bc71d512a0
|
@ -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
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#include "menu.cpp"
|
||||
#include "server.cpp"
|
||||
|
||||
int main(int ragc, char* argv[]) {
|
||||
void* ctx = menuInit();
|
||||
|
||||
serve(menu, ctx);
|
||||
|
||||
menuCleanup(ctx);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue