cpp

Advanced Templates

03_Advanced_Templatesāš™ļø
/**
 * ============================================================
 * C++ TEMPLATE ADVANCED TOPICS
 * ============================================================
 * 
 * This file covers:
 * - Template metaprogramming basics
 * - SFINAE (Substitution Failure Is Not An Error)
 * - Type traits
 * - constexpr with templates
 * - Template aliases
 * - CRTP (Curiously Recurring Template Pattern)
 * 
 * Compile: g++ -std=c++17 -Wall 01_advanced_templates.cpp -o adv_templates
 * Run: ./adv_templates
 * 
 * ============================================================
 */

#include <iostream>
#include <string>
#include <type_traits>
#include <vector>
#include <array>

using namespace std;

// ============================================================
// PART 1: TEMPLATE METAPROGRAMMING - COMPILE TIME COMPUTATION
// ============================================================

// Factorial at compile time
template <unsigned int N>
struct Factorial {
    static constexpr unsigned long long value = N * Factorial<N-1>::value;
};

// Base case specialization
template <>
struct Factorial<0> {
    static constexpr unsigned long long value = 1;
};

// Fibonacci at compile time
template <unsigned int N>
struct Fibonacci {
    static constexpr unsigned long long value = 
        Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};

template <>
struct Fibonacci<0> {
    static constexpr unsigned long long value = 0;
};

template <>
struct Fibonacci<1> {
    static constexpr unsigned long long value = 1;
};

// Power at compile time
template <int Base, unsigned int Exp>
struct Power {
    static constexpr long long value = Base * Power<Base, Exp-1>::value;
};

template <int Base>
struct Power<Base, 0> {
    static constexpr long long value = 1;
};

// ============================================================
// PART 2: TYPE TRAITS - COMPILE TIME TYPE INFORMATION
// ============================================================

// Custom type traits
template <typename T>
struct IsPointer {
    static constexpr bool value = false;
};

template <typename T>
struct IsPointer<T*> {
    static constexpr bool value = true;
};

template <typename T>
struct RemovePointer {
    using type = T;
};

template <typename T>
struct RemovePointer<T*> {
    using type = T;
};

// Check if two types are the same
template <typename T, typename U>
struct IsSame {
    static constexpr bool value = false;
};

template <typename T>
struct IsSame<T, T> {
    static constexpr bool value = true;
};

// ============================================================
// PART 3: SFINAE - ENABLE/DISABLE TEMPLATES
// ============================================================

// Using enable_if to enable for specific types
template <typename T>
typename enable_if<is_integral<T>::value, T>::type
doubleValue(T value) {
    cout << "Integer version: ";
    return value * 2;
}

template <typename T>
typename enable_if<is_floating_point<T>::value, T>::type
doubleValue(T value) {
    cout << "Float version: ";
    return value * 2.0;
}

// SFINAE with decltype
template <typename T>
auto hasSize(int) -> decltype(declval<T>().size(), true_type{}) {
    return {};
}

template <typename T>
false_type hasSize(...) {
    return {};
}

// More readable with void_t (C++17)
template <typename T, typename = void>
struct HasBeginEnd : false_type {};

template <typename T>
struct HasBeginEnd<T, void_t<decltype(declval<T>().begin()),
                             decltype(declval<T>().end())>> 
    : true_type {};

// ============================================================
// PART 4: TEMPLATE ALIASES
// ============================================================

// Simple alias
template <typename T>
using Ptr = T*;

template <typename T>
using Vec = vector<T>;

// Alias for complex types
template <typename K, typename V>
using StringMap = vector<pair<K, V>>;

// Alias with enable_if
template <typename T>
using EnableIfNumeric = enable_if_t<is_arithmetic_v<T>, T>;

// ============================================================
// PART 5: CRTP - CURIOUSLY RECURRING TEMPLATE PATTERN
// ============================================================

// Static polymorphism using CRTP
template <typename Derived>
class Comparable {
public:
    bool operator!=(const Derived& other) const {
        return !(static_cast<const Derived&>(*this) == other);
    }
    
    bool operator<=(const Derived& other) const {
        return !(other < static_cast<const Derived&>(*this));
    }
    
    bool operator>(const Derived& other) const {
        return other < static_cast<const Derived&>(*this);
    }
    
    bool operator>=(const Derived& other) const {
        return !(static_cast<const Derived&>(*this) < other);
    }
};

class Point : public Comparable<Point> {
public:
    int x, y;
    
    Point(int x = 0, int y = 0) : x(x), y(y) {}
    
    // Only need to implement == and <
    bool operator==(const Point& other) const {
        return x == other.x && y == other.y;
    }
    
    bool operator<(const Point& other) const {
        if (x != other.x) return x < other.x;
        return y < other.y;
    }
};

// CRTP for static interface
template <typename Derived>
class Shape {
public:
    double area() const {
        return static_cast<const Derived*>(this)->areaImpl();
    }
    
    void draw() const {
        static_cast<const Derived*>(this)->drawImpl();
    }
};

class Circle : public Shape<Circle> {
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double areaImpl() const {
        return 3.14159 * radius * radius;
    }
    
    void drawImpl() const {
        cout << "Drawing circle with radius " << radius << endl;
    }
};

class Square : public Shape<Square> {
    double side;
public:
    Square(double s) : side(s) {}
    
    double areaImpl() const {
        return side * side;
    }
    
    void drawImpl() const {
        cout << "Drawing square with side " << side << endl;
    }
};

// ============================================================
// PART 6: CONSTEXPR WITH TEMPLATES
// ============================================================

template <typename T, size_t N>
constexpr T arraySum(const T (&arr)[N]) {
    T sum = 0;
    for (size_t i = 0; i < N; i++) {
        sum += arr[i];
    }
    return sum;
}

template <typename T>
constexpr T constexprMax(T a, T b) {
    return (a > b) ? a : b;
}

// Compile-time string length
constexpr size_t strLen(const char* str) {
    return (*str == '\0') ? 0 : 1 + strLen(str + 1);
}

// ============================================================
// PART 7: IF CONSTEXPR (C++17)
// ============================================================

template <typename T>
auto processValue(T value) {
    if constexpr (is_integral_v<T>) {
        cout << "Processing integer: " << value << endl;
        return value * 2;
    } else if constexpr (is_floating_point_v<T>) {
        cout << "Processing float: " << value << endl;
        return value * 2.5;
    } else if constexpr (is_same_v<T, string>) {
        cout << "Processing string: " << value << endl;
        return value + value;
    } else {
        cout << "Processing unknown type" << endl;
        return value;
    }
}

// ============================================================
// MAIN FUNCTION
// ============================================================

int main() {
    cout << "============================================" << endl;
    cout << "     C++ ADVANCED TEMPLATES" << endl;
    cout << "============================================" << endl << endl;

    // ========================================================
    // DEMO 1: Template Metaprogramming
    // ========================================================
    
    cout << "--- DEMO 1: COMPILE-TIME COMPUTATION ---" << endl << endl;
    
    cout << "Factorial<0> = " << Factorial<0>::value << endl;
    cout << "Factorial<5> = " << Factorial<5>::value << endl;
    cout << "Factorial<10> = " << Factorial<10>::value << endl;
    cout << "Factorial<20> = " << Factorial<20>::value << endl;
    
    cout << "\nFibonacci<0> = " << Fibonacci<0>::value << endl;
    cout << "Fibonacci<10> = " << Fibonacci<10>::value << endl;
    cout << "Fibonacci<20> = " << Fibonacci<20>::value << endl;
    
    cout << "\nPower<2, 10> = " << Power<2, 10>::value << endl;
    cout << "Power<3, 5> = " << Power<3, 5>::value << endl;
    
    cout << "\nšŸ’” All computed at COMPILE TIME!" << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 2: Custom Type Traits
    // ========================================================
    
    cout << "--- DEMO 2: CUSTOM TYPE TRAITS ---" << endl << endl;
    
    cout << "IsPointer<int>::value = " << IsPointer<int>::value << endl;
    cout << "IsPointer<int*>::value = " << IsPointer<int*>::value << endl;
    cout << "IsPointer<string>::value = " << IsPointer<string>::value << endl;
    cout << "IsPointer<string*>::value = " << IsPointer<string*>::value << endl;
    
    cout << "\nIsSame<int, int>::value = " << IsSame<int, int>::value << endl;
    cout << "IsSame<int, double>::value = " << IsSame<int, double>::value << endl;
    
    cout << "\nRemovePointer<int*>::type is int? " 
         << IsSame<RemovePointer<int*>::type, int>::value << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 3: Standard Type Traits
    // ========================================================
    
    cout << "--- DEMO 3: STANDARD TYPE TRAITS ---" << endl << endl;
    
    cout << "is_integral<int>::value = " << is_integral<int>::value << endl;
    cout << "is_integral<double>::value = " << is_integral<double>::value << endl;
    cout << "is_floating_point<double>::value = " << is_floating_point<double>::value << endl;
    
    cout << "\nis_pointer<int>::value = " << is_pointer<int>::value << endl;
    cout << "is_pointer<int*>::value = " << is_pointer<int*>::value << endl;
    
    cout << "\nis_class<string>::value = " << is_class<string>::value << endl;
    cout << "is_class<int>::value = " << is_class<int>::value << endl;
    
    cout << "\nis_same<int, int32_t>::value = " << is_same<int, int32_t>::value << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 4: SFINAE
    // ========================================================
    
    cout << "--- DEMO 4: SFINAE ---" << endl << endl;
    
    cout << doubleValue(5) << endl;      // Integer version
    cout << doubleValue(3.14) << endl;   // Float version
    
    cout << "\nhasSize<vector<int>>: " << decltype(hasSize<vector<int>>(0))::value << endl;
    cout << "hasSize<int>: " << decltype(hasSize<int>(0))::value << endl;
    
    cout << "\nHasBeginEnd<vector<int>>: " << HasBeginEnd<vector<int>>::value << endl;
    cout << "HasBeginEnd<int>: " << HasBeginEnd<int>::value << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 5: Template Aliases
    // ========================================================
    
    cout << "--- DEMO 5: TEMPLATE ALIASES ---" << endl << endl;
    
    Ptr<int> intPtr = new int(42);
    Vec<string> names = {"Alice", "Bob", "Charlie"};
    
    cout << "Ptr<int> value: " << *intPtr << endl;
    cout << "Vec<string> contents: ";
    for (const auto& name : names) {
        cout << name << " ";
    }
    cout << endl;
    
    delete intPtr;
    
    cout << endl;

    // ========================================================
    // DEMO 6: CRTP
    // ========================================================
    
    cout << "--- DEMO 6: CRTP ---" << endl << endl;
    
    Point p1(3, 4), p2(3, 5), p3(3, 4);
    
    cout << "p1 == p3: " << (p1 == p3) << endl;
    cout << "p1 != p2: " << (p1 != p2) << endl;
    cout << "p1 < p2: " << (p1 < p2) << endl;
    cout << "p1 <= p2: " << (p1 <= p2) << endl;
    cout << "p1 > p2: " << (p1 > p2) << endl;
    
    cout << "\nShapes:" << endl;
    Circle c(5.0);
    Square s(4.0);
    
    c.draw();
    cout << "Circle area: " << c.area() << endl;
    
    s.draw();
    cout << "Square area: " << s.area() << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 7: constexpr with Templates
    // ========================================================
    
    cout << "--- DEMO 7: CONSTEXPR ---" << endl << endl;
    
    constexpr int arr[] = {1, 2, 3, 4, 5};
    constexpr auto sum = arraySum(arr);
    cout << "constexpr arraySum = " << sum << endl;
    
    constexpr auto maxVal = constexprMax(10, 20);
    cout << "constexpr max(10, 20) = " << maxVal << endl;
    
    constexpr auto len = strLen("Hello");
    cout << "constexpr strLen(\"Hello\") = " << len << endl;
    
    // Use in static_assert
    static_assert(Factorial<5>::value == 120, "Factorial failed!");
    static_assert(strLen("test") == 4, "strLen failed!");
    
    cout << "\nšŸ’” All verified at compile time with static_assert!" << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 8: if constexpr
    // ========================================================
    
    cout << "--- DEMO 8: IF CONSTEXPR ---" << endl << endl;
    
    auto r1 = processValue(42);
    cout << "Result: " << r1 << endl;
    
    auto r2 = processValue(3.14);
    cout << "Result: " << r2 << endl;
    
    auto r3 = processValue(string("Hello"));
    cout << "Result: " << r3 << endl;
    
    cout << endl;

    // ========================================================
    // SUMMARY
    // ========================================================
    
    cout << "--- ADVANCED TEMPLATE CONCEPTS ---" << endl << endl;
    
    cout << "Template Metaprogramming:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Computation at compile time" << endl;
    cout << "• Uses recursion with specialization" << endl;
    cout << "• Results embedded in executable" << endl;
    
    cout << "\nSFINAE:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Substitution Failure Is Not An Error" << endl;
    cout << "• Enable/disable templates based on types" << endl;
    cout << "• Use enable_if, void_t, decltype" << endl;
    
    cout << "\nCRTP:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Static polymorphism (no vtable)" << endl;
    cout << "• class Derived : public Base<Derived>" << endl;
    cout << "• Mixin functionality" << endl;
    
    cout << endl;

    cout << "============================================" << endl;
    cout << "ADVANCED TEMPLATES COMPLETE!" << endl;
    cout << "============================================" << endl;

    return 0;
}

// ============================================================
// EXERCISES:
// ============================================================
/*
 * 1. Implement compile-time:
 *    - IsPrime<N> - check if N is prime
 *    - GCD<A, B> - greatest common divisor
 *    - NthPrime<N> - Nth prime number
 * 
 * 2. Create type traits:
 *    - IsContainer<T> - has begin(), end(), size()
 *    - IsCallable<T> - can be called with operator()
 *    - RemoveConst<T>, AddConst<T>
 * 
 * 3. Implement CRTP for:
 *    - Singleton pattern
 *    - Object counter (track instances)
 *    - Cloneable interface
 * 
 * 4. Use SFINAE to create:
 *    - toString() for any type (numeric, string, container)
 *    - serialize() that handles built-in and custom types
 */
Advanced Templates - C++ Tutorial | DeepML