cpp
exercises
exercises.cpp⚙️cpp
/**
* Modern C++ Features - Exercises (C++17/20/23)
*
* Compile:
* C++17: g++ -std=c++17 -Wall exercises.cpp -o exercises
* C++20: g++ -std=c++20 -Wall exercises.cpp -o exercises
* C++23: g++ -std=c++23 -Wall exercises.cpp -o exercises
*/
#include <iostream>
#include <vector>
#include <memory>
#include <optional>
#include <variant>
#include <string>
#include <string_view>
#include <map>
#include <algorithm>
#include <functional>
#include <type_traits>
#include <numeric>
#include <array>
using namespace std;
// ============================================================
// Exercise 1: Smart Pointer Factory ⭐⭐
// ============================================================
// TODO: Create factory function that returns unique_ptr
// to different shapes based on type string
void exercise1() {
// auto shape = createShape("circle");
// shape->draw(); // "Drawing circle"
cout << "(Implement shape factory with unique_ptr)" << endl;
}
// ============================================================
// Exercise 2: Lambda Sorter ⭐⭐
// ============================================================
// TODO: Create function that takes vector and sort criteria lambda
// Sort by different criteria: by value, by abs, by last digit
void exercise2() {
// vector<int> v = {-5, 2, -3, 1, -4};
// sortBy(v, /* lambda for ascending */);
// sortBy(v, /* lambda for abs value */);
cout << "(Implement generic sortBy with lambdas)" << endl;
}
// ============================================================
// Exercise 3: Optional Chain ⭐⭐
// ============================================================
// TODO: Create functions that return optional and chain them
// User -> getAddress() -> getZipCode() -> optional<string>
void exercise3() {
// auto zip = getUser(1)
// .and_then(getAddress)
// .and_then(getZipCode);
cout << "(Implement optional chaining)" << endl;
}
// ============================================================
// Exercise 4: Variant Calculator ⭐⭐⭐
// ============================================================
// TODO: Use variant to represent different operation types
// variant<Add, Sub, Mul, Div> and visit to calculate
void exercise4() {
// Operation op = Add{};
// cout << calculate(op, 10, 5); // 15
// op = Mul{};
// cout << calculate(op, 10, 5); // 50
cout << "(Implement variant calculator)" << endl;
}
// ============================================================
// Exercise 5: Move-Only Type ⭐⭐⭐
// ============================================================
// TODO: Create class that can only be moved, not copied
// Like unique_ptr but for a custom resource
void exercise5() {
// UniqueResource r1("file.txt");
// UniqueResource r2 = r1; // ERROR: deleted
// UniqueResource r3 = move(r1); // OK
cout << "(Implement move-only type)" << endl;
}
// ============================================================
// Exercise 6: String_View Parser ⭐⭐⭐
// ============================================================
// TODO: Parse CSV line using string_view (no allocations)
// Return vector of string_view for each field
void exercise6() {
// string line = "name,age,city";
// auto fields = parseCSV(line);
// fields[0] = "name", fields[1] = "age", etc.
cout << "(Implement string_view CSV parser)" << endl;
}
// ============================================================
// Exercise 7: Constexpr Math ⭐⭐⭐
// ============================================================
// TODO: Create constexpr functions:
// - power(base, exp)
// - isPrime(n)
// - gcd(a, b)
void exercise7() {
// constexpr int p = power(2, 10); // 1024
// constexpr bool prime = isPrime(17); // true
// constexpr int g = gcd(48, 18); // 6
cout << "(Implement constexpr math functions)" << endl;
}
// ============================================================
// Exercise 8: Smart Container ⭐⭐⭐
// ============================================================
// TODO: Create container that stores shared_ptr to items
// and tracks weak_ptr references for cache behavior
void exercise8() {
// SmartCache<Widget> cache;
// auto w1 = cache.get(1); // Creates new
// auto w2 = cache.get(1); // Returns same
// cache.cleanup(); // Remove expired
cout << "(Implement smart container with weak_ptr)" << endl;
}
// ============================================================
// Exercise 9: Concept-Constrained Container ⭐⭐⭐ (C++20)
// ============================================================
// TODO: Create a container class that only accepts numeric types
// Use concepts to constrain the template parameter
// Methods: add(), sum(), average(), min(), max()
void exercise9() {
// NumericContainer<int> ints;
// ints.add(1).add(2).add(3);
// cout << ints.sum(); // 6
// cout << ints.average(); // 2.0
// NumericContainer<string> strs; // Should fail to compile!
cout << "(Implement concept-constrained numeric container)" << endl;
}
// ============================================================
// Exercise 10: Range Pipeline ⭐⭐⭐ (C++20)
// ============================================================
// TODO: Create custom range adaptors/views:
// 1. squared - transforms each element to its square
// 2. evens_only - filters only even numbers
// 3. clamped(min, max) - clamps values to range
void exercise10() {
// vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
//
// C++20 style:
// auto result = v
// | evens_only()
// | squared()
// | clamped(0, 50);
// // Result: {4, 16, 36, 50, 50} (64 and 100 clamped to 50)
cout << "(Implement custom range adaptors)" << endl;
}
// ============================================================
// Exercise 11: Expected-Based Parser ⭐⭐⭐ (C++23)
// ============================================================
// TODO: Create a JSON-like parser that returns expected
// Parse: {"name": "John", "age": 30}
// Return expected<JsonObject, ParseError>
void exercise11() {
// auto result = parseJson("{\"name\": \"John\"}");
// if (result) {
// cout << result->get<string>("name"); // "John"
// } else {
// cout << "Error: " << result.error().message;
// }
cout << "(Implement expected-based JSON parser)" << endl;
}
// ============================================================
// Exercise 12: Coroutine Generator ⭐⭐⭐⭐ (C++20)
// ============================================================
// TODO: Create a generator coroutine that yields:
// 1. Fibonacci sequence
// 2. Prime numbers
// 3. Custom range with step
void exercise12() {
// for (int n : fibonacci_generator() | take(10)) {
// cout << n << " "; // 0 1 1 2 3 5 8 13 21 34
// }
//
// for (int p : prime_generator() | take(5)) {
// cout << p << " "; // 2 3 5 7 11
// }
cout << "(Implement coroutine generators)" << endl;
}
// ============================================================
// Exercise 13: Span-Based Matrix ⭐⭐⭐ (C++20)
// ============================================================
// TODO: Create a matrix class that uses span for views
// Support row/column views without copying
void exercise13() {
// int data[] = {1,2,3,4,5,6,7,8,9};
// Matrix<3,3> mat(data);
//
// auto row1 = mat.row(0); // span<int, 3>: {1,2,3}
// auto col1 = mat.column(0); // {1,4,7} (may need copying)
// auto sub = mat.submatrix(0,0,2,2); // Top-left 2x2
cout << "(Implement span-based matrix views)" << endl;
}
// ============================================================
// Exercise 14: Three-Way Comparison ⭐⭐ (C++20)
// ============================================================
// TODO: Implement spaceship operator for:
// 1. Semantic version (major.minor.patch)
// 2. Case-insensitive string comparison
// 3. Partial ordering for floating point with NaN handling
void exercise14() {
// SemVer v1{1, 2, 3}, v2{1, 3, 0};
// cout << (v1 < v2); // true
// cout << (v1 == v2); // false
//
// CIString s1{"Hello"}, s2{"HELLO"};
// cout << (s1 == s2); // true (case-insensitive)
cout << "(Implement three-way comparison operators)" << endl;
}
// ============================================================
// Exercise 15: Format String Builder ⭐⭐⭐ (C++20)
// ============================================================
// TODO: Create a custom type that can be formatted with std::format
// Support format specifiers like width, alignment, fill character
void exercise15() {
// Money m{123, 45}; // $123.45
//
// cout << format("{}", m); // "$123.45"
// cout << format("{:>15}", m); // " $123.45"
// cout << format("{:*^15}", m); // "****$123.45****"
// cout << format("{:.0}", m); // "$123" (no cents)
cout << "(Implement custom std::format support)" << endl;
}
// ============================================================
// MAIN
// ============================================================
int main() {
cout << "╔══════════════════════════════════════════════════════════════╗" << endl;
cout << "║ MODERN C++ EXERCISES (C++17/20/23) ║" << endl;
cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
cout << "\n=== C++17 Exercises ===" << endl;
cout << "\nEx1: Smart Pointer Factory" << endl;
exercise1();
cout << "\nEx2: Lambda Sorter" << endl;
exercise2();
cout << "\nEx3: Optional Chain" << endl;
exercise3();
cout << "\nEx4: Variant Calculator" << endl;
exercise4();
cout << "\nEx5: Move-Only Type" << endl;
exercise5();
cout << "\nEx6: String_View Parser" << endl;
exercise6();
cout << "\nEx7: Constexpr Math" << endl;
exercise7();
cout << "\nEx8: Smart Container" << endl;
exercise8();
cout << "\n=== C++20 Exercises ===" << endl;
cout << "\nEx9: Concept-Constrained Container" << endl;
exercise9();
cout << "\nEx10: Range Pipeline" << endl;
exercise10();
cout << "\nEx12: Coroutine Generator" << endl;
exercise12();
cout << "\nEx13: Span-Based Matrix" << endl;
exercise13();
cout << "\nEx14: Three-Way Comparison" << endl;
exercise14();
cout << "\nEx15: Format String Builder" << endl;
exercise15();
cout << "\n=== C++23 Exercises ===" << endl;
cout << "\nEx11: Expected-Based Parser" << endl;
exercise11();
return 0;
}
// ============================================================
// ANSWERS
// ============================================================
/*
Ex1:
struct Shape { virtual void draw() = 0; virtual ~Shape() = default; };
struct Circle : Shape { void draw() override { cout << "Circle" << endl; } };
struct Square : Shape { void draw() override { cout << "Square" << endl; } };
unique_ptr<Shape> createShape(string_view type) {
if (type == "circle") return make_unique<Circle>();
if (type == "square") return make_unique<Square>();
return nullptr;
}
Ex2:
template<typename Compare>
void sortBy(vector<int>& v, Compare comp) {
sort(v.begin(), v.end(), comp);
}
// Usage:
sortBy(v, [](int a, int b) { return a < b; });
sortBy(v, [](int a, int b) { return abs(a) < abs(b); });
Ex5:
class UniqueResource {
string resource;
public:
UniqueResource(string r) : resource(move(r)) {}
UniqueResource(const UniqueResource&) = delete;
UniqueResource& operator=(const UniqueResource&) = delete;
UniqueResource(UniqueResource&& o) noexcept : resource(move(o.resource)) {}
UniqueResource& operator=(UniqueResource&& o) noexcept {
resource = move(o.resource);
return *this;
}
};
Ex6:
vector<string_view> parseCSV(string_view line) {
vector<string_view> fields;
size_t start = 0;
while (start < line.size()) {
size_t end = line.find(',', start);
if (end == string_view::npos) end = line.size();
fields.push_back(line.substr(start, end - start));
start = end + 1;
}
return fields;
}
Ex7:
constexpr int power(int base, int exp) {
int result = 1;
for (int i = 0; i < exp; i++) result *= base;
return result;
}
constexpr bool isPrime(int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) return false;
return true;
}
constexpr int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
Ex9: Concept-Constrained Container (C++20)
#include <concepts>
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
template<Numeric T>
class NumericContainer {
std::vector<T> data_;
public:
NumericContainer& add(T value) {
data_.push_back(value);
return *this;
}
T sum() const {
return std::accumulate(data_.begin(), data_.end(), T{});
}
double average() const {
return static_cast<double>(sum()) / data_.size();
}
T min() const {
return *std::min_element(data_.begin(), data_.end());
}
T max() const {
return *std::max_element(data_.begin(), data_.end());
}
};
Ex14: Three-Way Comparison (C++20)
#include <compare>
struct SemVer {
int major, minor, patch;
auto operator<=>(const SemVer&) const = default;
// Generates all 6 comparison operators!
};
struct CIString {
std::string value;
std::weak_ordering operator<=>(const CIString& other) const {
auto toLower = [](char c) { return std::tolower(c); };
auto it1 = value.begin(), it2 = other.value.begin();
while (it1 != value.end() && it2 != other.value.end()) {
auto c1 = toLower(*it1++), c2 = toLower(*it2++);
if (c1 < c2) return std::weak_ordering::less;
if (c1 > c2) return std::weak_ordering::greater;
}
if (value.size() < other.value.size()) return std::weak_ordering::less;
if (value.size() > other.value.size()) return std::weak_ordering::greater;
return std::weak_ordering::equivalent;
}
bool operator==(const CIString& other) const {
return (*this <=> other) == std::weak_ordering::equivalent;
}
};
Ex15: Format String Builder (C++20)
#include <format>
struct Money {
int dollars;
int cents;
};
template<>
struct std::formatter<Money> {
bool showCents = true;
constexpr auto parse(std::format_parse_context& ctx) {
auto it = ctx.begin();
if (it != ctx.end() && *it == '.') {
++it;
if (it != ctx.end() && *it == '0') {
showCents = false;
++it;
}
}
return it;
}
auto format(const Money& m, std::format_context& ctx) const {
if (showCents) {
return std::format_to(ctx.out(), "${}.{:02}", m.dollars, m.cents);
}
return std::format_to(ctx.out(), "${}", m.dollars);
}
};
*/