cpp
exercises
exercises.cpp⚙️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;
}