cpp

exercises

exercises.cpp⚙️
/**
 * Preprocessor Directives & Namespaces - Exercises
 * Practice problems from beginner to advanced
 */

#include <iostream>
#include <string>
#include <vector>
#include <cmath>

using std::cout;
using std::endl;
using std::string;

// ============================================================================
// EXERCISE 1: Basic Macros (Beginner)
// ============================================================================
/**
 * TODO: Define the following macros:
 * 1. EULER - Euler's number (2.71828)
 * 2. GOLDEN_RATIO - The golden ratio (1.61803)
 * 3. DOUBLE(x) - Returns x * 2
 * 4. IS_POSITIVE(x) - Returns 1 if x > 0, else 0
 * 5. SWAP_INT(a, b) - Swaps two integers (hint: use a temp variable and comma operator)
 */

// YOUR CODE HERE:
// #define EULER ...
// #define GOLDEN_RATIO ...
// #define DOUBLE(x) ...
// #define IS_POSITIVE(x) ...
// #define SWAP_INT(a, b) ...

void exercise1_test() {
    cout << "=== EXERCISE 1: Basic Macros ===" << endl;
    
    // Uncomment to test when you've implemented the macros:
    /*
    cout << "EULER = " << EULER << endl;
    cout << "GOLDEN_RATIO = " << GOLDEN_RATIO << endl;
    cout << "DOUBLE(5) = " << DOUBLE(5) << endl;
    cout << "DOUBLE(3.5) = " << DOUBLE(3.5) << endl;
    cout << "IS_POSITIVE(5) = " << IS_POSITIVE(5) << endl;
    cout << "IS_POSITIVE(-3) = " << IS_POSITIVE(-3) << endl;
    cout << "IS_POSITIVE(0) = " << IS_POSITIVE(0) << endl;
    
    int a = 10, b = 20;
    cout << "Before swap: a=" << a << ", b=" << b << endl;
    SWAP_INT(a, b);
    cout << "After swap: a=" << a << ", b=" << b << endl;
    */
}

// ============================================================================
// EXERCISE 2: Safe Macros (Beginner)
// ============================================================================
/**
 * The following macros have bugs. Fix them by adding proper parentheses.
 * 
 * Bug examples:
 * - BUGGY_SQUARE(2+3) gives 2+3*2+3 = 11, not 25
 * - BUGGY_AVERAGE(4, 6) gives 4+6/2 = 7, not 5
 */

#define BUGGY_SQUARE(x) x * x
#define BUGGY_CUBE(x) x * x * x  
#define BUGGY_AVERAGE(a, b) a + b / 2
#define BUGGY_DIFF(a, b) a - b

// TODO: Create fixed versions:
// #define SAFE_SQUARE(x) ...
// #define SAFE_CUBE(x) ...
// #define SAFE_AVERAGE(a, b) ...
// #define SAFE_DIFF(a, b) ...

void exercise2_test() {
    cout << "\n=== EXERCISE 2: Safe Macros ===" << endl;
    
    // Demonstrate the bugs:
    cout << "Buggy macros (incorrect results):" << endl;
    cout << "BUGGY_SQUARE(2+3) = " << BUGGY_SQUARE(2+3) << " (should be 25)" << endl;
    cout << "BUGGY_AVERAGE(4, 6) = " << BUGGY_AVERAGE(4, 6) << " (should be 5)" << endl;
    cout << "BUGGY_DIFF(10, 3+2) = " << BUGGY_DIFF(10, 3+2) << " (should be 5)" << endl;
    
    // Uncomment to test your fixed versions:
    /*
    cout << "\nFixed macros (correct results):" << endl;
    cout << "SAFE_SQUARE(2+3) = " << SAFE_SQUARE(2+3) << endl;
    cout << "SAFE_AVERAGE(4, 6) = " << SAFE_AVERAGE(4, 6) << endl;
    cout << "SAFE_DIFF(10, 3+2) = " << SAFE_DIFF(10, 3+2) << endl;
    */
}

// ============================================================================
// EXERCISE 3: Stringification (Intermediate)
// ============================================================================
/**
 * TODO: Create macros that:
 * 1. PRINT_VAR(x) - Prints "x = <value of x>" (e.g., "count = 42")
 * 2. PRINT_EXPR(expr) - Prints "expr = <result>" (e.g., "2+2 = 4")
 * 3. DEBUG_VAR(x) - Prints "[file:line] x = <value>"
 */

// YOUR CODE HERE:
// #define PRINT_VAR(x) ...
// #define PRINT_EXPR(expr) ...
// #define DEBUG_VAR(x) ...

void exercise3_test() {
    cout << "\n=== EXERCISE 3: Stringification ===" << endl;
    
    int count = 42;
    double price = 19.99;
    string name = "Alice";
    
    // Uncomment to test:
    /*
    PRINT_VAR(count);      // Should print: count = 42
    PRINT_VAR(price);      // Should print: price = 19.99
    PRINT_VAR(name);       // Should print: name = Alice
    
    PRINT_EXPR(2 + 2);         // Should print: 2 + 2 = 4
    PRINT_EXPR(count * 2);     // Should print: count * 2 = 84
    PRINT_EXPR(price > 10);    // Should print: price > 10 = 1
    
    DEBUG_VAR(count);      // Should print: [exercises.cpp:XX] count = 42
    */
}

// ============================================================================
// EXERCISE 4: Token Pasting (Intermediate)
// ============================================================================
/**
 * TODO: Create macros that:
 * 1. DECLARE_GETTER(type, name) - Creates a getter function for a member
 *    Example: DECLARE_GETTER(int, age) creates: int getAge() const { return age; }
 * 
 * 2. DECLARE_SETTER(type, name) - Creates a setter function
 *    Example: DECLARE_SETTER(int, age) creates: void setAge(int value) { age = value; }
 * 
 * 3. DECLARE_PROPERTY(type, name) - Creates both getter and setter
 */

// YOUR CODE HERE:
// #define DECLARE_GETTER(type, name) ...
// #define DECLARE_SETTER(type, name) ...
// #define DECLARE_PROPERTY(type, name) ...

class PersonExercise4 {
private:
    int age = 0;
    string name;
    double salary = 0.0;
    
public:
    // TODO: Use your macros here:
    // DECLARE_PROPERTY(int, age)
    // DECLARE_PROPERTY(string, name)  
    // DECLARE_GETTER(double, salary)  // Read-only
    
    // Manual implementation for testing (remove when using macros):
    int getAge() const { return age; }
    void setAge(int value) { age = value; }
    string getName() const { return name; }
    void setName(string value) { name = value; }
    double getSalary() const { return salary; }
};

void exercise4_test() {
    cout << "\n=== EXERCISE 4: Token Pasting ===" << endl;
    
    PersonExercise4 p;
    p.setAge(30);
    p.setName("John");
    
    cout << "Name: " << p.getName() << endl;
    cout << "Age: " << p.getAge() << endl;
    cout << "Salary: " << p.getSalary() << endl;
}

// ============================================================================
// EXERCISE 5: Conditional Compilation (Intermediate)
// ============================================================================
/**
 * TODO: Create a logging system with different levels.
 * Define LOG_LEVEL (0=none, 1=error, 2=warning, 3=info, 4=debug, 5=trace)
 * 
 * Create macros:
 * - LOG_ERROR(msg) - Always show if LOG_LEVEL >= 1
 * - LOG_WARNING(msg) - Show if LOG_LEVEL >= 2
 * - LOG_INFO(msg) - Show if LOG_LEVEL >= 3
 * - LOG_DEBUG(msg) - Show if LOG_LEVEL >= 4
 * - LOG_TRACE(msg) - Show if LOG_LEVEL >= 5
 * 
 * Each should print: [LEVEL] message
 */

#define LOG_LEVEL 3  // Change this to test different levels

// YOUR CODE HERE:
// #if LOG_LEVEL >= 1
//     #define LOG_ERROR(msg) ...
// #else
//     #define LOG_ERROR(msg)
// #endif
// ... etc

void exercise5_test() {
    cout << "\n=== EXERCISE 5: Conditional Compilation ===" << endl;
    cout << "Current LOG_LEVEL = " << LOG_LEVEL << endl;
    
    // Uncomment to test:
    /*
    LOG_ERROR("This is an error");
    LOG_WARNING("This is a warning");
    LOG_INFO("This is info");
    LOG_DEBUG("This is debug");
    LOG_TRACE("This is trace");
    */
}

// ============================================================================
// EXERCISE 6: Basic Namespaces (Beginner)
// ============================================================================
/**
 * TODO: Create three namespaces:
 * 
 * 1. Geometry namespace with:
 *    - PI constant
 *    - circleArea(radius) function
 *    - rectangleArea(width, height) function
 *    - triangleArea(base, height) function
 * 
 * 2. Conversion namespace with:
 *    - celsiusToFahrenheit(c) function
 *    - fahrenheitToCelsius(f) function
 *    - kmToMiles(km) function
 *    - milesToKm(miles) function
 * 
 * 3. Validation namespace with:
 *    - isPositive(n) function
 *    - isEven(n) function
 *    - isInRange(n, min, max) function
 */

// YOUR CODE HERE:
// namespace Geometry { ... }
// namespace Conversion { ... }
// namespace Validation { ... }

void exercise6_test() {
    cout << "\n=== EXERCISE 6: Basic Namespaces ===" << endl;
    
    // Uncomment to test:
    /*
    cout << "Geometry:" << endl;
    cout << "  Circle area (r=5): " << Geometry::circleArea(5) << endl;
    cout << "  Rectangle area (4x6): " << Geometry::rectangleArea(4, 6) << endl;
    cout << "  Triangle area (base=10, h=5): " << Geometry::triangleArea(10, 5) << endl;
    
    cout << "\nConversion:" << endl;
    cout << "  100°C = " << Conversion::celsiusToFahrenheit(100) << "°F" << endl;
    cout << "  32°F = " << Conversion::fahrenheitToCelsius(32) << "°C" << endl;
    cout << "  100km = " << Conversion::kmToMiles(100) << " miles" << endl;
    
    cout << "\nValidation:" << endl;
    cout << "  isPositive(5): " << Validation::isPositive(5) << endl;
    cout << "  isEven(6): " << Validation::isEven(6) << endl;
    cout << "  isInRange(5, 1, 10): " << Validation::isInRange(5, 1, 10) << endl;
    */
}

// ============================================================================
// EXERCISE 7: Nested Namespaces (Intermediate)
// ============================================================================
/**
 * TODO: Create a namespace structure for a game engine:
 * 
 * GameEngine::
 *   Graphics::
 *     Renderer::
 *       - init() function
 *       - render() function
 *     Texture::
 *       - load(filename) function
 *   Audio::
 *     - playSound(name) function
 *     - playMusic(name) function
 *   Physics::
 *     - update(deltaTime) function
 *     - checkCollision(obj1, obj2) function
 * 
 * Use C++17 nested namespace syntax where possible.
 * Create namespace aliases: GR for GameEngine::Graphics::Renderer
 */

// YOUR CODE HERE:

void exercise7_test() {
    cout << "\n=== EXERCISE 7: Nested Namespaces ===" << endl;
    
    // Uncomment to test:
    /*
    GameEngine::Graphics::Renderer::init();
    GameEngine::Graphics::Texture::load("player.png");
    GameEngine::Audio::playMusic("background.mp3");
    GameEngine::Physics::update(0.016);
    
    // Using alias
    GR::render();
    */
}

// ============================================================================
// EXERCISE 8: using Declarations (Intermediate)
// ============================================================================
/**
 * Given the namespace below, write a function that uses:
 * 1. Fully qualified names for some calls
 * 2. using declarations for specific items
 * 3. using directive (sparingly) in local scope
 * 
 * Demonstrate all three approaches.
 */

namespace DataProcessing {
    struct Record {
        int id;
        string name;
        double value;
    };
    
    void printRecord(const Record& r) {
        cout << "Record #" << r.id << ": " << r.name << " = " << r.value << endl;
    }
    
    double sumValues(const std::vector<Record>& records) {
        double sum = 0;
        for (const auto& r : records) sum += r.value;
        return sum;
    }
    
    Record createRecord(int id, const string& name, double value) {
        return {id, name, value};
    }
}

void exercise8_test() {
    cout << "\n=== EXERCISE 8: using Declarations ===" << endl;
    
    // TODO: Demonstrate three approaches:
    
    // Approach 1: Fully qualified names
    // DataProcessing::Record r1 = DataProcessing::createRecord(1, "Test", 100.0);
    // DataProcessing::printRecord(r1);
    
    // Approach 2: using declarations (your code here)
    
    // Approach 3: using directive in local scope (your code here)
}

// ============================================================================
// EXERCISE 9: Inline Namespaces for Versioning (Advanced)
// ============================================================================
/**
 * TODO: Create a versioned API for a Calculator library:
 * 
 * Calculator::
 *   v1:: (basic operations)
 *     - add(a, b)
 *     - subtract(a, b)
 *   
 *   v2:: (inline - current default, adds more operations)
 *     - add(a, b)
 *     - subtract(a, b)
 *     - multiply(a, b)
 *     - divide(a, b)
 *   
 *   v3:: (experimental, adds advanced operations)
 *     - add(a, b)
 *     - subtract(a, b)
 *     - multiply(a, b)
 *     - divide(a, b)
 *     - power(base, exp)
 *     - sqrt(n)
 * 
 * Make v2 the inline (default) namespace.
 */

// YOUR CODE HERE:

void exercise9_test() {
    cout << "\n=== EXERCISE 9: Inline Namespaces for Versioning ===" << endl;
    
    // Uncomment to test:
    /*
    // Uses v2 (default)
    cout << "Default (v2):" << endl;
    cout << "  add(5, 3) = " << Calculator::add(5, 3) << endl;
    cout << "  multiply(4, 7) = " << Calculator::multiply(4, 7) << endl;
    
    // Explicitly use v1
    cout << "\nExplicit v1:" << endl;
    cout << "  add(5, 3) = " << Calculator::v1::add(5, 3) << endl;
    // multiply doesn't exist in v1
    
    // Explicitly use v3 (experimental)
    cout << "\nExplicit v3 (experimental):" << endl;
    cout << "  power(2, 10) = " << Calculator::v3::power(2, 10) << endl;
    cout << "  sqrt(16) = " << Calculator::v3::sqrt(16) << endl;
    */
}

// ============================================================================
// EXERCISE 10: Debug Macro System (Advanced)
// ============================================================================
/**
 * TODO: Create a comprehensive debug macro system with:
 * 
 * 1. ASSERT(condition) - If false, print condition, file, line, function and abort
 * 2. ASSERT_MSG(condition, message) - Same as above but with custom message
 * 3. REQUIRE(condition) - Like assert but throws exception instead of abort
 * 4. ENSURE(condition) - Post-condition check (same as REQUIRE)
 * 5. UNREACHABLE() - Mark code that should never be reached
 * 6. TODO(message) - Print TODO reminder during compilation (use #pragma message)
 * 7. DEPRECATED(message) - Mark something as deprecated
 * 
 * All should be disabled when NDEBUG is defined.
 */

// YOUR CODE HERE:

void exercise10_test() {
    cout << "\n=== EXERCISE 10: Debug Macro System ===" << endl;
    
    // Uncomment to test:
    /*
    int x = 10;
    ASSERT(x > 0);
    ASSERT_MSG(x < 100, "x must be less than 100");
    
    try {
        REQUIRE(x != 0);
        int result = 100 / x;
        ENSURE(result > 0);
    } catch (const std::exception& e) {
        cout << "Exception: " << e.what() << endl;
    }
    */
}

// ============================================================================
// EXERCISE 11: Header Guard Simulation (Beginner)
// ============================================================================
/**
 * TODO: Write the proper header guard structure for these scenarios.
 * Just write the preprocessor directives (no actual code needed).
 * 
 * 1. Traditional #ifndef style for: graphics/renderer.h
 * 2. Traditional #ifndef style for: utils/string_utils.hpp
 * 3. Modern #pragma once style
 * 
 * Write your answers as comments below.
 */

void exercise11_test() {
    cout << "\n=== EXERCISE 11: Header Guard Simulation ===" << endl;
    cout << "Check your answers in the comments below this function." << endl;
    
    // Answer 1 (graphics/renderer.h):
    /*
    #ifndef GRAPHICS_RENDERER_H
    #define GRAPHICS_RENDERER_H
    
    // header content
    
    #endif // GRAPHICS_RENDERER_H
    */
    
    // Answer 2 (utils/string_utils.hpp):
    // YOUR ANSWER HERE
    
    // Answer 3 (modern style):
    // YOUR ANSWER HERE
}

// ============================================================================
// EXERCISE 12: Platform-Specific Code (Advanced)
// ============================================================================
/**
 * TODO: Write preprocessor conditionals that:
 * 1. Define PLATFORM_NAME as "Windows", "Linux", "macOS", or "Unknown"
 * 2. Define IS_64BIT as true or false
 * 3. Define PATH_SEPARATOR as '\\' on Windows, '/' elsewhere
 * 4. Define NEWLINE as "\r\n" on Windows, "\n" elsewhere
 * 5. Create a macro SLEEP_MS(ms) that calls the appropriate sleep function
 *    (Sleep on Windows, usleep on Unix-like)
 */

// YOUR CODE HERE:

void exercise12_test() {
    cout << "\n=== EXERCISE 12: Platform-Specific Code ===" << endl;
    
    // Uncomment to test:
    /*
    cout << "Platform: " << PLATFORM_NAME << endl;
    cout << "64-bit: " << (IS_64BIT ? "Yes" : "No") << endl;
    cout << "Path separator: '" << PATH_SEPARATOR << "'" << endl;
    cout << "Sleeping for 100ms..." << endl;
    SLEEP_MS(100);
    cout << "Done!" << endl;
    */
}

// ============================================================================
// CHALLENGE: Create Your Own Mini Testing Framework (Expert)
// ============================================================================
/**
 * TODO: Create a simple unit testing framework using macros:
 * 
 * TEST_CASE(name) { ... }     - Define a test case
 * EXPECT_TRUE(condition)      - Check condition is true
 * EXPECT_FALSE(condition)     - Check condition is false
 * EXPECT_EQ(a, b)            - Check a == b
 * EXPECT_NE(a, b)            - Check a != b
 * EXPECT_LT(a, b)            - Check a < b
 * EXPECT_LE(a, b)            - Check a <= b
 * EXPECT_GT(a, b)            - Check a > b
 * EXPECT_GE(a, b)            - Check a >= b
 * EXPECT_THROW(expr, type)   - Check that expr throws type
 * RUN_ALL_TESTS()            - Run all registered tests
 * 
 * Each EXPECT should:
 * - Print PASS or FAIL
 * - Show file:line on failure
 * - Show actual vs expected values on failure
 * 
 * Hint: Use a vector to store test function pointers.
 */

// YOUR CODE HERE:

void exercise_challenge_test() {
    cout << "\n=== CHALLENGE: Mini Testing Framework ===" << endl;
    
    // Uncomment when implemented:
    /*
    TEST_CASE(MathTests) {
        EXPECT_EQ(2 + 2, 4);
        EXPECT_NE(1 + 1, 3);
        EXPECT_TRUE(5 > 3);
        EXPECT_FALSE(2 > 5);
        EXPECT_LT(1, 2);
        EXPECT_LE(2, 2);
        EXPECT_GT(5, 3);
        EXPECT_GE(3, 3);
    }
    
    TEST_CASE(StringTests) {
        string s = "hello";
        EXPECT_EQ(s.length(), 5);
        EXPECT_TRUE(s.find("ell") != string::npos);
    }
    
    RUN_ALL_TESTS();
    */
}

// ============================================================================
// MAIN
// ============================================================================

int main() {
    cout << "╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║    PREPROCESSOR & NAMESPACES - EXERCISES                     ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    
    exercise1_test();
    exercise2_test();
    exercise3_test();
    exercise4_test();
    exercise5_test();
    exercise6_test();
    exercise7_test();
    exercise8_test();
    exercise9_test();
    exercise10_test();
    exercise11_test();
    exercise12_test();
    exercise_challenge_test();
    
    cout << "\n═══════════════════════════════════════════════════════════════" << endl;
    cout << "Complete the exercises by implementing the TODO sections!" << endl;
    cout << "Uncomment the test code after implementing each exercise." << endl;
    
    return 0;
}
Exercises - C++ Tutorial | DeepML