Contents

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:

  1. Timer Application: A precision timer with chrono library integration
  2. 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.