{fmt} (バリデーション)

バリデーションライブラリC++フォーマット型安全文字列処理コンパイル時チェック

ライブラリ

{fmt} (バリデーション)

概要

{fmt}は、C++20のstd::formatの基盤となったモダンなC++フォーマットライブラリです。主に文字列フォーマットに特化していますが、型安全性とコンパイル時チェック機能により、強力なバリデーション機能を提供します。フォーマット文字列の正確性をコンパイル時に検証し、実行時エラーを大幅に削減します。Python風のf-stringライクな構文と、printf/iostreamを上回る高いパフォーマンスにより、現代的なC++アプリケーションにおける文字列処理のスタンダードとして位置づけられています。

詳細

{fmt}ライブラリは、C++20で標準化されたstd::formatの原型となった実装で、型安全な文字列フォーマットとバリデーション機能を提供します。コンパイル時にフォーマット文字列と引数の型の整合性をチェックし、型不整合やバッファオーバーフローを防止します。カスタム型のフォーマッタを定義することで、独自のバリデーションロジックを組み込むことができ、複雑なデータ構造の安全な文字列化が可能です。実行時パフォーマンスも優秀で、従来のprintf系関数やiostreamと比較して高速な処理を実現しています。

主な特徴

  • 型安全性: コンパイル時の型チェックによる安全なフォーマット
  • 高性能: printf/iostreamを上回る実行速度
  • バッファ安全: 自動メモリ管理によるオーバーフロー防止
  • カスタマイズ性: ユーザー定義型のフォーマッタ対応
  • 標準準拠: C++20 std::formatの基盤実装
  • 広範囲サポート: C++11以降の幅広いコンパイラ対応

メリット・デメリット

メリット

  • コンパイル時型チェックによる高い安全性
  • バッファオーバーフロー等のセキュリティ問題の防止
  • printf/iostreamより高速な実行性能
  • 読みやすくPython風の直感的な構文
  • 豊富なフォーマットオプション
  • C++20標準の基盤となった実績と信頼性

デメリット

  • 主にフォーマット処理に特化(汎用バリデーションライブラリではない)
  • 複雑なスキーマバリデーション機能は提供しない
  • 学習コストが存在(特にカスタムフォーマッタの作成)
  • 大規模なライブラリ(単純な用途には機能過多)
  • 従来のprintf系コードからの移行作業が必要

参考ページ

書き方の例

インストールと基本セットアップ

# vcpkgを使用してインストール
vcpkg install fmt

# Conanを使用してインストール
conan install fmt/8.1.1@

# CMakeでのfind_package使用例
find_package(fmt REQUIRED)
target_link_libraries(your_target fmt::fmt)

# パッケージマネージャーを使用しない場合
git clone https://github.com/fmtlib/fmt.git
cd fmt
mkdir build && cd build
cmake ..
make -j4

基本的な型安全フォーマットとバリデーション

#include <fmt/format.h>
#include <fmt/printf.h>
#include <fmt/chrono.h>
#include <iostream>
#include <string>
#include <vector>
#include <chrono>

int main() {
    // 基本的な型安全フォーマット
    {
        int number = 42;
        std::string name = "Alice";
        
        // 型安全なフォーマット(コンパイル時チェック)
        std::string message = fmt::format("ユーザー {} の番号は {} です", name, number);
        fmt::print("{}\n", message);
        
        // 位置指定フォーマット
        std::string formatted = fmt::format("{1} years ago, {0} was born", name, 25);
        fmt::print("{}\n", formatted);
    }
    
    // 数値フォーマットとバリデーション
    {
        double pi = 3.14159265359;
        int value = 1234567;
        
        // 小数点以下の桁数指定
        fmt::print("円周率: {:.2f}\n", pi);
        
        // 右寄せ、幅指定
        fmt::print("値: {:>10}\n", value);
        
        // 左寄せ、0埋め
        fmt::print("値: {:0>10}\n", value);
        
        // 中央寄せ
        fmt::print("値: {:^10}\n", value);
        
        // 千の位区切り
        fmt::print("値: {:n}\n", value);
    }
    
    // 文字列フォーマットとバリデーション
    {
        std::string text = "Hello";
        
        // 幅指定と配置
        fmt::print("|{:10}|\n", text);      // 右寄せ
        fmt::print("|{:<10}|\n", text);     // 左寄せ
        fmt::print("|{:^10}|\n", text);     // 中央寄せ
        fmt::print("|{:>10}|\n", text);     // 右寄せ(明示的)
        
        // 切り詰め
        std::string long_text = "This is a very long text";
        fmt::print("切り詰め: {:.10}\n", long_text);
    }
    
    // 時間フォーマットとバリデーション
    {
        auto now = std::chrono::system_clock::now();
        auto time_t = std::chrono::system_clock::to_time_t(now);
        
        // 時間のフォーマット
        fmt::print("現在時刻: {:%Y-%m-%d %H:%M:%S}\n", 
                  fmt::localtime(time_t));
        
        // 期間のフォーマット
        auto duration = std::chrono::milliseconds(1234567);
        fmt::print("期間: {}\n", duration);
    }
    
    return 0;
}

高度な型安全性とエラーハンドリング

#include <fmt/format.h>
#include <fmt/compile.h>
#include <fmt/args.h>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <stdexcept>

// カスタムエラーハンドラー
class ValidationError : public std::runtime_error {
public:
    ValidationError(const std::string& message) : std::runtime_error(message) {}
};

// 型安全なフォーマット関数
template<typename... Args>
std::string safe_format(const std::string& format_str, Args&&... args) {
    try {
        return fmt::format(format_str, std::forward<Args>(args)...);
    } catch (const fmt::format_error& e) {
        throw ValidationError(fmt::format("フォーマットエラー: {}", e.what()));
    }
}

// 条件付きフォーマット
template<typename T>
std::string format_with_validation(const T& value, const std::string& format_str) {
    try {
        return fmt::format(format_str, value);
    } catch (const fmt::format_error& e) {
        throw ValidationError(fmt::format("値 {} のフォーマットに失敗: {}", 
                                        value, e.what()));
    }
}

// 複雑なデータ構造の検証付きフォーマット
struct UserInfo {
    std::string name;
    int age;
    double salary;
    std::vector<std::string> skills;
    std::map<std::string, std::string> metadata;
};

class UserFormatter {
public:
    // 基本情報のフォーマット
    static std::string formatBasicInfo(const UserInfo& user) {
        // 入力値の検証
        if (user.name.empty()) {
            throw ValidationError("ユーザー名が空です");
        }
        
        if (user.age < 0 || user.age > 150) {
            throw ValidationError(fmt::format("無効な年齢: {}", user.age));
        }
        
        if (user.salary < 0) {
            throw ValidationError(fmt::format("無効な給与: {}", user.salary));
        }
        
        return fmt::format("ユーザー: {} ({}歳, 給与: {:,.2f}円)", 
                          user.name, user.age, user.salary);
    }
    
    // 詳細情報のフォーマット
    static std::string formatDetailedInfo(const UserInfo& user) {
        std::string basic = formatBasicInfo(user);
        
        // スキルの検証とフォーマット
        std::string skills_str;
        if (!user.skills.empty()) {
            skills_str = fmt::format("スキル: [{}]", fmt::join(user.skills, ", "));
        } else {
            skills_str = "スキル: なし";
        }
        
        // メタデータの検証とフォーマット
        std::string metadata_str;
        if (!user.metadata.empty()) {
            std::vector<std::string> metadata_parts;
            for (const auto& [key, value] : user.metadata) {
                if (key.empty()) {
                    throw ValidationError("メタデータキーが空です");
                }
                metadata_parts.push_back(fmt::format("{}={}", key, value));
            }
            metadata_str = fmt::format("メタデータ: [{}]", 
                                     fmt::join(metadata_parts, ", "));
        } else {
            metadata_str = "メタデータ: なし";
        }
        
        return fmt::format("{}\n{}\n{}", basic, skills_str, metadata_str);
    }
    
    // HTMLフォーマット
    static std::string formatAsHtml(const UserInfo& user) {
        std::string basic = formatBasicInfo(user);
        
        // HTMLエスケープ処理
        auto escape_html = [](const std::string& text) -> std::string {
            std::string result;
            for (char c : text) {
                switch (c) {
                    case '<': result += "&lt;"; break;
                    case '>': result += "&gt;"; break;
                    case '&': result += "&amp;"; break;
                    case '"': result += "&quot;"; break;
                    case '\'': result += "&#39;"; break;
                    default: result += c; break;
                }
            }
            return result;
        };
        
        return fmt::format(
            R"(<div class="user-info">
    <h3>{}</h3>
    <p>年齢: {}</p>
    <p>給与: {:,.2f}円</p>
    <p>スキル: {}</p>
</div>)",
            escape_html(user.name),
            user.age,
            user.salary,
            escape_html(fmt::join(user.skills, ", "))
        );
    }
};

int main() {
    try {
        // 基本的な型安全フォーマット
        {
            std::string name = "田中太郎";
            int age = 30;
            double salary = 5000000.50;
            
            std::string result = safe_format("ユーザー: {} ({}歳, 給与: {:,.2f}円)", 
                                           name, age, salary);
            fmt::print("{}\n", result);
        }
        
        // 複雑なデータ構造の検証付きフォーマット
        {
            UserInfo user = {
                "佐藤花子",
                28,
                4500000.75,
                {"C++", "Python", "JavaScript", "SQL"},
                {{"department", "Engineering"}, {"location", "Tokyo"}, {"level", "Senior"}}
            };
            
            std::string basic = UserFormatter::formatBasicInfo(user);
            fmt::print("基本情報:\n{}\n\n", basic);
            
            std::string detailed = UserFormatter::formatDetailedInfo(user);
            fmt::print("詳細情報:\n{}\n\n", detailed);
            
            std::string html = UserFormatter::formatAsHtml(user);
            fmt::print("HTML形式:\n{}\n\n", html);
        }
        
        // エラーハンドリングの例
        {
            UserInfo invalid_user = {
                "",  // 空の名前
                -5,  // 無効な年齢
                1000000.0,
                {},
                {}
            };
            
            try {
                std::string result = UserFormatter::formatBasicInfo(invalid_user);
                fmt::print("{}\n", result);
            } catch (const ValidationError& e) {
                fmt::print("バリデーションエラー: {}\n", e.what());
            }
        }
        
        // 動的フォーマット文字列の検証
        {
            std::vector<std::string> format_templates = {
                "値: {}",
                "値: {:.2f}",
                "値: {:>10}",
                "無効: {:.2f} と {}"  // 引数不足でエラー
            };
            
            double value = 123.456;
            
            for (const auto& template_str : format_templates) {
                try {
                    std::string result = format_with_validation(value, template_str);
                    fmt::print("成功: {}\n", result);
                } catch (const ValidationError& e) {
                    fmt::print("エラー: {}\n", e.what());
                }
            }
        }
        
    } catch (const std::exception& e) {
        fmt::print("予期しないエラー: {}\n", e.what());
        return 1;
    }
    
    return 0;
}

コンパイル時最適化とカスタムフォーマッタ

#include <fmt/format.h>
#include <fmt/compile.h>
#include <fmt/ostream.h>
#include <iostream>
#include <string>
#include <vector>
#include <chrono>
#include <regex>

// カスタム型の定義
struct Point {
    double x, y;
    
    Point(double x, double y) : x(x), y(y) {}
    
    // 検証機能付きのコンストラクタ
    static Point createValidated(double x, double y) {
        if (std::isnan(x) || std::isnan(y)) {
            throw std::invalid_argument("座標にNaNが含まれています");
        }
        
        if (std::isinf(x) || std::isinf(y)) {
            throw std::invalid_argument("座標に無限大が含まれています");
        }
        
        return Point(x, y);
    }
    
    // 距離の計算
    double distance() const {
        return std::sqrt(x * x + y * y);
    }
    
    // 検証機能
    bool isValid() const {
        return !std::isnan(x) && !std::isnan(y) && 
               !std::isinf(x) && !std::isinf(y);
    }
};

// Pointのカスタムフォーマッタ
template<>
struct fmt::formatter<Point> {
    enum class format_type { coordinate, polar, json };
    format_type type = format_type::coordinate;
    int precision = 2;
    
    // フォーマット文字列の解析
    constexpr auto parse(format_parse_context& ctx) {
        auto it = ctx.begin();
        auto end = ctx.end();
        
        if (it != end) {
            switch (*it) {
                case 'c': type = format_type::coordinate; break;
                case 'p': type = format_type::polar; break;
                case 'j': type = format_type::json; break;
                default: 
                    throw fmt::format_error("無効なフォーマットタイプ");
            }
            ++it;
        }
        
        // 精度の解析
        if (it != end && *it == '.') {
            ++it;
            if (it != end && *it >= '0' && *it <= '9') {
                precision = *it - '0';
                ++it;
            }
        }
        
        return it;
    }
    
    // フォーマット実行
    template<typename FormatContext>
    auto format(const Point& p, FormatContext& ctx) {
        // 値の検証
        if (!p.isValid()) {
            return fmt::format_to(ctx.out(), "Invalid Point");
        }
        
        switch (type) {
            case format_type::coordinate:
                return fmt::format_to(ctx.out(), "({:.{}f}, {:.{}f})", 
                                    p.x, precision, p.y, precision);
            case format_type::polar: {
                double r = p.distance();
                double theta = std::atan2(p.y, p.x);
                return fmt::format_to(ctx.out(), "r={:.{}f}, θ={:.{}f}", 
                                    r, precision, theta, precision);
            }
            case format_type::json:
                return fmt::format_to(ctx.out(), "{{\"x\":{:.{}f},\"y\":{:.{}f}}}", 
                                    p.x, precision, p.y, precision);
        }
        
        return ctx.out();
    }
};

// 複雑なデータ構造
struct GeometryData {
    std::string name;
    std::vector<Point> points;
    std::string color;
    double area;
    
    // 検証機能
    bool isValid() const {
        if (name.empty()) return false;
        if (points.empty()) return false;
        if (area < 0) return false;
        
        for (const auto& point : points) {
            if (!point.isValid()) return false;
        }
        
        // 色の検証(HTML色コード)
        std::regex color_regex("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
        if (!std::regex_match(color, color_regex)) {
            return false;
        }
        
        return true;
    }
};

// GeometryDataのカスタムフォーマッタ
template<>
struct fmt::formatter<GeometryData> {
    enum class format_type { simple, detailed, json };
    format_type type = format_type::simple;
    
    constexpr auto parse(format_parse_context& ctx) {
        auto it = ctx.begin();
        auto end = ctx.end();
        
        if (it != end) {
            switch (*it) {
                case 's': type = format_type::simple; break;
                case 'd': type = format_type::detailed; break;
                case 'j': type = format_type::json; break;
                default: 
                    throw fmt::format_error("無効なフォーマットタイプ");
            }
            ++it;
        }
        
        return it;
    }
    
    template<typename FormatContext>
    auto format(const GeometryData& geo, FormatContext& ctx) {
        if (!geo.isValid()) {
            return fmt::format_to(ctx.out(), "Invalid GeometryData");
        }
        
        switch (type) {
            case format_type::simple:
                return fmt::format_to(ctx.out(), "{} ({}点, 面積: {:.2f})", 
                                    geo.name, geo.points.size(), geo.area);
            
            case format_type::detailed: {
                auto out = fmt::format_to(ctx.out(), 
                    "図形: {}\n色: {}\n面積: {:.2f}\n頂点数: {}\n頂点座標:\n", 
                    geo.name, geo.color, geo.area, geo.points.size());
                
                for (size_t i = 0; i < geo.points.size(); ++i) {
                    out = fmt::format_to(out, "  {}: {:.2f}\n", i + 1, geo.points[i]);
                }
                return out;
            }
            
            case format_type::json: {
                auto out = fmt::format_to(ctx.out(), 
                    "{{\"name\":\"{}\",\"color\":\"{}\",\"area\":{:.2f},\"points\":[", 
                    geo.name, geo.color, geo.area);
                
                for (size_t i = 0; i < geo.points.size(); ++i) {
                    if (i > 0) out = fmt::format_to(out, ",");
                    out = fmt::format_to(out, "{:j}", geo.points[i]);
                }
                
                return fmt::format_to(out, "]}");
            }
        }
        
        return ctx.out();
    }
};

// パフォーマンステスト
class PerformanceTest {
public:
    static void testCompiledFormat() {
        constexpr auto compiled_format = fmt::compile("値: {}, 名前: {}");
        
        auto start = std::chrono::high_resolution_clock::now();
        
        for (int i = 0; i < 100000; ++i) {
            std::string result = fmt::format(compiled_format, i, "test");
        }
        
        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        
        fmt::print("コンパイル済みフォーマット: {} μs\n", duration.count());
    }
    
    static void testRuntimeFormat() {
        const std::string format_str = "値: {}, 名前: {}";
        
        auto start = std::chrono::high_resolution_clock::now();
        
        for (int i = 0; i < 100000; ++i) {
            std::string result = fmt::format(format_str, i, "test");
        }
        
        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        
        fmt::print("実行時フォーマット: {} μs\n", duration.count());
    }
};

int main() {
    try {
        // カスタムフォーマッタの使用例
        {
            Point p1 = Point::createValidated(3.14159, 2.71828);
            Point p2 = Point::createValidated(std::nan(""), 1.0);  // 無効な座標
            
            fmt::print("座標形式: {:.3f}\n", p1);
            fmt::print("極座標形式: {:p.3f}\n", p1);
            fmt::print("JSON形式: {:j.3f}\n", p1);
            fmt::print("無効な座標: {:.2f}\n", p2);
        }
        
        // 複雑なデータ構造のフォーマット
        {
            GeometryData triangle = {
                "三角形",
                {Point(0, 0), Point(3, 0), Point(1.5, 2.6)},
                "#FF0000",
                3.9
            };
            
            fmt::print("シンプル形式:\n{:s}\n\n", triangle);
            fmt::print("詳細形式:\n{:d}\n", triangle);
            fmt::print("JSON形式:\n{:j}\n\n", triangle);
        }
        
        // 無効なデータのテスト
        {
            GeometryData invalid_geo = {
                "",  // 空の名前
                {Point(1, 1)},
                "invalid_color",  // 無効な色
                -1.0  // 負の面積
            };
            
            fmt::print("無効なデータ: {:s}\n", invalid_geo);
        }
        
        // パフォーマンステスト
        {
            fmt::print("パフォーマンステスト結果:\n");
            PerformanceTest::testCompiledFormat();
            PerformanceTest::testRuntimeFormat();
        }
        
    } catch (const std::exception& e) {
        fmt::print("エラー: {}\n", e.what());
        return 1;
    }
    
    return 0;
}

国際化対応と高度なバリデーション

#include <fmt/format.h>
#include <fmt/locale.h>
#include <fmt/chrono.h>
#include <iostream>
#include <string>
#include <locale>
#include <map>
#include <regex>
#include <optional>

// 多言語対応のメッセージクラス
class LocalizedMessages {
private:
    std::map<std::string, std::map<std::string, std::string>> messages;
    std::string current_locale;
    
public:
    LocalizedMessages() : current_locale("ja") {
        // 日本語メッセージ
        messages["ja"] = {
            {"validation_error", "バリデーションエラー: {}"},
            {"invalid_email", "無効なメールアドレス: {}"},
            {"invalid_phone", "無効な電話番号: {}"},
            {"invalid_date", "無効な日付: {}"},
            {"required_field", "必須フィールド '{}' が空です"},
            {"out_of_range", "値 {} は範囲 {} - {} 外です"},
            {"user_info", "ユーザー: {} ({}歳, {})"}
        };
        
        // 英語メッセージ
        messages["en"] = {
            {"validation_error", "Validation error: {}"},
            {"invalid_email", "Invalid email address: {}"},
            {"invalid_phone", "Invalid phone number: {}"},
            {"invalid_date", "Invalid date: {}"},
            {"required_field", "Required field '{}' is empty"},
            {"out_of_range", "Value {} is out of range {} - {}"},
            {"user_info", "User: {} ({}y/o, {})"}
        };
    }
    
    void setLocale(const std::string& locale) {
        if (messages.find(locale) != messages.end()) {
            current_locale = locale;
        }
    }
    
    template<typename... Args>
    std::string format(const std::string& key, Args&&... args) {
        auto locale_it = messages.find(current_locale);
        if (locale_it == messages.end()) {
            throw std::runtime_error("未サポートのロケール: " + current_locale);
        }
        
        auto message_it = locale_it->second.find(key);
        if (message_it == locale_it->second.end()) {
            throw std::runtime_error("未定義のメッセージキー: " + key);
        }
        
        return fmt::format(message_it->second, std::forward<Args>(args)...);
    }
};

// 高度なバリデーション機能
class AdvancedValidator {
private:
    LocalizedMessages messages;
    
    // メールアドレスの検証
    bool isValidEmail(const std::string& email) {
        static const std::regex email_regex(
            R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)"
        );
        return std::regex_match(email, email_regex);
    }
    
    // 電話番号の検証(日本の形式)
    bool isValidPhoneNumber(const std::string& phone) {
        static const std::regex phone_regex(
            R"(^(\+81|0)[1-9][0-9]{8,9}$)"
        );
        return std::regex_match(phone, phone_regex);
    }
    
    // 日付の検証
    bool isValidDate(const std::string& date) {
        static const std::regex date_regex(
            R"(^\d{4}-\d{2}-\d{2}$)"
        );
        return std::regex_match(date, date_regex);
    }
    
public:
    void setLocale(const std::string& locale) {
        messages.setLocale(locale);
    }
    
    // 文字列フィールドの検証
    std::optional<std::string> validateString(const std::string& value, 
                                            const std::string& field_name,
                                            bool required = true,
                                            size_t min_length = 0,
                                            size_t max_length = std::numeric_limits<size_t>::max()) {
        if (value.empty() && required) {
            return messages.format("required_field", field_name);
        }
        
        if (!value.empty() && (value.length() < min_length || value.length() > max_length)) {
            return fmt::format("フィールド '{}' の長さは {} - {} 文字である必要があります", 
                             field_name, min_length, max_length);
        }
        
        return std::nullopt;
    }
    
    // 数値の範囲検証
    template<typename T>
    std::optional<std::string> validateRange(const T& value, 
                                           const std::string& field_name,
                                           const T& min_val, 
                                           const T& max_val) {
        if (value < min_val || value > max_val) {
            return messages.format("out_of_range", value, min_val, max_val);
        }
        return std::nullopt;
    }
    
    // メールアドレスの検証
    std::optional<std::string> validateEmail(const std::string& email) {
        if (!isValidEmail(email)) {
            return messages.format("invalid_email", email);
        }
        return std::nullopt;
    }
    
    // 電話番号の検証
    std::optional<std::string> validatePhoneNumber(const std::string& phone) {
        if (!isValidPhoneNumber(phone)) {
            return messages.format("invalid_phone", phone);
        }
        return std::nullopt;
    }
    
    // 日付の検証
    std::optional<std::string> validateDate(const std::string& date) {
        if (!isValidDate(date)) {
            return messages.format("invalid_date", date);
        }
        return std::nullopt;
    }
    
    // 複合バリデーション
    std::vector<std::string> validateUserData(const std::map<std::string, std::string>& data) {
        std::vector<std::string> errors;
        
        // 名前の検証
        auto name_it = data.find("name");
        if (name_it != data.end()) {
            auto error = validateString(name_it->second, "name", true, 2, 50);
            if (error) errors.push_back(*error);
        } else {
            errors.push_back(messages.format("required_field", "name"));
        }
        
        // メールアドレスの検証
        auto email_it = data.find("email");
        if (email_it != data.end()) {
            auto error = validateEmail(email_it->second);
            if (error) errors.push_back(*error);
        } else {
            errors.push_back(messages.format("required_field", "email"));
        }
        
        // 電話番号の検証(任意)
        auto phone_it = data.find("phone");
        if (phone_it != data.end() && !phone_it->second.empty()) {
            auto error = validatePhoneNumber(phone_it->second);
            if (error) errors.push_back(*error);
        }
        
        // 年齢の検証
        auto age_it = data.find("age");
        if (age_it != data.end()) {
            try {
                int age = std::stoi(age_it->second);
                auto error = validateRange(age, "age", 0, 120);
                if (error) errors.push_back(*error);
            } catch (const std::exception&) {
                errors.push_back(fmt::format("年齢 '{}' は無効な数値です", age_it->second));
            }
        } else {
            errors.push_back(messages.format("required_field", "age"));
        }
        
        return errors;
    }
};

// 使用例
int main() {
    try {
        AdvancedValidator validator;
        
        // 日本語でのテスト
        {
            fmt::print("=== 日本語バリデーション ===\n");
            validator.setLocale("ja");
            
            std::map<std::string, std::string> user_data = {
                {"name", "田中太郎"},
                {"email", "[email protected]"},
                {"phone", "09012345678"},
                {"age", "30"}
            };
            
            auto errors = validator.validateUserData(user_data);
            
            if (errors.empty()) {
                fmt::print("✓ バリデーション成功: {}\n", 
                          fmt::format("ユーザー: {} ({}歳, {})", 
                                    user_data["name"], 
                                    user_data["age"], 
                                    user_data["email"]));
            } else {
                fmt::print("✗ バリデーションエラー:\n");
                for (const auto& error : errors) {
                    fmt::print("  - {}\n", error);
                }
            }
        }
        
        // 英語でのテスト
        {
            fmt::print("\n=== English Validation ===\n");
            validator.setLocale("en");
            
            std::map<std::string, std::string> user_data = {
                {"name", "A"},  // 短すぎる名前
                {"email", "invalid-email"},  // 無効なメール
                {"phone", "123"},  // 無効な電話番号
                {"age", "150"}  // 範囲外の年齢
            };
            
            auto errors = validator.validateUserData(user_data);
            
            if (errors.empty()) {
                fmt::print("✓ Validation successful\n");
            } else {
                fmt::print("✗ Validation errors:\n");
                for (const auto& error : errors) {
                    fmt::print("  - {}\n", error);
                }
            }
        }
        
        // 高度なフォーマット機能
        {
            fmt::print("\n=== 高度なフォーマット ===\n");
            
            // 数値の地域別フォーマット
            double large_number = 1234567.89;
            fmt::print("日本語形式: {:.2Lf}\n", large_number);
            
            // 時間の地域別フォーマット
            auto now = std::chrono::system_clock::now();
            auto time_t = std::chrono::system_clock::to_time_t(now);
            fmt::print("現在時刻: {:%Y年%m月%d日 %H:%M:%S}\n", 
                      fmt::localtime(time_t));
            
            // 複雑なデータ構造のフォーマット
            std::vector<std::string> items = {"りんご", "みかん", "バナナ"};
            fmt::print("アイテム: [{}]\n", fmt::join(items, ", "));
            
            // 条件付きフォーマット
            bool has_items = !items.empty();
            fmt::print("アイテム状況: {}\n", 
                      has_items ? fmt::format("{}個のアイテム", items.size()) : "アイテムなし");
        }
        
    } catch (const std::exception& e) {
        fmt::print("エラー: {}\n", e.what());
        return 1;
    }
    
    return 0;
}