I know it’s tagged c, but since you spammed it in Lounge<C++>
, let me humor you with C++:
#include <algorithm>
#include <functional>
#include <atomic>
#include <fstream>
#include <iostream>
#include <list>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
// not thread-aware:
struct Bank {
struct Account {
int id;
int password;
int balance;
Account(int id, int password = 0, int balance = 0)
: id(id), password(password), balance(balance) { }
bool operator==(Account const& other) const { return id == other.id; }
};
void remove(int id) {
auto& acc = get_unverified(id);
accounts.remove(acc);
}
void add(Account const& acc) {
if (std::count(accounts.begin(), accounts.end(), acc.id))
throw std::runtime_error("account with the same id exists"); // TODO include id?
accounts.push_back(acc);
}
Account& get_unverified(int id) {
auto it = std::find(accounts.begin(), accounts.end(), id);
if (it != accounts.end())
return *it;
throw std::runtime_error("Account " + std::to_string(id) + " doesn't exist");
}
Account& get(int id, int password) {
auto& acc = get_unverified(id);
if (acc.password != password)
throw std::runtime_error("Password for id <" + std::to_string(id) + "> is incorrect");
return acc;
}
void status() const {
std::cout << "*******************************\n";
std::cout << "Bank status: \n";
int totalAmount = 0;
for (auto const &acc : accounts) {
std::cout << "Account: " << acc.id << ", Account password: " << acc.password << ", Balance: " << acc.balance
<< "$\n";
totalAmount += acc.balance;
}
// Print and Reset bank's balance to zero
std::cout << "Bank's balance: " << totalAmount << "\n";
std::cout << "*******************************\n";
}
private:
std::list<Account> accounts;
};
// something to make access guarded:
template <typename T>
struct Locking {
template <typename Op>
void transactional(Op&& op) {
std::lock_guard<std::mutex> lk(_mx);
std::forward<Op>(op)(_value);
}
private:
std::mutex _mx;
T _value;
};
static void bankLoop(Locking<Bank>& safe, std::atomic_bool& keepRunning) {
while (keepRunning) {
std::this_thread::sleep_for(std::chrono::seconds(1));
safe.transactional(std::mem_fn(&Bank::status));
}
}
struct Log {
Log() : ofs("log.txt") { } // TODO fix mode/fail on existing?
void Write(std::string const &msg) {
std::lock_guard<std::mutex> lk(_mx);
ofs << msg << "\n";
}
private:
std::mutex _mx;
std::ofstream ofs;
} logDescriptor;
struct Atm {
Locking<Bank>& safe;
int const fileNum;
void process(std::string const& fileName) {
std::ifstream file(fileName);
std::string line;
while (std::getline(file, line)) {
// totally made up this feature:
if (line.empty()) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
continue;
}
switch (line[0]) {
case 'O': openNewAccount(line); break;
case 'D': depositAccount(line); break;
case 'W': Withdrawl(line); break;
case 'B': Balance(line); break;
case 'Q': CloseAccount(line); break;
case 'T': TransferAccount(line); break;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
file.close();
}
private:
void TransferAccount(std::string const& msg) {
std::cout << "Transfering to account... " << msg << "\n";
safe.transactional([=](Bank& bank) {
std::ostringstream msgLog;
try {
auto const cmd = parseCommand(msg);
auto& source = bank.get(cmd.id, cmd.pw);
auto& target = bank.get_unverified(cmd.id2);
source.balance -= cmd.amount;
target.balance += cmd.amount;
msgLog
<< "<ATM ID: " << fileNum << ">: "
<< "Transfer <" << cmd.amount << "> "
<< "from account <" << cmd.id << "> "
<< "to account <" << cmd.id2 << "> "
<< "new balance is <" << source.balance << "> "
<< "new target account balance is <" << target.balance << ">";
} catch(std::exception const& e) {
msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed - " << e.what();
}
logDescriptor.Write(msgLog.str());
});
}
void CloseAccount(std::string const& msg) {
std::cout << "Closing account... " << msg << "\n";
safe.transactional([=](Bank& bank) {
std::ostringstream msgLog;
try {
auto const cmd = parseCommand(msg);
auto& acc = bank.get(cmd.id, cmd.pw);
auto const balance = acc.balance;
bank.remove(acc.id);
msgLog
<< "<ATM ID: " << fileNum << ">: "
<< "Account <" << cmd.id << "> is now closed. "
<< "Balance was <" << balance << ">";
} catch(std::exception const& e) {
msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed - " << e.what();
}
logDescriptor.Write(msgLog.str());
});
}
void depositAccount(std::string const& msg) {
std::cout << "Depositing to account.. " << msg << "\n";
safe.transactional([=](Bank& bank) {
std::ostringstream msgLog;
try {
auto const cmd = parseCommand(msg);
auto& acc = bank.get(cmd.id, cmd.pw);
acc.balance += cmd.amount;
msgLog
<< "<ATM ID: " << fileNum << ">: "
<< "Account <" << cmd.id << "> "
<< "new balance is <" << acc.balance << "> "
<< "after <" << cmd.amount << "> was deposited";
} catch(std::exception const& e) {
msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed - " << e.what();
}
logDescriptor.Write(msgLog.str());
});
}
void Withdrawl(std::string const& msg) {
std::cout << "Withdrawl from account.. " << msg << "\n";
safe.transactional([=](Bank& bank) {
std::ostringstream msgLog;
try {
auto const cmd = parseCommand(msg);
auto& acc = bank.get(cmd.id, cmd.pw);
acc.balance -= cmd.amount;
msgLog
<< "<ATM ID: " << fileNum << ">: "
<< "Account <" << cmd.id << "> "
<< "new balance is <" << acc.balance << "> "
<< "after <" << cmd.amount << "> was Withdrawl";
} catch(std::exception const& e) {
msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed - " << e.what();
}
logDescriptor.Write(msgLog.str());
});
}
void Balance(std::string const& msg) {
std::cout << "Balance from account.. " << msg << "\n";
safe.transactional([=](Bank& bank) {
std::ostringstream msgLog;
try {
auto const cmd = parseCommand(msg);
auto& acc = bank.get(cmd.id, cmd.pw);
msgLog
<< "<ATM ID: " << fileNum << ">: "
<< "Account <" << cmd.id << "> "
<< "new balance is <" << acc.balance << ">";
} catch(std::exception const& e) {
msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed - " << e.what();
}
logDescriptor.Write(msgLog.str());
});
}
void openNewAccount(std::string const& msg) {
std::cout << "Opening account... " << msg << "\n";
safe.transactional([=](Bank& bank) {
std::ostringstream msgLog;
try {
auto const cmd = parseCommand(msg);
Bank::Account const acc(cmd.id, cmd.pw, cmd.amount);
bank.add(acc);
msgLog
<< "<ATM ID: " << fileNum << ">: "
<< "New account id is <" << acc.id << "> with passoword <" << acc.password << "> and initial balance <" << acc.balance << ">,"; // FIXME trailing comma
} catch(std::exception const& e) {
msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed - " << e.what();
}
logDescriptor.Write(msgLog.str());
});
}
private:
struct Cmd { int id=-1, pw=-1, amount=0, id2=-1; };
static Cmd parseCommand(std::string const& msg) {
Cmd result;
char discard; // absorbs command character
std::istringstream iss(msg);
if (!(iss >> discard >> result.id >> result.pw))
throw std::runtime_error("the command message is invalid");
iss >> result.amount >> result.id2;
return result;
}
};
int main() {
std::cout << "Please enter the number of ATMs you want: ";
int n = 0;
if (!(std::cin >> n))
throw std::runtime_error("Input failed");
// Create bank thread
Locking<Bank> bank;
std::atomic_bool keepRunning{true};
std::thread bankThread(&bankLoop, std::ref(bank), std::ref(keepRunning));
std::list<std::thread> atmThreads;
for (int i = 0; i < n; i++) {
atmThreads.emplace_back([&bank, i] {
Atm atm { bank, i };
atm.process("ATM_" + std::to_string(i) + "_input_file.txt");
});
}
// Join ATM threads
for (auto &atm : atmThreads)
atm.join();
// Join bank thread
keepRunning = false;
bankThread.join();
}
Let’s test it with 1 ATM file
O 123 8888 10
O 123 8888 10
W 123 8888 5
B 123 8888
B 123 7777
O 234 9999 20
D 234 9999 50
B 234 9999
W 123 8888 15
T 234 9999 30 123
Q 234 9999
Q 234 9999
B 123 what
It prints
Please enter the number of ATMs you want: 1
Opening account... O 123 8888 10
Opening account... O 123 8888 10
Withdrawl from account.. W 123 8888 5
Balance from account.. B 123 8888
Balance from account.. B 123 7777
Opening account... O 234 9999 20
Depositing to account.. D 234 9999 50
Balance from account.. B 234 9999
Withdrawl from account.. W 123 8888 15
Transfering to account... T 234 9999 30 123
Closing account... Q 234 9999
Closing account... Q 234 9999
Balance from account.. B 123 what
*******************************
Bank status:
Account: 123, Account password: 8888, Balance: 20$
Bank's balance: 20
*******************************
And log.txt
ends up like:
<ATM ID: 0>: New account id is <123> with passoword <8888> and initial balance <10>,
Error <ATM ID: 0>: Your transaction failed - account with the same id exists
<ATM ID: 0>: Account <123> new balance is <5> after <5> was Withdrawl
<ATM ID: 0>: Account <123> new balance is <5>
Error <ATM ID: 0>: Your transaction failed - Password for id <123> is incorrect
<ATM ID: 0>: New account id is <234> with passoword <9999> and initial balance <20>,
<ATM ID: 0>: Account <234> new balance is <70> after <50> was deposited
<ATM ID: 0>: Account <234> new balance is <70>
<ATM ID: 0>: Account <123> new balance is <-10> after <15> was Withdrawl
<ATM ID: 0>: Transfer <30> from account <234> to account <123> new balance is <40> new target account balance is <20>
<ATM ID: 0>: Account <234> is now closed. Balance was <40>
Error <ATM ID: 0>: Your transaction failed - Account 234 doesn't exist
Error <ATM ID: 0>: Your transaction failed - the command message is invalid
1
solved bank, 4 atm machine input txt files, syncing information between them with semaphores