Update CMake configuration and refactor code to use smart pointers for memory management

This commit is contained in:
Torsten Schulz
2025-08-11 11:15:54 +02:00
parent ba6b788075
commit f44d780537
10 changed files with 110 additions and 77 deletions

7
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": []
}

28
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,28 @@
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: gcc-13 build active file",
"command": "/usr/bin/gcc-13",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
}
],
"version": "2.0.0"
}

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
project(YourChat VERSION 0.1) project(YourChat VERSION 0.1)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_STANDARD_REQUIRED True)
include_directories(${PROJECT_SOURCE_DIR}) include_directories(${PROJECT_SOURCE_DIR})
add_executable(yourchat main.cpp base.cpp config.cpp server.cpp room.cpp tools.cpp user.cpp) add_executable(yourchat main.cpp base.cpp config.cpp server.cpp room.cpp tools.cpp user.cpp)

View File

@@ -3,8 +3,8 @@
// main function // main function
int main(int, char **) { int main(int, char **) {
Yc::Lib::Config *config = new Yc::Lib::Config(); auto config = std::make_shared<Yc::Lib::Config>();
Yc::Lib::Server server(config); auto server = std::make_shared<Yc::Lib::Server>(config);
server.run(); server->run();
return 0; return 0;
} }

View File

@@ -12,11 +12,11 @@
namespace Yc { namespace Yc {
namespace Lib { namespace Lib {
Room::Room(Server *parent, Json::Value roomParams) : Room::Room(std::shared_ptr<Server> parent, Json::Value roomParams) :
_parent(parent), _parent(std::move(parent)),
_blocked(false), _blocked(false),
_stop(false), _stop(false),
_roundRunning(false) { _roundRunning(false) {
_name = roomParams["name"].asString(); _name = roomParams["name"].asString();
_password = roomParams["password"].asString(); _password = roomParams["password"].asString();
std::vector<std::string> allowedUsers; std::vector<std::string> allowedUsers;
@@ -26,12 +26,10 @@ namespace Yc {
_type = (RoomType)roomParams["type"].asInt(); _type = (RoomType)roomParams["type"].asInt();
_roundLength = roomParams["roundlength"].asInt(); _roundLength = roomParams["roundlength"].asInt();
_lastRoundEnd = std::time(NULL); _lastRoundEnd = std::time(NULL);
thread = new std::thread(&Room::run, this); thread = std::make_unique<std::thread>(&Room::run, this);
} }
Room::~Room() { Room::~Room() = default;
delete thread;
}
void Room::run() { void Room::run() {
while (!_stop) { while (!_stop) {
@@ -57,7 +55,7 @@ namespace Yc {
if (_password != "" && _password == _password && std::find(std::begin(_allowedUsers), std::end(_allowedUsers), _userName) == std::end(_allowedUsers)) { if (_password != "" && _password == _password && std::find(std::begin(_allowedUsers), std::end(_allowedUsers), _userName) == std::end(_allowedUsers)) {
return false; return false;
} }
User *newUser = new User(this, _userName, color, socket); auto newUser = std::make_shared<User>(shared_from_this(), _userName, color, socket);
_users.push_back(newUser); _users.push_back(newUser);
newUser->sendMsg(User::roomList, _parent->jsonRoomList(), "", ""); newUser->sendMsg(User::roomList, _parent->jsonRoomList(), "", "");
addMessage(User::system, "room_entered", newUser->name(), newUser->color()); addMessage(User::system, "room_entered", newUser->name(), newUser->color());
@@ -65,10 +63,10 @@ namespace Yc {
return true; return true;
} }
bool Room::addUser(User *user, std::string password) { bool Room::addUser(std::shared_ptr<User> user, std::string password) {
if (password == _password) { if (password == _password) {
_users.push_back(user); _users.push_back(user);
user->setParent(this); user->setParent(shared_from_this());
_initRound(); _initRound();
return true; return true;
} }
@@ -76,7 +74,7 @@ namespace Yc {
} }
bool Room::userNameExists(std::string userName) { bool Room::userNameExists(std::string userName) {
for (auto &user: _users) { for (const auto &user: _users) {
if (user->name() == userName) { if (user->name() == userName) {
return true; return true;
} }
@@ -85,24 +83,24 @@ namespace Yc {
} }
void Room::removeUser(std::string _token, bool silent) { void Room::removeUser(std::string _token, bool silent) {
for (std::vector<User*>::iterator user = _users.begin(); user != _users.end(); ++user) { for (auto it = _users.begin(); it != _users.end(); ++it) {
if ((*user)->validateToken(_token)) { if ((*it)->validateToken(_token)) {
_users.erase(user);
if (!silent) { if (!silent) {
addMessage(User::system, "room_exit", (*user)->name(), (*user)->color()); addMessage(User::system, "room_exit", (*it)->name(), (*it)->color());
} }
_users.erase(it);
break; break;
} }
} }
} }
void Room::removeUser(User *userToRemove, bool silent) { void Room::removeUser(std::shared_ptr<User> userToRemove, bool silent) {
for (std::vector<User*>::iterator user = _users.begin(); user != _users.end(); ++user) { for (auto it = _users.begin(); it != _users.end(); ++it) {
if (*user == userToRemove) { if (*it == userToRemove) {
_users.erase(user);
if (!silent) { if (!silent) {
addMessage(User::system, "room_exit", (*user)->name(), (*user)->color()); addMessage(User::system, "room_exit", (*it)->name(), (*it)->color());
} }
_users.erase(it);
break; break;
} }
} }
@@ -129,15 +127,15 @@ namespace Yc {
addMessage(type, getJsonString(messageText), userName, color); addMessage(type, getJsonString(messageText), userName, color);
} }
void Room::addUserWhenQueueEmpty(User *user) { void Room::addUserWhenQueueEmpty(std::shared_ptr<User> user) {
while (_msgQueue.size() > 0) { while (_msgQueue.size() > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
_users.push_back(user); _users.push_back(user);
user->setParent(this); user->setParent(shared_from_this());
} }
bool Room::userToNewRoom(User *user, std::string newRoom, std::string password) { bool Room::userToNewRoom(std::shared_ptr<User> user, std::string newRoom, std::string password) {
return _parent->changeRoom(user, newRoom, password); return _parent->changeRoom(user, newRoom, password);
} }
@@ -171,7 +169,7 @@ namespace Yc {
return (_roundRunning || (_type & rounds) == rounds) && (_type & dice) == dice; return (_roundRunning || (_type & rounds) == rounds) && (_type & dice) == dice;
} }
unsigned int Room::addDice(User *user, int diceValue) { unsigned int Room::addDice(std::shared_ptr<User> user, int diceValue) {
if (!canDice()) { if (!canDice()) {
return 1; return 1;
} }
@@ -183,7 +181,6 @@ namespace Yc {
_diceValues.push_back(std::make_pair(user, diceValue)); _diceValues.push_back(std::make_pair(user, diceValue));
addMessage(User::dice, std::to_string(diceValue), user->name(), user->color()); addMessage(User::dice, std::to_string(diceValue), user->name(), user->color());
return 0; return 0;
} }
bool Room::accessAllowed(std::string userName, std::string password) { bool Room::accessAllowed(std::string userName, std::string password) {
@@ -223,13 +220,13 @@ namespace Yc {
} }
void Room::_showDiceRoundResults() { void Room::_showDiceRoundResults() {
std::sort(_diceValues.begin(), _diceValues.end(), [=](std::pair<User*, int> val1, std::pair<User*, int> val2) { std::sort(_diceValues.begin(), _diceValues.end(), [=](const std::pair<std::shared_ptr<User>, int>& val1, const std::pair<std::shared_ptr<User>, int>& val2) {
return (val1.second > val2.second); return (val1.second > val2.second);
}); });
Json::Value userList = Json::arrayValue; Json::Value userList = Json::arrayValue;
for (auto &user: _diceValues) { for (auto &user: _diceValues) {
Json::Value entry = Json::objectValue; Json::Value entry = Json::objectValue;
entry["name"] = user.first; entry["name"] = user.first->name();
entry["value"] = user.second; entry["value"] = user.second;
userList.append(entry); userList.append(entry);
} }

22
room.h
View File

@@ -16,7 +16,7 @@ namespace Yc {
class Server; class Server;
class Room: public Base { class Room: public Base, public std::enable_shared_from_this<Room> {
public: public:
enum RoomType { enum RoomType {
none = 0, none = 0,
@@ -26,15 +26,15 @@ namespace Yc {
password = 8 password = 8
}; };
Room(Server *parent, Json::Value roomParams); Room(std::shared_ptr<Server> parent, Json::Value roomParams);
~Room(); ~Room();
void run(); void run();
std::string name(); std::string name();
bool addUser(std::string userName, std::string color, std::string password, int socket); bool addUser(std::string userName, std::string color, std::string password, int socket);
bool addUser(User *user, std::string password); bool addUser(std::shared_ptr<User> user, std::string password);
bool userNameExists(std::string userName); bool userNameExists(std::string userName);
void removeUser(std::string _token, bool silent = false); void removeUser(std::string _token, bool silent = false);
void removeUser(User *user, bool silent = false); void removeUser(std::shared_ptr<User> user, bool silent = false);
void setStop(); void setStop();
void addMessage(User::MsgType type, const char* messageText, std::string userName = "", std::string color = ""); void addMessage(User::MsgType type, const char* messageText, std::string userName = "", std::string color = "");
void addMessage(User::MsgType type, std::string messageText, std::string userName = "", std::string color = ""); void addMessage(User::MsgType type, std::string messageText, std::string userName = "", std::string color = "");
@@ -42,11 +42,11 @@ namespace Yc {
RoomType type(); RoomType type();
bool isType(RoomType type); bool isType(RoomType type);
bool canDice(); bool canDice();
unsigned int addDice(User *user, int diceValue); unsigned int addDice(std::shared_ptr<User> user, int diceValue);
bool accessAllowed(std::string userName, std::string password); bool accessAllowed(std::string userName, std::string password);
bool userIsInRoom(std::string userName); bool userIsInRoom(std::string userName);
void addUserWhenQueueEmpty(User *user); void addUserWhenQueueEmpty(std::shared_ptr<User> user);
bool userToNewRoom(User *user, std::string newRoom, std::string password); bool userToNewRoom(std::shared_ptr<User> user, std::string newRoom, std::string password);
unsigned int flags(); unsigned int flags();
Json::Value userList(); Json::Value userList();
private: private:
@@ -57,21 +57,21 @@ namespace Yc {
std::string color; std::string color;
}; };
Server *_parent; std::shared_ptr<Server> _parent;
std::string _name; std::string _name;
std::string _password; std::string _password;
std::vector<std::string> _allowedUsers; std::vector<std::string> _allowedUsers;
RoomType _type; RoomType _type;
std::vector<User*> _users; std::vector<std::shared_ptr<User>> _users;
bool _blocked; bool _blocked;
bool _stop; bool _stop;
std::queue<Message> _msgQueue; std::queue<Message> _msgQueue;
std::thread *thread; std::unique_ptr<std::thread> thread;
bool _roundRunning; bool _roundRunning;
time_t _roundStart; time_t _roundStart;
time_t _lastRoundEnd; time_t _lastRoundEnd;
int _roundLength; int _roundLength;
std::vector<std::pair<User *, int>> _diceValues; std::vector<std::pair<std::shared_ptr<User>, int>> _diceValues;
void _handleDice(); void _handleDice();
void _startDiceRound(); void _startDiceRound();
void _endDiceRound(); void _endDiceRound();

View File

@@ -15,9 +15,9 @@
namespace Yc { namespace Yc {
namespace Lib { namespace Lib {
Server::Server(Yc::Lib::Config *config) : Server::Server(std::shared_ptr<Yc::Lib::Config> config) :
_config(config), _config(std::move(config)),
_stop(false) { _stop(false) {
struct sockaddr_in serverAddr; struct sockaddr_in serverAddr;
int opt = true; int opt = true;
_socket = socket(AF_INET, SOCK_STREAM, 0); _socket = socket(AF_INET, SOCK_STREAM, 0);
@@ -31,7 +31,7 @@ namespace Yc {
std::cout << "bind not possible" << std::endl; std::cout << "bind not possible" << std::endl;
exit(-1); exit(-1);
} }
createRooms(config->group("rooms")); createRooms(_config->group("rooms"));
} }
void Server::run() { void Server::run() {
@@ -49,14 +49,14 @@ namespace Yc {
FD_ZERO(&fd); FD_ZERO(&fd);
FD_SET(_socket, &fd); FD_SET(_socket, &fd);
if (select(_maxSd + 1, &fd, NULL, NULL, &tv) > 0) { if (select(_maxSd + 1, &fd, NULL, NULL, &tv) > 0) {
std::async(std::launch::async, &Server::handleRequest, this); std::thread(&Server::handleRequest, this).detach();
} }
} }
} }
std::vector<std::string> Server::roomList() { std::vector<std::string> Server::roomList() {
std::vector<std::string> list; std::vector<std::string> list;
for (auto &room: _rooms) { for (const auto &room: _rooms) {
list.push_back(room->name()); list.push_back(room->name());
} }
return list; return list;
@@ -82,7 +82,7 @@ namespace Yc {
return false; return false;
} }
bool Server::changeRoom(User *user, std::string newRoom, std::string password) { bool Server::changeRoom(std::shared_ptr<User> user, std::string newRoom, std::string password) {
if (!roomAllowed(newRoom, user->name(), password)) { if (!roomAllowed(newRoom, user->name(), password)) {
return false; return false;
} }
@@ -113,8 +113,9 @@ namespace Yc {
} }
void Server::createRooms(Json::Value roomList) { void Server::createRooms(Json::Value roomList) {
auto self = shared_from_this();
for (auto &room: roomList) { for (auto &room: roomList) {
Room *newRoom = new Room(this, room); auto newRoom = std::make_shared<Room>(self, room);
_rooms.push_back(newRoom); _rooms.push_back(newRoom);
} }
} }
@@ -143,7 +144,7 @@ namespace Yc {
} }
bool Server::userExists(std::string userName) { bool Server::userExists(std::string userName) {
for (auto &room: _rooms) { for (const auto &room: _rooms) {
if (room->userNameExists(userName)) { if (room->userNameExists(userName)) {
return true; return true;
} }
@@ -160,10 +161,10 @@ namespace Yc {
close(userSocket); close(userSocket);
return; return;
} }
std::string room = data["room"].asString(); std::string roomName = data["room"].asString();
bool added(false); bool added(false);
for (auto &room: _rooms) { for (auto &room: _rooms) {
if (room->name() == data["room"].asString()) { if (room->name() == roomName) {
if (room->addUser(data["name"].asString(), data["color"].asString(), data["password"].asString(), userSocket)) { if (room->addUser(data["name"].asString(), data["color"].asString(), data["password"].asString(), userSocket)) {
added = true; added = true;
break; break;

View File

@@ -10,20 +10,20 @@
namespace Yc { namespace Yc {
namespace Lib { namespace Lib {
class Server: public Base class Server: public Base, public std::enable_shared_from_this<Server>
{ {
public: public:
Server(Yc::Lib::Config *config); Server(std::shared_ptr<Yc::Lib::Config> config);
void run(); void run();
std::vector<std::string> roomList(); std::vector<std::string> roomList();
Json::Value jsonRoomList(); Json::Value jsonRoomList();
bool roomAllowed(std::string roomName, std::string userName, std::string password); bool roomAllowed(std::string roomName, std::string userName, std::string password);
bool changeRoom(User *user, std::string newRoom, std::string password); bool changeRoom(std::shared_ptr<User> user, std::string newRoom, std::string password);
private: private:
int _socket; int _socket;
Yc::Lib::Config *_config; std::shared_ptr<Yc::Lib::Config> _config;
bool _stop; bool _stop;
std::vector<Yc::Lib::Room*> _rooms; std::vector<std::shared_ptr<Yc::Lib::Room>> _rooms;
void createRooms(Json::Value roomList); void createRooms(Json::Value roomList);
void handleRequest(); void handleRequest();
void inputSwitcher(int userSocket, std::string input); void inputSwitcher(int userSocket, std::string input);

View File

@@ -14,15 +14,15 @@
namespace Yc { namespace Yc {
namespace Lib { namespace Lib {
User::User(Room *parent, std::string name, std::string color, int socket) : User::User(std::shared_ptr<Room> parent, std::string name, std::string color, int socket) :
_parent(parent), _parent(std::move(parent)),
_name(name), _name(std::move(name)),
_color(color), _color(std::move(color)),
_socket(socket), _socket(socket),
_stop(false) { _stop(false) {
_token = Yc::Lib::Tools::generateRandomString(32); _token = Yc::Lib::Tools::generateRandomString(32);
sendMsg(token, _token, "", ""); sendMsg(token, _token, "", "");
thread = new std::thread(&User::checkerTask, this); thread = std::make_unique<std::thread>(&User::checkerTask, this);
} }
User::~User() { User::~User() {
@@ -37,8 +37,8 @@ namespace Yc {
return (token == _token); return (token == _token);
} }
bool User::isUser(User *toValidate) { bool User::isUser(std::shared_ptr<User> toValidate) {
return (toValidate == this); return (toValidate.get() == this);
} }
void User::sendMsg(MsgType type, const char *message, std::string userName, std::string color) { void User::sendMsg(MsgType type, const char *message, std::string userName, std::string color) {
@@ -94,8 +94,8 @@ namespace Yc {
return _color; return _color;
} }
void User::setParent(Room *parent) { void User::setParent(std::shared_ptr<Room> parent) {
_parent = parent; _parent = std::move(parent);
} }
void User::send(std::string out) { void User::send(std::string out) {
@@ -151,7 +151,7 @@ namespace Yc {
} }
void User::doDice() { void User::doDice() {
switch (_parent->addDice(this, (rand() % 6) + 1)) { switch (_parent->addDice(shared_from_this(), (rand() % 6) + 1)) {
case 1: case 1:
sendMsg(system, "dice_not_possible", "", ""); sendMsg(system, "dice_not_possible", "", "");
break; break;
@@ -164,7 +164,7 @@ namespace Yc {
} }
void User::changeRoom(std::string newRoom, std::string password) { void User::changeRoom(std::string newRoom, std::string password) {
if (!_parent->userToNewRoom(this, newRoom, password)) { if (!_parent->userToNewRoom(shared_from_this(), newRoom, password)) {
sendMsg(User::system, "room_not_possible", "", ""); sendMsg(User::system, "room_not_possible", "", "");
} }
} }

12
user.h
View File

@@ -11,7 +11,7 @@ namespace Yc {
class Room; class Room;
class User: public Base { class User: public Base, public std::enable_shared_from_this<User> {
public: public:
enum MsgType { enum MsgType {
error = -1, error = -1,
@@ -26,26 +26,26 @@ namespace Yc {
result = 9 result = 9
}; };
User(Room *parent, std::string name, std::string color, int socket); User(std::shared_ptr<Room> parent, std::string name, std::string color, int socket);
~User(); ~User();
std::string name() const; std::string name() const;
bool validateToken(std::string token); bool validateToken(std::string token);
bool isUser(User *toValidate); bool isUser(std::shared_ptr<User> toValidate);
void sendMsg(MsgType type, std::string message, std::string userName, std::string color); void sendMsg(MsgType type, std::string message, std::string userName, std::string color);
void sendMsg(MsgType type, const char *message, std::string userName, std::string color); void sendMsg(MsgType type, const char *message, std::string userName, std::string color);
void sendMsg(MsgType type, Json::Value message, std::string userName, std::string color); void sendMsg(MsgType type, Json::Value message, std::string userName, std::string color);
void checkerTask(); void checkerTask();
void stop(); void stop();
std::string color() const; std::string color() const;
void setParent(Room *parent); void setParent(std::shared_ptr<Room> parent);
private: private:
Room *_parent; std::shared_ptr<Room> _parent;
std::string _name; std::string _name;
std::string _color; std::string _color;
int _socket; int _socket;
std::string _token; std::string _token;
bool _stop; bool _stop;
std::thread *thread; std::unique_ptr<std::thread> thread;
void send(std::string out); void send(std::string out);
void send(Json::Value out); void send(Json::Value out);
void handleMessage(std::string message); void handleMessage(std::string message);