Skip to content

Instantly share code, notes, and snippets.

@layou233
Created May 9, 2025 15:08
Show Gist options
  • Save layou233/944a41e084700e922349e6049905190e to your computer and use it in GitHub Desktop.
Save layou233/944a41e084700e922349e6049905190e to your computer and use it in GitHub Desktop.
Practice real estate system in C++
/**
* Copyright (C) 2025 Launium, all rights reserved.
* This is a practice project and comes with ABSOLUTELY
* NO WARRANTY, full of unsafe function calls, unverified code
* and your computer may end up nuked.
* DO NOT COPY ANY PART OF THE CODE or you may face
* attacks such as buffer overflow, code injection, etc.
* Anyway, use at your own risk. :)
* @date May 9th, 2025
*/
#include <algorithm>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <iomanip>
#include <cstdint>
#include <cstdlib>
#ifdef _WIN32
#define clear_screen() system("cls")
#else // assume ANSI compatible terminal
#define clear_screen() std::cout << "\e[1;1H\e[2J"
#endif
using namespace std;
constexpr int width_id = 6;
constexpr int width_area = 10;
constexpr int width_unit_price = 15;
constexpr int width_total_price = 15;
constexpr int width_address = 40;
constexpr int width_owner = 15;
constexpr int width_contact = 20;
constexpr int width_date = 12;
string truncate(const string &s, size_t max_length)
{
return s.length() >= max_length - 5 ? s.substr(0, max_length - 5) + "..." : s;
}
struct Resource
{
struct StringTable
{
string language_name;
string you_have_chosen;
string as_display_language;
string save_is_not_found_and_creating;
string available_commands;
string prompt_continue_from_save;
string prompt_continue_with_enter;
string prompt_enter_id;
string prompt_enter_to_return_to_main;
string prompt_zero_to_generate_an_id;
string unknown_command;
string no_id_found;
string saved_successfully;
string property_record_removed;
string new_property_record_added;
string unsaved_exit_warning;
string exit_thanks;
string unknown_contact_method;
string contact_method_phone;
string contact_method_email;
string main_menu_view_database;
string main_menu_get_by_id;
string main_menu_add_new;
string main_menu_save_to_file;
string main_menu_exit;
string database_area;
string database_unit_price;
string database_total_price;
string database_address;
string database_owner;
string database_contact;
string database_purchase_date;
string info_menu_commands_for;
string info_menu_delete;
string info_menu_back;
} string;
} R;
// uint8 causes problems for cin input since
// cin would concider it as character type
// and store ASCII value of the input into it
// Another C shit :)
#define command_t uint_fast16_t
#define LANG_EN 1
#define LANG_CN 2
void load_translation(command_t lang)
{
switch (lang)
{
case LANG_EN:
R.string.language_name = "English - en_US";
R.string.you_have_chosen = "You have chosen ";
R.string.as_display_language = " as the display language.";
R.string.save_is_not_found_and_creating = "No save is found. Creating new database...";
R.string.available_commands = "Available Commands";
R.string.prompt_continue_from_save = "Do you want to continue from a previous save? [Y/n]: ";
R.string.prompt_continue_with_enter = "Press enter to continue...";
R.string.prompt_enter_id = "Enter the ID: ";
R.string.prompt_enter_to_return_to_main = "Press enter to return to main menu...";
R.string.prompt_zero_to_generate_an_id = "0 to generate an ID";
R.string.no_id_found = "No house found with ID: ";
R.string.saved_successfully = "Database saved.";
R.string.property_record_removed = "Removed property record.";
R.string.new_property_record_added = "Added new property record to the database.";
R.string.unsaved_exit_warning = "You have unsaved changes. Exit anyway? [y/N]: ";
R.string.exit_thanks = "Thanks for using the Real Estate System. Goodbye!";
R.string.unknown_contact_method = "Unknown contact method";
R.string.contact_method_phone = "Phone";
R.string.contact_method_email = "Email";
R.string.main_menu_view_database = "View database";
R.string.main_menu_get_by_id = "Get real estate information by ID";
R.string.main_menu_add_new = "Add new real estate information";
R.string.main_menu_save_to_file = "Save the database to file";
R.string.main_menu_exit = "Exit program";
R.string.info_menu_commands_for = "Available commands for this property record:";
R.string.info_menu_delete = "Delete";
R.string.info_menu_back = "Back to main menu";
R.string.database_area = "Area/m²";
R.string.database_unit_price = "Unit Price/m²";
R.string.database_total_price = "Total Price";
R.string.database_address = "Address";
R.string.database_owner = "Owner";
R.string.database_contact = "Contact";
R.string.database_purchase_date = "Purchase Date";
R.string.unknown_command = "Unknown command: ";
break;
case LANG_CN:
R.string.language_name = "简体中文(中国) - zh_CN";
R.string.you_have_chosen = "您已选择 ";
R.string.as_display_language = " 作为显示语言。";
R.string.save_is_not_found_and_creating = "未找到存档。正在创建新的数据库...";
R.string.available_commands = "可用的命令";
R.string.prompt_continue_from_save = "您想从先前的存档中继续吗?[Y/n]: ";
R.string.prompt_continue_with_enter = "请按回车键以继续...";
R.string.prompt_enter_id = "输入 ID: ";
R.string.prompt_enter_to_return_to_main = "请按回车键以回到主菜单...";
R.string.prompt_zero_to_generate_an_id = "0 以生成一个 ID";
R.string.no_id_found = "没找到对应 ID 的房地产:";
R.string.saved_successfully = "数据库已保存。";
R.string.property_record_removed = "已删除财产记录。";
R.string.new_property_record_added = "已将新财产记录添加到数据库。";
R.string.unsaved_exit_warning = "您有未保存的变更。仍然退出?[y/N]: ";
R.string.exit_thanks = "感谢使用房地产系统。再见!";
R.string.unknown_contact_method = "未知联系方式";
R.string.contact_method_phone = "电话";
R.string.contact_method_email = "电子邮件";
R.string.main_menu_view_database = "查看数据库";
R.string.main_menu_get_by_id = "获取指定 ID 房地产信息";
R.string.main_menu_add_new = "添加新的房地产信息";
R.string.main_menu_save_to_file = "保存数据库到文件";
R.string.main_menu_exit = "退出程序";
R.string.info_menu_commands_for = "针对此财产记录的可用命令:";
R.string.info_menu_delete = "删除";
R.string.info_menu_back = "返回主菜单";
R.string.database_area = "面积/m²";
R.string.database_unit_price = "单价/m²";
R.string.database_total_price = "总价";
R.string.database_address = "地址";
R.string.database_owner = "业主";
R.string.database_contact = "联系方式";
R.string.database_purchase_date = "购买日期";
R.string.unknown_command = "未知的命令:";
break;
default:
cout << "Unknown language code: " << lang << endl;
exit(1);
}
}
void pause()
{
cout << R.string.prompt_continue_with_enter;
cin.ignore();
cin.get();
cout << endl;
}
void print_banner()
{
cout << "______ _ _____ _ _ _____ _" << endl
<< "| ___ \\ | | | ___| | | | | / ___| | |" << endl
<< "| |_/ /___ __ _| | | |__ ___| |_ __ _| |_ ___ \\ `--. _ _ ___| |_ ___ _ __ ___" << endl
<< "| // _ \\/ _` | | | __/ __| __/ _` | __/ _ \\ `--. \\ | | / __| __/ _ \\ '_ ` _ \\" << endl
<< "| |\\ \\ __/ (_| | | | |__\\__ \\ || (_| | || __/ /\\__/ / |_| \\__ \\ || __/ | | | | |" << endl
<< "\\_| \\_\\___|\\__,_|_| \\____/___/\\__\\__,_|\\__\\___| \\____/ \\__, |___/\\__\\___|_| |_| |_|" << endl
<< " Copyright (C) 2025 Launium, all rights reserved. __/ |" << endl
<< " |___/" << endl;
}
#define MAIN_MENU_VIEW_DATABASE 1
#define MAIN_MENU_GET_BY_ID 2
#define MAIN_MENU_ADD_NEW 3
#define MAIN_MENU_SAVE_TO_FILE 4
#define MAIN_MENU_EXIT 5
void print_main_menu()
{
cout << "======= " << R.string.available_commands << " =======" << endl
<< "1 - " << R.string.main_menu_view_database << endl
<< "2 - " << R.string.main_menu_get_by_id << endl
<< "3 - " << R.string.main_menu_add_new << endl
<< "4 - " << R.string.main_menu_save_to_file << endl
<< "5 - " << R.string.main_menu_exit << endl;
}
#define INFO_MENU_DELETE 1
#define INFO_MENU_BACK 2
void print_information_menu()
{
cout << "======= " << R.string.available_commands << " =======" << endl
<< "1 - " << R.string.info_menu_delete << endl
<< "2 - " << R.string.info_menu_back << endl;
}
class Contact
{
public:
Contact()
{
}
virtual string method_descriptor()
{
return "unknown";
}
virtual string method_name()
{
return R.string.unknown_contact_method;
}
virtual string to_string()
{
return "";
};
};
class PhoneContact : public Contact
{
private:
uint64_t phone_number;
public:
PhoneContact(uint64_t phone_number)
{
this->phone_number = phone_number;
}
PhoneContact(const char *raw)
{
this->phone_number = strtoull(raw, nullptr, 10);
}
string method_descriptor()
{
return "phone";
}
string method_name()
{
return R.string.contact_method_phone;
}
string to_string()
{
return std::to_string(phone_number);
}
};
class EmailContact : public Contact
{
private:
string email;
public:
EmailContact(string email)
{
this->email = email;
}
string method_descriptor()
{
return "email";
}
string method_name()
{
return R.string.contact_method_email;
}
string to_string()
{
return email;
}
};
class House
{
public:
uint64_t id;
uint32_t area; // in square centimeters
uint32_t price_per_square_meter; // in coins per square meter
string address;
string owner;
Contact *contact;
uint32_t purchase_time; // YYYYMMDD, e.g. 20250102
House()
{
}
~House()
{
if (contact)
{
delete contact;
}
}
bool operator<(const House &another)
{
return id > another.id;
}
bool operator>(const House &another)
{
return id < another.id;
}
double get_area_in_square_meters()
{
return 1.0e-6 * area;
}
};
class Database
{
private:
vector<House *> houses;
bool updated;
public:
Database()
{
updated = false;
}
Database(fstream &f)
{
updated = false;
string line;
while (getline(f, line))
{
if (line.empty())
continue;
stringstream ss(line);
string temp;
House *house = new House();
getline(ss, temp, ',');
house->id = strtoull(temp.c_str(), nullptr, 10);
getline(ss, temp, ',');
house->area = stoul(temp);
getline(ss, temp, ',');
house->price_per_square_meter = stoul(temp);
getline(ss, house->address, ',');
getline(ss, house->owner, ',');
string contact_method;
getline(ss, contact_method, ',');
string contact_detail;
getline(ss, contact_detail, ',');
if (contact_method == "phone")
{
house->contact = new PhoneContact(contact_detail.c_str());
}
else if (contact_method == "email")
{
house->contact = new EmailContact(contact_detail);
}
else
{
house->contact = new Contact();
}
getline(ss, temp, ',');
house->purchase_time = stoul(temp);
houses.push_back(house);
}
sort_database_by_id();
}
~Database()
{
for (House *h : houses)
delete h;
houses.clear();
}
inline void sort_database_by_id()
{
sort(houses.begin(), houses.end());
}
void event_loop()
{
for (;;)
{
print_main_menu();
cout << endl
<< "> ";
command_t command;
cin >> command;
switch (command)
{
case MAIN_MENU_VIEW_DATABASE:
{
clear_screen();
cout << left
<< setw(width_id) << "ID"
<< setw(width_area) << R.string.database_area
<< setw(width_unit_price) << R.string.database_unit_price
<< setw(width_total_price) << R.string.database_total_price
<< setw(width_address) << R.string.database_address
<< setw(width_owner) << R.string.database_owner
<< setw(width_contact) << R.string.database_contact
<< setw(width_date) << R.string.database_purchase_date
<< endl;
for (House *h : houses)
{
double area = h->get_area_in_square_meters();
cout << left
<< setw(width_id) << h->id
<< setw(width_area) << area
<< setw(width_unit_price) << h->price_per_square_meter
<< setw(width_total_price) << area * h->price_per_square_meter
<< setw(width_address) << truncate(h->address, width_address)
<< setw(width_owner) << truncate(h->owner, width_owner)
<< setw(width_contact) << h->contact->to_string()
<< setw(width_date) << h->purchase_time
<< endl;
}
cout << endl;
pause();
break;
}
case MAIN_MENU_GET_BY_ID:
{
uint64_t query_id;
cout << R.string.prompt_enter_id;
cin >> query_id;
vector<House *>::iterator it = find_if(houses.begin(), houses.end(), [&](House *h)
{ return h->id == query_id; });
if (it != houses.end())
{
House *h = *it;
double area = h->get_area_in_square_meters();
cout << "ID: " << h->id << endl
<< R.string.database_area << ": " << area << endl
<< R.string.database_unit_price << ": " << h->price_per_square_meter << endl
<< R.string.database_total_price << ": " << area * h->price_per_square_meter << endl
<< R.string.database_address << ": " << h->address << endl
<< R.string.database_owner << ": " << h->owner << endl
<< R.string.database_contact << ": [" << h->contact->method_name() << "] " << h->contact->to_string() << endl
<< R.string.database_purchase_date << "(YYYY MM DD): " << h->purchase_time << endl;
cout << endl
<< R.string.info_menu_commands_for << endl;
bool loop = true;
while (loop)
{
print_information_menu();
cout << endl
<< "> ";
cin >> command;
switch (command)
{
case INFO_MENU_DELETE:
{
houses.erase(it);
delete h;
cout << R.string.property_record_removed << endl;
updated = true;
loop = false;
break;
}
case INFO_MENU_BACK:
{
loop = false;
break;
}
default:
{
cout << R.string.unknown_command << command << endl;
break;
}
}
}
}
else
{
cout << R.string.no_id_found << query_id << endl;
}
cout << endl;
pause();
break;
}
case MAIN_MENU_ADD_NEW:
{
clear_screen();
House *new_house = new House();
cout << "ID (" << R.string.prompt_zero_to_generate_an_id << "): ";
cin >> new_house->id;
if (new_house->id <= 0)
{ // default ID by rules
if (houses.empty())
{
new_house->id = 0;
}
else
{
new_house->id = houses.back()->id + 1;
}
}
double area_m2;
cout << R.string.database_area << " (m²): ";
cin >> area_m2;
new_house->area = area_m2 * 1e6; // converts to square centimeter
cout << R.string.database_unit_price << ": ";
cin >> new_house->price_per_square_meter;
cout << R.string.database_address << ": ";
cin.ignore();
getline(cin, new_house->address);
cout << R.string.database_owner << ": ";
getline(cin, new_house->owner);
cout << R.string.database_contact << " (1 = Phone, 2 = Email): ";
int contact_type;
cin >> contact_type;
cin.ignore();
if (contact_type == 1)
{
string phone;
cout << R.string.contact_method_phone << ": ";
getline(cin, phone);
new_house->contact = new PhoneContact(phone.c_str());
}
else if (contact_type == 2)
{
string email;
cout << R.string.contact_method_email << ": ";
getline(cin, email);
new_house->contact = new EmailContact(email);
}
else
{
new_house->contact = new Contact(); // fallback
}
cout << R.string.database_purchase_date << " (YYYYMMDD): ";
cin >> new_house->purchase_time;
houses.push_back(new_house);
sort_database_by_id();
updated = true;
cout << endl
<< R.string.new_property_record_added << endl;
pause();
break;
}
case MAIN_MENU_SAVE_TO_FILE:
{
ofstream out("real_estate_database.csv");
for (House *h : houses)
{
out << h->id << "," << h->area << "," << h->price_per_square_meter << ","
<< h->address << "," << h->owner << ","
<< h->contact->method_descriptor() << "," << h->contact->to_string() << ","
<< h->purchase_time << endl;
}
out.close();
}
updated = false;
cout << R.string.saved_successfully << endl;
pause();
break;
case MAIN_MENU_EXIT:
{
if (updated)
{
cout << R.string.unsaved_exit_warning;
char exit_choice;
cin >> exit_choice;
if (exit_choice != 'y' && exit_choice != 'Y')
{
break; // back to main menu
}
}
cout << endl
<< R.string.exit_thanks << endl;
pause();
return;
}
default:
cout << R.string.unknown_command << command << endl;
pause();
break;
}
clear_screen();
print_banner();
}
}
};
int main()
{
clear_screen();
#ifdef _WIN32
system("chcp 65001>nul"); // Set the console to expect codepage 65001 = UTF-8.
#endif
print_banner();
cout << "Welcome to Real Estate System." << endl;
cout << endl
<< "Choose a display language:" << endl
<< endl;
cout << "1 - English (United States) - en_US" << endl;
cout << "2 - 简体中文(中国) - zh_CN" << endl;
cout << endl
<< "Default 1. Enter your choice: ";
{
command_t lang;
cin >> lang;
load_translation(lang);
}
clear_screen();
print_banner();
cout << R.string.you_have_chosen << R.string.language_name << R.string.as_display_language << endl
<< endl;
// load save
Database *database;
fstream file_save;
file_save.open("real_estate_database.csv", ios_base::in);
if (file_save)
{
cout << R.string.prompt_continue_from_save;
char continue_from_save;
cin >> continue_from_save;
if (continue_from_save != 'n' && continue_from_save != 'N')
{
database = new Database(file_save);
}
else
{
database = new Database();
}
file_save.close();
}
else
{
cout << R.string.save_is_not_found_and_creating << endl;
}
database->event_loop();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment