typetui 0.2.1

A terminal-based typing test.
Documentation
#include <iostream>
#include <type_traits>
#include <tuple>

template<typename T>
struct TypeInfo {
    static constexpr bool is_pointer = std::is_pointer_v<T>;
    static constexpr bool is_reference = std::is_reference_v<T>;
    static constexpr bool is_const = std::is_const_v<std::remove_reference_t<T>>;
    static constexpr size_t size = sizeof(T);
    
    using remove_pointer = std::remove_pointer_t<T>;
    using remove_reference = std::remove_reference_t<T>;
    using remove_const = std::remove_const_t<std::remove_reference_t<T>>;
};

template<typename... Ts>
struct TypeList {
    static constexpr size_t size = sizeof...(Ts);
    
    template<template<typename> typename F>
    using transform = TypeList<F<Ts>...>;
    
    template<typename T>
    using append = TypeList<Ts..., T>;
    
    template<typename T>
    using prepend = TypeList<T, Ts...>;
};

template<typename T>
struct is_container : std::false_type {};

template<typename T, typename Alloc>
struct is_container<std::vector<T, Alloc>> : std::true_type {};

template<typename T, typename Alloc>
struct is_container<std::list<T, Alloc>> : std::true_type {};

template<typename T>
inline constexpr bool is_container_v = is_container<T>::value;

template<typename T>
void process_if_container(T&& value) {
    if constexpr (is_container_v<std::decay_t<T>>) {
        std::cout << "Processing container of size: " << value.size() << "\n";
    } else {
        std::cout << "Not a container\n";
    }
}

template<int N>
struct Factorial {
    static constexpr int value = N * Factorial<N - 1>::value;
};

template<>
struct Factorial<0> {
    static constexpr int value = 1;
};

template<int N>
constexpr int factorial_v = Factorial<N>::value;

template<size_t N>
struct Fibonacci {
    static constexpr size_t value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;
};

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

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

template<size_t N>
constexpr size_t fibonacci_v = Fibonacci<N>::value;

template<typename... Args>
constexpr size_t count_args = sizeof...(Args);

template<typename... Args>
constexpr bool all_same_types() {
    return (std::is_same_v<Args, std::tuple_element_t<0, std::tuple<Args...>>> && ...);
}

template<typename... Args>
constexpr auto sum_args(Args... args) {
    return (args + ...);
}

template<typename F, typename... Args>
decltype(auto) invoke(F&& f, Args&&... args) {
    return std::forward<F>(f)(std::forward<Args>(args)...);
}

template<typename T>
class Singleton {
public:
    static T& instance() {
        static T instance;
        return instance;
    }
    
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
protected:
    Singleton() = default;
};

class Logger : public Singleton<Logger> {
    friend class Singleton<Logger>;
public:
    void log(const std::string& msg) {
        std::cout << "[LOG] " << msg << "\n";
    }
};

template<typename Derived>
class Cloneable {
public:
    virtual ~Cloneable() = default;
    
    std::unique_ptr<Derived> clone() const {
        return std::unique_ptr<Derived>(static_cast<Derived*>(clone_impl()));
    }
    
protected:
    virtual Cloneable* clone_impl() const = 0;
};

class Document : public Cloneable<Document> {
    std::string content;
    
public:
    explicit Document(std::string c) : content(std::move(c)) {}
    
    Document* clone_impl() const override {
        return new Document(content);
    }
    
    void print() const {
        std::cout << "Document: " << content << "\n";
    }
};

template<auto Value>
struct IntegralConstant {
    static constexpr auto value = Value;
};

template<size_t Index, typename... Types>
struct TypeAt;

template<typename First, typename... Rest>
struct TypeAt<0, First, Rest...> {
    using type = First;
};

template<size_t Index, typename First, typename... Rest>
struct TypeAt<Index, First, Rest...> {
    using type = typename TypeAt<Index - 1, Rest...>::type;
};

template<size_t Index, typename... Types>
using type_at_t = typename TypeAt<Index, Types...>::type;

template<typename... Types>
struct TypeCounter {
    template<typename T>
    static constexpr size_t count() {
        return ((std::is_same_v<T, Types> ? 1 : 0) + ...);
    }
};

template<typename T, typename... Args>
constexpr bool is_one_of = (std::is_same_v<T, Args> || ...);

template<typename T>
concept Arithmetic = std::is_arithmetic_v<T>;

template<typename T>
concept Container = requires(T t) {
    { t.begin() } -> std::input_or_output_iterator;
    { t.end() } -> std::input_or_output_iterator;
    { t.size() } -> std::convertible_to<size_t>;
};

template<Arithmetic T>
T add(T a, T b) {
    return a + b;
}

template<Container C>
auto sum_container(const C& container) -> typename C::value_type {
    typename C::value_type sum{};
    for (const auto& item : container) {
        sum += item;
    }
    return sum;
}

int main() {
    static_assert(factorial_v<5> == 120);
    static_assert(fibonacci_v<10> == 55);
    
    std::cout << "Factorial<5>: " << factorial_v<5> << "\n";
    std::cout << "Fibonacci<10>: " << fibonacci_v<10> << "\n";
    
    std::vector<int> vec = {1, 2, 3, 4, 5};
    process_if_container(vec);
    process_if_container(42);
    
    Logger::instance().log("Hello from singleton");
    
    auto doc = std::make_unique<Document>("Original");
    auto copy = doc->clone();
    copy->print();
    
    std::cout << "Sum: " << sum_args(1, 2, 3, 4, 5) << "\n";
    
    return 0;
}