tl::expected

Error HandlingC++Functional ProgrammingValue ValidationMonadException-free

Library

tl::expected

Overview

tl::expected is a backport implementation of C++23's std::expected, providing functional programming-style error handling. It is a type that can represent both successful values and errors, enabling safe error handling without exceptions. Developed by Sy Brand (TartanLlama), it can be used with C++11/14/17. It supports monadic operations (map, and_then, etc.) and allows error handling logic to be separated as side effects, making it compatible with functional programming paradigms and supporting the creation of robust code.

Details

tl::expected is a template class that holds either a success value (T) or an error value (E). Similar to std::optional, but differs in that it can provide detailed error information when a value doesn't exist. Through monadic operations, error handling can be separated from function chains, allowing you to write code that focuses on the success path. The exception-free error handling pattern allows safe error processing in performance-critical scenarios or environments where exceptions are prohibited. When combined with validation functions, powerful validation functionality can be built.

Key Features

  • Exception-free Error Handling: Safe error processing without exceptions
  • Functional Programming: Function chaining through monadic operations
  • Type Safety: Compile-time enforcement of error handling
  • Performance: No exception handling overhead
  • C++11 Support: Usable with older compilers
  • Header-only: Easy to integrate

Pros and Cons

Pros

  • Predictable performance as it doesn't use exceptions
  • Error handling is enforced at compile time
  • Clean code with functional programming style
  • Type-safe handling of detailed error information
  • Easy integration as header-only
  • Early implementation of C++23 standard

Cons

  • Learning curve for functional programming concepts
  • Integration effort with existing exception-based code
  • Difficult to obtain stack traces during debugging
  • Unfamiliar concept for some developers
  • Potentially verbose description for complex error handling

References

Code Examples

Installation and Basic Setup

# Install using vcpkg
vcpkg install tl-expected

# Manually download header file
wget https://github.com/TartanLlama/expected/releases/download/v1.0.0/tl-expected.hpp
# Place tl-expected.hpp in appropriate directory

# CMake usage example
find_package(tl-expected REQUIRED)
target_link_libraries(your_target tl::expected)

Basic Error Handling and Validation

#include <tl/expected.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <regex>

// Error type definition
enum class ValidationError {
    EMPTY_INPUT,
    INVALID_FORMAT,
    OUT_OF_RANGE,
    INVALID_EMAIL,
    INVALID_PHONE,
    INVALID_URL
};

// Error message conversion
std::string errorToString(ValidationError error) {
    switch (error) {
        case ValidationError::EMPTY_INPUT:
            return "Input is empty";
        case ValidationError::INVALID_FORMAT:
            return "Invalid format";
        case ValidationError::OUT_OF_RANGE:
            return "Value out of range";
        case ValidationError::INVALID_EMAIL:
            return "Invalid email address";
        case ValidationError::INVALID_PHONE:
            return "Invalid phone number";
        case ValidationError::INVALID_URL:
            return "Invalid URL";
        default:
            return "Unknown error";
    }
}

// Basic validation functions
tl::expected<std::string, ValidationError> validateNonEmpty(const std::string& input) {
    if (input.empty()) {
        return tl::unexpected(ValidationError::EMPTY_INPUT);
    }
    return input;
}

tl::expected<std::string, ValidationError> validateLength(const std::string& input, size_t min_len, size_t max_len) {
    if (input.length() < min_len || input.length() > max_len) {
        return tl::unexpected(ValidationError::OUT_OF_RANGE);
    }
    return input;
}

tl::expected<std::string, ValidationError> validateEmail(const std::string& input) {
    static const std::regex email_regex(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)");
    if (!std::regex_match(input, email_regex)) {
        return tl::unexpected(ValidationError::INVALID_EMAIL);
    }
    return input;
}

tl::expected<std::string, ValidationError> validatePhone(const std::string& input) {
    static const std::regex phone_regex(R"(^(\+81|0)[1-9][0-9]{8,9}$)");
    if (!std::regex_match(input, phone_regex)) {
        return tl::unexpected(ValidationError::INVALID_PHONE);
    }
    return input;
}

tl::expected<int, ValidationError> validateAge(const std::string& input) {
    try {
        int age = std::stoi(input);
        if (age < 0 || age > 120) {
            return tl::unexpected(ValidationError::OUT_OF_RANGE);
        }
        return age;
    } catch (const std::exception&) {
        return tl::unexpected(ValidationError::INVALID_FORMAT);
    }
}

// Numeric validation
tl::expected<double, ValidationError> validateNumber(const std::string& input, double min_val, double max_val) {
    try {
        double value = std::stod(input);
        if (value < min_val || value > max_val) {
            return tl::unexpected(ValidationError::OUT_OF_RANGE);
        }
        return value;
    } catch (const std::exception&) {
        return tl::unexpected(ValidationError::INVALID_FORMAT);
    }
}

int main() {
    // Basic usage example
    {
        std::cout << "=== Basic Validation ===" << std::endl;
        
        // Success case
        auto result1 = validateNonEmpty("Hello World");
        if (result1) {
            std::cout << "✓ Success: " << *result1 << std::endl;
        } else {
            std::cout << "✗ Error: " << errorToString(result1.error()) << std::endl;
        }
        
        // Failure case
        auto result2 = validateNonEmpty("");
        if (result2) {
            std::cout << "✓ Success: " << *result2 << std::endl;
        } else {
            std::cout << "✗ Error: " << errorToString(result2.error()) << std::endl;
        }
    }
    
    // Sequential validation
    {
        std::cout << "\n=== Sequential Validation ===" << std::endl;
        
        std::string email = "[email protected]";
        
        // Functional sequential validation
        auto result = validateNonEmpty(email)
            .and_then([](const std::string& s) { return validateLength(s, 5, 100); })
            .and_then([](const std::string& s) { return validateEmail(s); });
        
        if (result) {
            std::cout << "✓ Email address is valid: " << *result << std::endl;
        } else {
            std::cout << "✗ Email address error: " << errorToString(result.error()) << std::endl;
        }
    }
    
    // Age validation
    {
        std::cout << "\n=== Age Validation ===" << std::endl;
        
        std::vector<std::string> ages = {"25", "150", "abc", "-5"};
        
        for (const auto& age_str : ages) {
            auto result = validateAge(age_str);
            if (result) {
                std::cout << "✓ Age " << age_str << " is valid: " << *result << " years old" << std::endl;
            } else {
                std::cout << "✗ Age " << age_str << " is invalid: " << errorToString(result.error()) << std::endl;
            }
        }
    }
    
    // Numeric validation
    {
        std::cout << "\n=== Numeric Validation ===" << std::endl;
        
        std::vector<std::string> numbers = {"3.14", "100.5", "abc", "200"};
        
        for (const auto& num_str : numbers) {
            auto result = validateNumber(num_str, 0.0, 100.0);
            if (result) {
                std::cout << "✓ Number " << num_str << " is valid: " << *result << std::endl;
            } else {
                std::cout << "✗ Number " << num_str << " is invalid: " << errorToString(result.error()) << std::endl;
            }
        }
    }
    
    return 0;
}

Advanced Validation Features and Structured Data

#include <tl/expected.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <regex>

// Complex error type
struct ValidationError {
    enum Type {
        REQUIRED_FIELD_MISSING,
        INVALID_TYPE,
        INVALID_FORMAT,
        OUT_OF_RANGE,
        INVALID_REFERENCE,
        CUSTOM_ERROR
    };
    
    Type type;
    std::string field;
    std::string message;
    
    ValidationError(Type t, const std::string& f, const std::string& m)
        : type(t), field(f), message(m) {}
    
    std::string toString() const {
        return "Field '" + field + "': " + message;
    }
};

// User information structure
struct UserInfo {
    std::string name;
    std::string email;
    int age;
    std::string phone;
    std::vector<std::string> skills;
    std::map<std::string, std::string> metadata;
    
    UserInfo() = default;
    UserInfo(const std::string& n, const std::string& e, int a, const std::string& p)
        : name(n), email(e), age(a), phone(p) {}
};

// Advanced validation class
class UserValidator {
private:
    // Internal validation functions
    static tl::expected<std::string, ValidationError> validateName(const std::string& name) {
        if (name.empty()) {
            return tl::unexpected(ValidationError(ValidationError::REQUIRED_FIELD_MISSING, 
                                                "name", "Name is required"));
        }
        
        if (name.length() < 2 || name.length() > 50) {
            return tl::unexpected(ValidationError(ValidationError::OUT_OF_RANGE, 
                                                "name", "Name must be between 2 and 50 characters"));
        }
        
        return name;
    }
    
    static tl::expected<std::string, ValidationError> validateEmailFormat(const std::string& email) {
        if (email.empty()) {
            return tl::unexpected(ValidationError(ValidationError::REQUIRED_FIELD_MISSING, 
                                                "email", "Email address is required"));
        }
        
        static const std::regex email_regex(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)");
        if (!std::regex_match(email, email_regex)) {
            return tl::unexpected(ValidationError(ValidationError::INVALID_FORMAT, 
                                                "email", "Invalid email address format"));
        }
        
        return email;
    }
    
    static tl::expected<int, ValidationError> validateAgeRange(int age) {
        if (age < 0 || age > 120) {
            return tl::unexpected(ValidationError(ValidationError::OUT_OF_RANGE, 
                                                "age", "Age must be between 0 and 120"));
        }
        
        return age;
    }
    
    static tl::expected<std::string, ValidationError> validatePhoneFormat(const std::string& phone) {
        if (phone.empty()) {
            return phone;  // Phone number is optional
        }
        
        static const std::regex phone_regex(R"(^(\+81|0)[1-9][0-9]{8,9}$)");
        if (!std::regex_match(phone, phone_regex)) {
            return tl::unexpected(ValidationError(ValidationError::INVALID_FORMAT, 
                                                "phone", "Invalid phone number format"));
        }
        
        return phone;
    }
    
    static tl::expected<std::vector<std::string>, ValidationError> validateSkills(const std::vector<std::string>& skills) {
        if (skills.size() > 20) {
            return tl::unexpected(ValidationError(ValidationError::OUT_OF_RANGE, 
                                                "skills", "Maximum of 20 skills allowed"));
        }
        
        for (const auto& skill : skills) {
            if (skill.empty()) {
                return tl::unexpected(ValidationError(ValidationError::INVALID_FORMAT, 
                                                    "skills", "Empty skills are not allowed"));
            }
            
            if (skill.length() > 30) {
                return tl::unexpected(ValidationError(ValidationError::OUT_OF_RANGE, 
                                                    "skills", "Each skill must be 30 characters or less"));
            }
        }
        
        return skills;
    }

public:
    // Individual field validation
    static tl::expected<std::string, ValidationError> validateUserName(const std::string& name) {
        return validateName(name);
    }
    
    static tl::expected<std::string, ValidationError> validateUserEmail(const std::string& email) {
        return validateEmailFormat(email);
    }
    
    static tl::expected<int, ValidationError> validateUserAge(int age) {
        return validateAgeRange(age);
    }
    
    static tl::expected<std::string, ValidationError> validateUserPhone(const std::string& phone) {
        return validatePhoneFormat(phone);
    }
    
    // Composite validation
    static tl::expected<UserInfo, ValidationError> validateUser(const UserInfo& user) {
        // Name validation
        auto name_result = validateName(user.name);
        if (!name_result) {
            return tl::unexpected(name_result.error());
        }
        
        // Email validation
        auto email_result = validateEmailFormat(user.email);
        if (!email_result) {
            return tl::unexpected(email_result.error());
        }
        
        // Age validation
        auto age_result = validateAgeRange(user.age);
        if (!age_result) {
            return tl::unexpected(age_result.error());
        }
        
        // Phone validation
        auto phone_result = validatePhoneFormat(user.phone);
        if (!phone_result) {
            return tl::unexpected(phone_result.error());
        }
        
        // Skills validation
        auto skills_result = validateSkills(user.skills);
        if (!skills_result) {
            return tl::unexpected(skills_result.error());
        }
        
        // All successful
        UserInfo validated_user = user;
        validated_user.name = *name_result;
        validated_user.email = *email_result;
        validated_user.age = *age_result;
        validated_user.phone = *phone_result;
        validated_user.skills = *skills_result;
        
        return validated_user;
    }
    
    // Batch validation
    static std::vector<tl::expected<UserInfo, ValidationError>> validateUsers(const std::vector<UserInfo>& users) {
        std::vector<tl::expected<UserInfo, ValidationError>> results;
        results.reserve(users.size());
        
        for (const auto& user : users) {
            results.push_back(validateUser(user));
        }
        
        return results;
    }
};

// Conditional validation
class ConditionalValidator {
public:
    // Age-based conditional validation
    static tl::expected<UserInfo, ValidationError> validateUserWithConditions(const UserInfo& user) {
        // Basic validation
        auto basic_result = UserValidator::validateUser(user);
        if (!basic_result) {
            return basic_result;
        }
        
        UserInfo validated_user = *basic_result;
        
        // Conditional validation
        if (validated_user.age < 18) {
            // Phone number required for minors
            if (validated_user.phone.empty()) {
                return tl::unexpected(ValidationError(ValidationError::REQUIRED_FIELD_MISSING, 
                                                    "phone", "Phone number required for minors"));
            }
        }
        
        if (validated_user.age >= 65) {
            // Special validation for seniors
            if (validated_user.skills.empty()) {
                return tl::unexpected(ValidationError(ValidationError::CUSTOM_ERROR, 
                                                    "skills", "Please provide skills information"));
            }
        }
        
        return validated_user;
    }
};

// Actual usage example
int main() {
    try {
        // Test valid user information
        {
            std::cout << "=== Valid User Information Test ===" << std::endl;
            
            UserInfo user("John Doe", "[email protected]", 30, "+1234567890");
            user.skills = {"C++", "Python", "JavaScript"};
            user.metadata = {{"department", "Engineering"}, {"level", "Senior"}};
            
            auto result = UserValidator::validateUser(user);
            if (result) {
                std::cout << "✓ User information is valid:" << std::endl;
                std::cout << "  Name: " << result->name << std::endl;
                std::cout << "  Email: " << result->email << std::endl;
                std::cout << "  Age: " << result->age << std::endl;
                std::cout << "  Phone: " << result->phone << std::endl;
                std::cout << "  Skills count: " << result->skills.size() << std::endl;
            } else {
                std::cout << "✗ " << result.error().toString() << std::endl;
            }
        }
        
        // Test invalid user information
        {
            std::cout << "\n=== Invalid User Information Test ===" << std::endl;
            
            std::vector<UserInfo> invalid_users = {
                {"", "[email protected]", 30, "+1234567890"},  // Empty name
                {"John Doe", "invalid-email", 30, "+1234567890"},  // Invalid email
                {"John Doe", "[email protected]", 150, "+1234567890"},  // Out of range age
                {"John Doe", "[email protected]", 30, "123"},  // Invalid phone number
            };
            
            auto results = UserValidator::validateUsers(invalid_users);
            
            for (size_t i = 0; i < results.size(); ++i) {
                std::cout << "User " << (i + 1) << ": ";
                if (results[i]) {
                    std::cout << "✓ Valid" << std::endl;
                } else {
                    std::cout << "✗ " << results[i].error().toString() << std::endl;
                }
            }
        }
        
        // Conditional validation
        {
            std::cout << "\n=== Conditional Validation ===" << std::endl;
            
            // Minor user (no phone number)
            UserInfo minor("Student John", "[email protected]", 16, "");
            minor.skills = {"Study", "Sports"};
            
            auto result = ConditionalValidator::validateUserWithConditions(minor);
            if (result) {
                std::cout << "✓ Minor user is valid" << std::endl;
            } else {
                std::cout << "✗ Minor user: " << result.error().toString() << std::endl;
            }
            
            // Senior user (no skills)
            UserInfo senior("Senior Jane", "[email protected]", 70, "+1987654321");
            
            auto result2 = ConditionalValidator::validateUserWithConditions(senior);
            if (result2) {
                std::cout << "✓ Senior user is valid" << std::endl;
            } else {
                std::cout << "✗ Senior user: " << result2.error().toString() << std::endl;
            }
        }
        
        // Functional style sequential validation
        {
            std::cout << "\n=== Functional Style Sequential Validation ===" << std::endl;
            
            std::string name = "Jane Smith";
            std::string email = "[email protected]";
            int age = 25;
            std::string phone = "+1555123456";
            
            auto final_result = UserValidator::validateUserName(name)
                .and_then([&](const std::string& validated_name) {
                    return UserValidator::validateUserEmail(email)
                        .and_then([&](const std::string& validated_email) {
                            return UserValidator::validateUserAge(age)
                                .and_then([&](int validated_age) {
                                    return UserValidator::validateUserPhone(phone)
                                        .map([&](const std::string& validated_phone) {
                                            return UserInfo(validated_name, validated_email, validated_age, validated_phone);
                                        });
                                });
                        });
                });
            
            if (final_result) {
                std::cout << "✓ All validations successful" << std::endl;
                std::cout << "  Created user: " << final_result->name << " (" << final_result->email << ")" << std::endl;
            } else {
                std::cout << "✗ Validation error: " << final_result.error().toString() << std::endl;
            }
        }
        
    } catch (const std::exception& e) {
        std::cerr << "Unexpected error: " << e.what() << std::endl;
    }
    
    return 0;
}

Performance Comparison and Benchmarks

#include <tl/expected.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <chrono>
#include <stdexcept>
#include <random>

// Data structure for validation
struct TestData {
    std::string value;
    int number;
    
    TestData(const std::string& v, int n) : value(v), number(n) {}
};

// Exception-based validation
class ExceptionValidator {
public:
    static TestData validateWithExceptions(const TestData& data) {
        if (data.value.empty()) {
            throw std::invalid_argument("Value is empty");
        }
        
        if (data.value.length() < 5 || data.value.length() > 50) {
            throw std::invalid_argument("Invalid value length");
        }
        
        if (data.number < 0 || data.number > 1000) {
            throw std::invalid_argument("Number out of range");
        }
        
        return data;
    }
};

// tl::expected based validation
class ExpectedValidator {
public:
    enum class Error {
        EMPTY_VALUE,
        INVALID_LENGTH,
        OUT_OF_RANGE
    };
    
    static tl::expected<TestData, Error> validateWithExpected(const TestData& data) {
        if (data.value.empty()) {
            return tl::unexpected(Error::EMPTY_VALUE);
        }
        
        if (data.value.length() < 5 || data.value.length() > 50) {
            return tl::unexpected(Error::INVALID_LENGTH);
        }
        
        if (data.number < 0 || data.number > 1000) {
            return tl::unexpected(Error::OUT_OF_RANGE);
        }
        
        return data;
    }
};

// Test data generation
class TestDataGenerator {
private:
    std::random_device rd;
    std::mt19937 gen{rd()};
    std::uniform_int_distribution<> length_dist{1, 60};
    std::uniform_int_distribution<> char_dist{'a', 'z'};
    std::uniform_int_distribution<> number_dist{-100, 1100};
    std::uniform_real_distribution<> valid_ratio{0.0, 1.0};
    
public:
    std::vector<TestData> generateTestData(size_t count, double valid_percentage = 0.7) {
        std::vector<TestData> data;
        data.reserve(count);
        
        for (size_t i = 0; i < count; ++i) {
            bool should_be_valid = valid_ratio(gen) < valid_percentage;
            
            // String generation
            std::string value;
            if (should_be_valid) {
                int length = std::uniform_int_distribution<>{5, 50}(gen);
                value.reserve(length);
                for (int j = 0; j < length; ++j) {
                    value += static_cast<char>(char_dist(gen));
                }
            } else {
                // Generate invalid data
                if (valid_ratio(gen) < 0.3) {
                    value = "";  // Empty string
                } else {
                    int length = std::uniform_int_distribution<>{1, 4}(gen);  // Too short
                    value.reserve(length);
                    for (int j = 0; j < length; ++j) {
                        value += static_cast<char>(char_dist(gen));
                    }
                }
            }
            
            // Number generation
            int number;
            if (should_be_valid) {
                number = std::uniform_int_distribution<>{0, 1000}(gen);
            } else {
                number = number_dist(gen);  // Potentially out of range
            }
            
            data.emplace_back(value, number);
        }
        
        return data;
    }
};

// Performance test
class PerformanceTest {
public:
    struct BenchmarkResult {
        size_t total_items;
        size_t valid_items;
        size_t invalid_items;
        double elapsed_ms;
        double items_per_second;
        
        void print(const std::string& name) const {
            std::cout << name << " Results:" << std::endl;
            std::cout << "  Total items: " << total_items << std::endl;
            std::cout << "  Valid items: " << valid_items << std::endl;
            std::cout << "  Invalid items: " << invalid_items << std::endl;
            std::cout << "  Processing time: " << elapsed_ms << " ms" << std::endl;
            std::cout << "  Processing speed: " << items_per_second << " items/sec" << std::endl;
        }
    };
    
    static BenchmarkResult testExceptionBased(const std::vector<TestData>& test_data) {
        BenchmarkResult result;
        result.total_items = test_data.size();
        result.valid_items = 0;
        result.invalid_items = 0;
        
        auto start = std::chrono::high_resolution_clock::now();
        
        for (const auto& data : test_data) {
            try {
                ExceptionValidator::validateWithExceptions(data);
                result.valid_items++;
            } catch (const std::exception&) {
                result.invalid_items++;
            }
        }
        
        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        
        result.elapsed_ms = duration.count() / 1000.0;
        result.items_per_second = (result.total_items * 1000000.0) / duration.count();
        
        return result;
    }
    
    static BenchmarkResult testExpectedBased(const std::vector<TestData>& test_data) {
        BenchmarkResult result;
        result.total_items = test_data.size();
        result.valid_items = 0;
        result.invalid_items = 0;
        
        auto start = std::chrono::high_resolution_clock::now();
        
        for (const auto& data : test_data) {
            auto validation_result = ExpectedValidator::validateWithExpected(data);
            if (validation_result) {
                result.valid_items++;
            } else {
                result.invalid_items++;
            }
        }
        
        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        
        result.elapsed_ms = duration.count() / 1000.0;
        result.items_per_second = (result.total_items * 1000000.0) / duration.count();
        
        return result;
    }
};

// Functional usage example
class FunctionalExample {
public:
    // Combine multiple validations
    static tl::expected<TestData, std::string> validateWithMultipleSteps(const TestData& data) {
        return ExpectedValidator::validateWithExpected(data)
            .map_error([](ExpectedValidator::Error error) {
                switch (error) {
                    case ExpectedValidator::Error::EMPTY_VALUE:
                        return std::string("Value is empty");
                    case ExpectedValidator::Error::INVALID_LENGTH:
                        return std::string("Invalid value length");
                    case ExpectedValidator::Error::OUT_OF_RANGE:
                        return std::string("Number out of range");
                    default:
                        return std::string("Unknown error");
                }
            })
            .and_then([](const TestData& validated_data) -> tl::expected<TestData, std::string> {
                // Additional validation logic
                if (validated_data.value.find("invalid") != std::string::npos) {
                    return tl::unexpected(std::string("Contains invalid string"));
                }
                return validated_data;
            });
    }
    
    // Batch processing
    static std::vector<tl::expected<TestData, std::string>> validateBatch(const std::vector<TestData>& data) {
        std::vector<tl::expected<TestData, std::string>> results;
        results.reserve(data.size());
        
        for (const auto& item : data) {
            results.push_back(validateWithMultipleSteps(item));
        }
        
        return results;
    }
    
    // Extract only valid data
    static std::vector<TestData> extractValidData(const std::vector<TestData>& data) {
        std::vector<TestData> valid_data;
        
        for (const auto& item : data) {
            auto result = validateWithMultipleSteps(item);
            if (result) {
                valid_data.push_back(*result);
            }
        }
        
        return valid_data;
    }
};

int main() {
    try {
        TestDataGenerator generator;
        
        // Small-scale test
        std::cout << "=== Small-scale Performance Test (10,000 items) ===" << std::endl;
        
        auto small_data = generator.generateTestData(10000, 0.7);
        
        auto exception_result = PerformanceTest::testExceptionBased(small_data);
        exception_result.print("Exception-based");
        
        std::cout << std::endl;
        
        auto expected_result = PerformanceTest::testExpectedBased(small_data);
        expected_result.print("tl::expected-based");
        
        double speedup = expected_result.items_per_second / exception_result.items_per_second;
        std::cout << "\nSpeedup: " << speedup << "x" << std::endl;
        
        // Large-scale test
        std::cout << "\n=== Large-scale Performance Test (100,000 items) ===" << std::endl;
        
        auto large_data = generator.generateTestData(100000, 0.7);
        
        auto large_exception_result = PerformanceTest::testExceptionBased(large_data);
        large_exception_result.print("Exception-based");
        
        std::cout << std::endl;
        
        auto large_expected_result = PerformanceTest::testExpectedBased(large_data);
        large_expected_result.print("tl::expected-based");
        
        double large_speedup = large_expected_result.items_per_second / large_exception_result.items_per_second;
        std::cout << "\nSpeedup: " << large_speedup << "x" << std::endl;
        
        // Functional usage example
        std::cout << "\n=== Functional Usage Example ===" << std::endl;
        
        auto sample_data = generator.generateTestData(10, 0.5);
        auto functional_results = FunctionalExample::validateBatch(sample_data);
        
        std::cout << "Batch processing results:" << std::endl;
        for (size_t i = 0; i < functional_results.size(); ++i) {
            const auto& result = functional_results[i];
            std::cout << "  Item " << (i + 1) << ": ";
            if (result) {
                std::cout << "✓ Valid (value: " << result->value.substr(0, 10) << "..., number: " << result->number << ")" << std::endl;
            } else {
                std::cout << "✗ Error: " << result.error() << std::endl;
            }
        }
        
        // Extract only valid data
        auto valid_data = FunctionalExample::extractValidData(sample_data);
        std::cout << "\nValid data extracted: " << valid_data.size() << " / " << sample_data.size() << " items" << std::endl;
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    
    return 0;
}