C++ System Programming Projects: Timer and Supermarket Management
C++ System Programming Projects: Timer and Supermarket Management
In this post, I’ll share my experience building two significant C++ projects that demonstrate system programming concepts, database integration, and modern C++ development practices.
Project Overview
These projects represent my journey into C++ system programming, covering:
- Timer Application: A precision timer with chrono library integration
- Supermarket Management System: A comprehensive inventory management system with PostgreSQL integration
Both projects showcase modern C++ features, testing practices, and database connectivity.
Timer Application
Project Goals
- Precision Timing: High-precision timing using C++ chrono library
- Cross-platform: Portable across different operating systems
- Testing: Comprehensive unit testing with Google Test
- Performance: Optimized for minimal overhead
Technical Implementation
Chrono Library Usage
#include <chrono>
#include <thread>
class PrecisionTimer {
private:
std::chrono::high_resolution_clock::time_point start_time;
std::chrono::high_resolution_clock::time_point end_time;
public:
void start() {
start_time = std::chrono::high_resolution_clock::now();
}
void stop() {
end_time = std::chrono::high_resolution_clock::now();
}
double getElapsedMilliseconds() const {
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - start_time
);
return duration.count();
}
double getElapsedMicroseconds() const {
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
end_time - start_time
);
return duration.count();
}
};Threading Implementation
class TimerThread {
private:
std::thread timer_thread;
std::atomic<bool> running{false};
std::function<void()> callback;
std::chrono::milliseconds interval;
public:
TimerThread(std::function<void()> cb, std::chrono::milliseconds interval_ms)
: callback(cb), interval(interval_ms) {}
void start() {
running = true;
timer_thread = std::thread([this]() {
while (running) {
std::this_thread::sleep_for(interval);
if (running) {
callback();
}
}
});
}
void stop() {
running = false;
if (timer_thread.joinable()) {
timer_thread.join();
}
}
};Testing Strategy
#include <gtest/gtest.h>
class TimerTest : public ::testing::Test {
protected:
void SetUp() override {
timer = std::make_unique<PrecisionTimer>();
}
std::unique_ptr<PrecisionTimer> timer;
};
TEST_F(TimerTest, BasicTiming) {
timer->start();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
timer->stop();
double elapsed = timer->getElapsedMilliseconds();
EXPECT_GE(elapsed, 95);
EXPECT_LE(elapsed, 105);
}Supermarket Management System
Project Architecture
- Database Layer: PostgreSQL integration with libpqxx
- Business Logic: Inventory management and transaction processing
- User Interface: Command-line interface for system interaction
- Testing: Comprehensive unit and integration tests
Database Integration
Connection Management
#include <pqxx/pqxx>
class DatabaseManager {
private:
std::unique_ptr<pqxx::connection> connection;
public:
DatabaseManager(const std::string& connection_string) {
try {
connection = std::make_unique<pqxx::connection>(connection_string);
} catch (const std::exception& e) {
throw std::runtime_error("Database connection failed: " + std::string(e.what()));
}
}
pqxx::connection& getConnection() {
return *connection;
}
};Product Management
class ProductManager {
private:
DatabaseManager& db_manager;
public:
ProductManager(DatabaseManager& db) : db_manager(db) {}
bool addProduct(const std::string& name, double price, int quantity) {
try {
pqxx::work transaction(db_manager.getConnection());
std::string query = "INSERT INTO products (name, price, quantity) VALUES ($1, $2, $3)";
transaction.exec_params(query, name, price, quantity);
transaction.commit();
return true;
} catch (const std::exception& e) {
std::cerr << "Error adding product: " << e.what() << std::endl;
return false;
}
}
std::vector<Product> getAllProducts() {
std::vector<Product> products;
try {
pqxx::work transaction(db_manager.getConnection());
pqxx::result result = transaction.exec("SELECT id, name, price, quantity FROM products");
for (const auto& row : result) {
Product product;
product.id = row[0].as<int>();
product.name = row[1].as<std::string>();
product.price = row[2].as<double>();
product.quantity = row[3].as<int>();
products.push_back(product);
}
transaction.commit();
} catch (const std::exception& e) {
std::cerr << "Error retrieving products: " << e.what() << std::endl;
}
return products;
}
};Transaction Processing
class TransactionManager {
private:
DatabaseManager& db_manager;
public:
TransactionManager(DatabaseManager& db) : db_manager(db) {}
bool processSale(const std::vector<SaleItem>& items) {
try {
pqxx::work transaction(db_manager.getConnection());
// Start transaction
transaction.exec("BEGIN");
// Process each item
for (const auto& item : items) {
// Check inventory
pqxx::result result = transaction.exec_params(
"SELECT quantity FROM products WHERE id = $1",
item.product_id
);
if (result.empty()) {
throw std::runtime_error("Product not found");
}
int available_quantity = result[0][0].as<int>();
if (available_quantity < item.quantity) {
throw std::runtime_error("Insufficient inventory");
}
// Update inventory
transaction.exec_params(
"UPDATE products SET quantity = quantity - $1 WHERE id = $2",
item.quantity, item.product_id
);
// Record sale
transaction.exec_params(
"INSERT INTO sales (product_id, quantity, price, sale_date) VALUES ($1, $2, $3, NOW())",
item.product_id, item.quantity, item.price
);
}
transaction.commit();
return true;
} catch (const std::exception& e) {
std::cerr << "Transaction failed: " << e.what() << std::endl;
return false;
}
}
};Build System and Dependencies
CMake Configuration
cmake_minimum_required(VERSION 3.16)
project(SupermarketManagement)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Find required packages
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBPQXX REQUIRED libpqxx)
# Include directories
include_directories(${LIBPQXX_INCLUDE_DIRS})
# Add executable
add_executable(supermarket_manager
src/main.cpp
src/DatabaseManager.cpp
src/ProductManager.cpp
src/TransactionManager.cpp
)
# Link libraries
target_link_libraries(supermarket_manager ${LIBPQXX_LIBRARIES})
# Add tests
enable_testing()
add_subdirectory(tests)Testing Setup
# tests/CMakeLists.txt
find_package(GTest REQUIRED)
add_executable(supermarket_tests
test_main.cpp
test_database_manager.cpp
test_product_manager.cpp
test_transaction_manager.cpp
)
target_link_libraries(supermarket_tests
GTest::GTest
GTest::Main
${LIBPQXX_LIBRARIES}
)
enable_testing()
add_test(NAME SupermarketTests COMMAND supermarket_tests)Development Challenges
Memory Management
- RAII Principles: Resource Acquisition Is Initialization
- Smart Pointers: Using unique_ptr and shared_ptr appropriately
- Exception Safety: Ensuring exception-safe code
- Resource Cleanup: Proper cleanup of database connections
Database Integration
- Connection Pooling: Managing database connections efficiently
- Transaction Management: Ensuring ACID properties
- Error Handling: Robust error handling for database operations
- SQL Injection Prevention: Using parameterized queries
Cross-platform Compatibility
- Compiler Differences: Handling different compiler behaviors
- Library Dependencies: Managing platform-specific dependencies
- Build System: Ensuring consistent builds across platforms
- Testing: Cross-platform testing strategies
Performance Optimization
Database Performance
- Query Optimization: Efficient SQL queries
- Indexing: Proper database indexing
- Connection Management: Efficient connection handling
- Batch Operations: Batch processing for bulk operations
Memory Optimization
- Object Pooling: Reusing objects to reduce allocations
- Move Semantics: Using move semantics for efficiency
- Container Optimization: Choosing appropriate containers
- Memory Profiling: Identifying memory bottlenecks
Lessons Learned
C++ Best Practices
- Modern C++: Using C++11/14/17 features effectively
- RAII: Resource management through RAII principles
- Exception Safety: Writing exception-safe code
- Template Programming: Effective use of templates
System Programming
- Low-level Control: Understanding system-level operations
- Performance: Writing high-performance code
- Memory Management: Efficient memory usage
- Cross-platform: Writing portable code
Database Integration
- ACID Properties: Understanding transaction properties
- Connection Management: Efficient database connections
- Error Handling: Robust error handling strategies
- Performance: Database performance optimization
Future Enhancements
Advanced Features
- Multi-threading: Concurrent transaction processing
- Caching: In-memory caching for improved performance
- Logging: Comprehensive logging system
- Configuration: External configuration management
Scalability Improvements
- Connection Pooling: Advanced connection pooling
- Load Balancing: Distributed system architecture
- Monitoring: Performance monitoring and metrics
- Backup: Automated backup and recovery
Conclusion
These C++ projects demonstrate the power of system programming and database integration. Key achievements include:
- Modern C++: Effective use of modern C++ features
- Database Integration: Robust PostgreSQL integration
- Testing: Comprehensive testing strategies
- Performance: High-performance system design
- Cross-platform: Portable code across platforms
Both projects are available on GitHub:
These projects represent my exploration into C++ system programming and demonstrate how modern C++ can be used to build robust, high-performance applications with database integration. The lessons learned here continue to influence my approach to system programming and performance optimization.