tl::expected
ライブラリ
tl::expected
概要
tl::expectedは、C++23のstd::expectedのバックポート実装で、関数型プログラミングスタイルのエラーハンドリングを提供します。成功した値とエラーの両方を表現できる型で、例外を使わずに安全なエラーハンドリングが可能です。Sy Brand(TartanLlama)によって開発され、C++11/14/17で使用できます。monadic操作(map、and_then等)をサポートし、エラーハンドリングロジックを副作用として分離できるため、関数型プログラミングパラダイムと相性が良く、堅牢なコードの作成を支援します。
詳細
tl::expectedは、成功値(T)またはエラー値(E)のいずれかを保持するテンプレートクラスです。std::optionalと似ていますが、値が存在しない場合に詳細なエラー情報を提供できる点が異なります。monadic操作により、エラーハンドリングを関数チェーンから分離し、成功パスに集中したコードを書くことができます。例外を使わないエラーハンドリングパターンにより、パフォーマンスが重要な場面や例外が禁止されている環境でも安全にエラーを処理できます。検証関数と組み合わせることで、強力なバリデーション機能を構築できます。
主な特徴
- 例外なしエラーハンドリング: 例外を使わずに安全なエラー処理
- 関数型プログラミング: monadic操作による関数チェーン
- 型安全性: コンパイル時にエラーハンドリングを強制
- パフォーマンス: 例外処理のオーバーヘッドがない
- C++11対応: 古いコンパイラでも使用可能
- ヘッダーオンリー: 簡単に導入できる
メリット・デメリット
メリット
- 例外を使わないため、パフォーマンスが予測しやすい
- エラーハンドリングがコンパイル時に強制される
- 関数型プログラミングスタイルでクリーンなコード
- 詳細なエラー情報を型安全に扱える
- ヘッダーオンリーで導入が簡単
- C++23標準の先取り実装
デメリット
- 関数型プログラミングの学習コストが存在
- 既存の例外ベースコードとの統合に手間
- デバッグ時のスタックトレースが取得困難
- 一部の開発者には馴染みがない概念
- 複雑なエラーハンドリングでは記述が冗長になる可能性
参考ページ
書き方の例
インストールと基本セットアップ
# vcpkgを使用してインストール
vcpkg install tl-expected
# 手動でヘッダーファイルをダウンロード
wget https://github.com/TartanLlama/expected/releases/download/v1.0.0/tl-expected.hpp
# tl-expected.hppを適切なディレクトリに配置
# CMakeでの使用例
find_package(tl-expected REQUIRED)
target_link_libraries(your_target tl::expected)
基本的なエラーハンドリングとバリデーション
#include <tl/expected.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <regex>
// エラー型の定義
enum class ValidationError {
EMPTY_INPUT,
INVALID_FORMAT,
OUT_OF_RANGE,
INVALID_EMAIL,
INVALID_PHONE,
INVALID_URL
};
// エラーメッセージの変換
std::string errorToString(ValidationError error) {
switch (error) {
case ValidationError::EMPTY_INPUT:
return "入力が空です";
case ValidationError::INVALID_FORMAT:
return "無効なフォーマットです";
case ValidationError::OUT_OF_RANGE:
return "値が範囲外です";
case ValidationError::INVALID_EMAIL:
return "無効なメールアドレスです";
case ValidationError::INVALID_PHONE:
return "無効な電話番号です";
case ValidationError::INVALID_URL:
return "無効なURLです";
default:
return "未知のエラー";
}
}
// 基本的な検証関数
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);
}
}
// 数値バリデーション
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() {
// 基本的な使用例
{
std::cout << "=== 基本的なバリデーション ===" << std::endl;
// 成功ケース
auto result1 = validateNonEmpty("Hello World");
if (result1) {
std::cout << "✓ 成功: " << *result1 << std::endl;
} else {
std::cout << "✗ エラー: " << errorToString(result1.error()) << std::endl;
}
// 失敗ケース
auto result2 = validateNonEmpty("");
if (result2) {
std::cout << "✓ 成功: " << *result2 << std::endl;
} else {
std::cout << "✗ エラー: " << errorToString(result2.error()) << std::endl;
}
}
// 連続バリデーション
{
std::cout << "\n=== 連続バリデーション ===" << std::endl;
std::string email = "[email protected]";
// 関数的な連続バリデーション
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 << "✓ メールアドレスは有効です: " << *result << std::endl;
} else {
std::cout << "✗ メールアドレスエラー: " << errorToString(result.error()) << std::endl;
}
}
// 年齢バリデーション
{
std::cout << "\n=== 年齢バリデーション ===" << 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_str << " は有効です: " << *result << "歳" << std::endl;
} else {
std::cout << "✗ 年齢 " << age_str << " は無効です: " << errorToString(result.error()) << std::endl;
}
}
}
// 数値バリデーション
{
std::cout << "\n=== 数値バリデーション ===" << 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 << "✓ 数値 " << num_str << " は有効です: " << *result << std::endl;
} else {
std::cout << "✗ 数値 " << num_str << " は無効です: " << errorToString(result.error()) << std::endl;
}
}
}
return 0;
}
高度なバリデーション機能と構造化データ
#include <tl/expected.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <regex>
// 複雑なエラー型
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 + "': " + message;
}
};
// ユーザー情報の構造体
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) {}
};
// 高度なバリデーション クラス
class UserValidator {
private:
// 内部バリデーション関数
static tl::expected<std::string, ValidationError> validateName(const std::string& name) {
if (name.empty()) {
return tl::unexpected(ValidationError(ValidationError::REQUIRED_FIELD_MISSING,
"name", "名前が入力されていません"));
}
if (name.length() < 2 || name.length() > 50) {
return tl::unexpected(ValidationError(ValidationError::OUT_OF_RANGE,
"name", "名前は2文字以上50文字以下である必要があります"));
}
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", "メールアドレスが入力されていません"));
}
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", "無効なメールアドレス形式です"));
}
return email;
}
static tl::expected<int, ValidationError> validateAgeRange(int age) {
if (age < 0 || age > 120) {
return tl::unexpected(ValidationError(ValidationError::OUT_OF_RANGE,
"age", "年齢は0歳から120歳の範囲で入力してください"));
}
return age;
}
static tl::expected<std::string, ValidationError> validatePhoneFormat(const std::string& phone) {
if (phone.empty()) {
return phone; // 電話番号は任意
}
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", "無効な電話番号形式です"));
}
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", "スキルは20個まで設定できます"));
}
for (const auto& skill : skills) {
if (skill.empty()) {
return tl::unexpected(ValidationError(ValidationError::INVALID_FORMAT,
"skills", "空のスキルは設定できません"));
}
if (skill.length() > 30) {
return tl::unexpected(ValidationError(ValidationError::OUT_OF_RANGE,
"skills", "各スキルは30文字以下である必要があります"));
}
}
return skills;
}
public:
// 個別フィールドの検証
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);
}
// 複合バリデーション
static tl::expected<UserInfo, ValidationError> validateUser(const UserInfo& user) {
// 名前の検証
auto name_result = validateName(user.name);
if (!name_result) {
return tl::unexpected(name_result.error());
}
// メールアドレスの検証
auto email_result = validateEmailFormat(user.email);
if (!email_result) {
return tl::unexpected(email_result.error());
}
// 年齢の検証
auto age_result = validateAgeRange(user.age);
if (!age_result) {
return tl::unexpected(age_result.error());
}
// 電話番号の検証
auto phone_result = validatePhoneFormat(user.phone);
if (!phone_result) {
return tl::unexpected(phone_result.error());
}
// スキルの検証
auto skills_result = validateSkills(user.skills);
if (!skills_result) {
return tl::unexpected(skills_result.error());
}
// すべて成功した場合
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;
}
// バッチ検証
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;
}
};
// 条件付きバリデーション
class ConditionalValidator {
public:
// 年齢に基づく条件付きバリデーション
static tl::expected<UserInfo, ValidationError> validateUserWithConditions(const UserInfo& user) {
// 基本バリデーション
auto basic_result = UserValidator::validateUser(user);
if (!basic_result) {
return basic_result;
}
UserInfo validated_user = *basic_result;
// 条件付きバリデーション
if (validated_user.age < 18) {
// 未成年の場合は電話番号が必須
if (validated_user.phone.empty()) {
return tl::unexpected(ValidationError(ValidationError::REQUIRED_FIELD_MISSING,
"phone", "未成年者は電話番号の入力が必要です"));
}
}
if (validated_user.age >= 65) {
// 高齢者の場合は特別な検証
if (validated_user.skills.empty()) {
return tl::unexpected(ValidationError(ValidationError::CUSTOM_ERROR,
"skills", "高齢者の方はスキルの入力をお願いします"));
}
}
return validated_user;
}
};
// 実際の使用例
int main() {
try {
// 有効なユーザー情報のテスト
{
std::cout << "=== 有効なユーザー情報のテスト ===" << std::endl;
UserInfo user("田中太郎", "[email protected]", 30, "09012345678");
user.skills = {"C++", "Python", "JavaScript"};
user.metadata = {{"department", "Engineering"}, {"level", "Senior"}};
auto result = UserValidator::validateUser(user);
if (result) {
std::cout << "✓ ユーザー情報は有効です:" << std::endl;
std::cout << " 名前: " << result->name << std::endl;
std::cout << " メール: " << result->email << std::endl;
std::cout << " 年齢: " << result->age << std::endl;
std::cout << " 電話: " << result->phone << std::endl;
std::cout << " スキル数: " << result->skills.size() << std::endl;
} else {
std::cout << "✗ " << result.error().toString() << std::endl;
}
}
// 無効なユーザー情報のテスト
{
std::cout << "\n=== 無効なユーザー情報のテスト ===" << std::endl;
std::vector<UserInfo> invalid_users = {
{"", "[email protected]", 30, "09012345678"}, // 空の名前
{"田中太郎", "invalid-email", 30, "09012345678"}, // 無効なメール
{"田中太郎", "[email protected]", 150, "09012345678"}, // 範囲外の年齢
{"田中太郎", "[email protected]", 30, "123"}, // 無効な電話番号
};
auto results = UserValidator::validateUsers(invalid_users);
for (size_t i = 0; i < results.size(); ++i) {
std::cout << "ユーザー " << (i + 1) << ": ";
if (results[i]) {
std::cout << "✓ 有効" << std::endl;
} else {
std::cout << "✗ " << results[i].error().toString() << std::endl;
}
}
}
// 条件付きバリデーション
{
std::cout << "\n=== 条件付きバリデーション ===" << std::endl;
// 未成年のユーザー(電話番号なし)
UserInfo minor("高校生太郎", "[email protected]", 16, "");
minor.skills = {"勉強", "部活動"};
auto result = ConditionalValidator::validateUserWithConditions(minor);
if (result) {
std::cout << "✓ 未成年ユーザーは有効です" << std::endl;
} else {
std::cout << "✗ 未成年ユーザー: " << result.error().toString() << std::endl;
}
// 高齢者のユーザー(スキルなし)
UserInfo senior("高齢者花子", "[email protected]", 70, "09087654321");
auto result2 = ConditionalValidator::validateUserWithConditions(senior);
if (result2) {
std::cout << "✓ 高齢者ユーザーは有効です" << std::endl;
} else {
std::cout << "✗ 高齢者ユーザー: " << result2.error().toString() << std::endl;
}
}
// 関数型スタイルの連続バリデーション
{
std::cout << "\n=== 関数型スタイルの連続バリデーション ===" << std::endl;
std::string name = "佐藤次郎";
std::string email = "[email protected]";
int age = 25;
std::string phone = "09056781234";
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 << "✓ 全ての検証が成功しました" << std::endl;
std::cout << " 構築されたユーザー: " << final_result->name << " (" << final_result->email << ")" << std::endl;
} else {
std::cout << "✗ 検証エラー: " << final_result.error().toString() << std::endl;
}
}
} catch (const std::exception& e) {
std::cerr << "予期しないエラー: " << e.what() << std::endl;
}
return 0;
}
パフォーマンス比較とベンチマーク
#include <tl/expected.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <chrono>
#include <stdexcept>
#include <random>
// 検証対象のデータ構造
struct TestData {
std::string value;
int number;
TestData(const std::string& v, int n) : value(v), number(n) {}
};
// 例外ベースのバリデーション
class ExceptionValidator {
public:
static TestData validateWithExceptions(const TestData& data) {
if (data.value.empty()) {
throw std::invalid_argument("値が空です");
}
if (data.value.length() < 5 || data.value.length() > 50) {
throw std::invalid_argument("値の長さが無効です");
}
if (data.number < 0 || data.number > 1000) {
throw std::invalid_argument("数値が範囲外です");
}
return data;
}
};
// tl::expectedベースのバリデーション
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;
}
};
// テストデータ生成
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;
// 文字列生成
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 {
// 無効なデータを生成
if (valid_ratio(gen) < 0.3) {
value = ""; // 空の文字列
} else {
int length = std::uniform_int_distribution<>{1, 4}(gen); // 短すぎる
value.reserve(length);
for (int j = 0; j < length; ++j) {
value += static_cast<char>(char_dist(gen));
}
}
}
// 数値生成
int number;
if (should_be_valid) {
number = std::uniform_int_distribution<>{0, 1000}(gen);
} else {
number = number_dist(gen); // 範囲外の可能性
}
data.emplace_back(value, number);
}
return data;
}
};
// パフォーマンステスト
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 << " 結果:" << std::endl;
std::cout << " 総アイテム数: " << total_items << std::endl;
std::cout << " 有効アイテム数: " << valid_items << std::endl;
std::cout << " 無効アイテム数: " << invalid_items << std::endl;
std::cout << " 処理時間: " << elapsed_ms << " ms" << std::endl;
std::cout << " 処理速度: " << 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;
}
};
// 機能的な使用例
class FunctionalExample {
public:
// 複数のバリデーションを組み合わせる
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("値が空です");
case ExpectedValidator::Error::INVALID_LENGTH:
return std::string("値の長さが無効です");
case ExpectedValidator::Error::OUT_OF_RANGE:
return std::string("数値が範囲外です");
default:
return std::string("未知のエラー");
}
})
.and_then([](const TestData& validated_data) -> tl::expected<TestData, std::string> {
// 追加の検証ロジック
if (validated_data.value.find("invalid") != std::string::npos) {
return tl::unexpected(std::string("無効な文字列が含まれています"));
}
return validated_data;
});
}
// バッチ処理
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;
}
// 有効なデータのみを抽出
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;
// 小規模テスト
std::cout << "=== 小規模パフォーマンステスト (10,000 アイテム) ===" << std::endl;
auto small_data = generator.generateTestData(10000, 0.7);
auto exception_result = PerformanceTest::testExceptionBased(small_data);
exception_result.print("例外ベース");
std::cout << std::endl;
auto expected_result = PerformanceTest::testExpectedBased(small_data);
expected_result.print("tl::expected ベース");
double speedup = expected_result.items_per_second / exception_result.items_per_second;
std::cout << "\n速度向上: " << speedup << "x" << std::endl;
// 大規模テスト
std::cout << "\n=== 大規模パフォーマンステスト (100,000 アイテム) ===" << std::endl;
auto large_data = generator.generateTestData(100000, 0.7);
auto large_exception_result = PerformanceTest::testExceptionBased(large_data);
large_exception_result.print("例外ベース");
std::cout << std::endl;
auto large_expected_result = PerformanceTest::testExpectedBased(large_data);
large_expected_result.print("tl::expected ベース");
double large_speedup = large_expected_result.items_per_second / large_exception_result.items_per_second;
std::cout << "\n速度向上: " << large_speedup << "x" << std::endl;
// 機能的な使用例
std::cout << "\n=== 機能的な使用例 ===" << std::endl;
auto sample_data = generator.generateTestData(10, 0.5);
auto functional_results = FunctionalExample::validateBatch(sample_data);
std::cout << "バッチ処理結果:" << std::endl;
for (size_t i = 0; i < functional_results.size(); ++i) {
const auto& result = functional_results[i];
std::cout << " アイテム " << (i + 1) << ": ";
if (result) {
std::cout << "✓ 有効 (値: " << result->value.substr(0, 10) << "..., 数値: " << result->number << ")" << std::endl;
} else {
std::cout << "✗ エラー: " << result.error() << std::endl;
}
}
// 有効なデータのみ抽出
auto valid_data = FunctionalExample::extractValidData(sample_data);
std::cout << "\n有効なデータのみ抽出: " << valid_data.size() << " / " << sample_data.size() << " アイテム" << std::endl;
} catch (const std::exception& e) {
std::cerr << "エラー: " << e.what() << std::endl;
}
return 0;
}