C++ enum クラスとは:強力な型安全性をもたらす新機能
従来の enum の問題点と限界
従来のC++におけるenum(列挙型)には、いくつかの重大な問題点が存在していました:
- 名前空間の汚染
// 従来のenum enum Color { RED, GREEN, BLUE }; enum TrafficLight { RED, YELLOW, GREEN }; // コンパイルエラー:REDとGREENが重複
- 型安全性の欠如
enum OldEnum { A = 1, B = 2 }; int x = A; // 暗黙の型変換が可能 if (x == 1) { /* ... */ } // 数値との直接比較が可能
- サイズ指定の制限
// 従来のenumはサイズ指定が柔軟でない enum SmallEnum { X = 1, Y = 2 }; // コンパイラが自動的にサイズを決定
enum クラスが解決する3つの重要な課題
C++11で導入されたenum classは、これらの問題を効果的に解決します:
- スコープの制御
enum class Color { RED, GREEN, BLUE }; enum class TrafficLight { RED, YELLOW, GREEN }; // 問題なくコンパイル可能 // 使用時は明示的なスコープ指定が必要 Color c = Color::RED;
- 強力な型安全性
enum class NewEnum { A = 1, B = 2 }; int x = NewEnum::A; // コンパイルエラー:暗黙の型変換が禁止 if (x == 1) { /* ... */ } // コンパイルエラー:数値との直接比較が禁止 if (x == NewEnum::A) { /* ... */ } // OK:同じ型との比較は可能
- 柔軟な基底型指定
enum class BigEnum : uint64_t { // 明示的なサイズ指定が可能 LARGE_VALUE = 0xFFFFFFFFFFFFFFFF };
enum classがもたらす追加のメリット
- コードの可読性向上
- 列挙子の使用箇所が明確になる
- 意図しない型変換を防ぐことでバグの発見が容易に
- 保守性の向上
- スコープ付きのため、名前の衝突リスクが低減
- コンパイラによる型チェックが強化され、早期のバグ発見が可能
- 将来の拡張性
enum class Status : uint8_t { OK = 0, ERROR = 1, PENDING = 2 // 後から新しい値を追加しても、既存のコードに影響を与えにくい };
これらの特徴により、enum classは特に大規模プロジェクトやチーム開発において、コードの品質と保守性を大きく向上させる重要な機能となっています。型安全性と明確なスコープ制御により、多くの一般的なプログラミングエラーを防ぐことができ、より堅牢なソフトウェア開発を実現します。
enum クラスの基本的な使い方と実装方法
enum クラスの宣言と定義の正しい方法
enum classの基本的な宣言方法から、より高度な使用方法まで段階的に解説します。
- 基本的な宣言方法
// 最もシンプルな宣言 enum class SimpleStatus { OK, // 0 ERROR, // 1 PENDING // 2 }; // 基底型を指定した宣言 enum class Flags : uint8_t { NONE = 0x00, READ = 0x01, WRITE = 0x02, EXECUTE = 0x04 };
- 初期値の明示的な指定
enum class HttpStatus : uint16_t { OK = 200, CREATED = 201, BAD_REQUEST = 400, NOT_FOUND = 404, SERVER_ERROR = 500 };
- クラス内での enum class の使用
class FileSystem { public: enum class Permission : uint8_t { NONE = 0, READ = 1 << 0, WRITE = 1 << 1, EXECUTE = 1 << 2, ALL = READ | WRITE | EXECUTE }; bool checkPermission(Permission perm) { return (m_currentPermission & static_cast<uint8_t>(perm)) != 0; } private: Permission m_currentPermission{Permission::NONE}; };
スコープ付き列挙型のベストプラクティス
- 命名規則とスタイル
// 推奨される命名規則 enum class LogLevel { // パスカルケースを使用 Debug, // 各要素はアッパーキャメルケース Info, Warning, Error, Critical }; // 意味のある名前と値の使用 enum class DayOfWeek { Sunday = 0, // 明示的な値の割り当て Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6 };
- ユーティリティ関数の実装
enum class Temperature : int { Freezing = 0, Cold = 10, Normal = 20, Warm = 25, Hot = 30 }; // 文字列変換用のユーティリティ関数 std::string temperatureToString(Temperature temp) { switch (temp) { case Temperature::Freezing: return "Freezing"; case Temperature::Cold: return "Cold"; case Temperature::Normal: return "Normal"; case Temperature::Warm: return "Warm"; case Temperature::Hot: return "Hot"; default: return "Unknown"; } } // 数値からの変換関数 Temperature intToTemperature(int value) { if (value <= 0) return Temperature::Freezing; else if (value <= 10) return Temperature::Cold; else if (value <= 20) return Temperature::Normal; else if (value <= 25) return Temperature::Warm; else return Temperature::Hot; }
- 比較演算子のオーバーロード
enum class Priority : uint8_t { Low = 0, Medium = 1, High = 2, Critical = 3 }; // 比較演算子のオーバーロード constexpr bool operator<(Priority lhs, Priority rhs) { return static_cast<uint8_t>(lhs) < static_cast<uint8_t>(rhs); } // 使用例 void processByPriority(Priority p) { if (p < Priority::High) { // 低優先度の処理 } else { // 高優先度の処理 } }
- ビット演算のサポート
enum class Permissions : unsigned int { None = 0, Read = 1 << 0, // 1 Write = 1 << 1, // 2 Execute = 1 << 2 // 4 }; // ビット演算子のオーバーロード constexpr Permissions operator|(Permissions lhs, Permissions rhs) { return static_cast<Permissions>( static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs) ); } // 使用例 Permissions p = Permissions::Read | Permissions::Write;
これらのベストプラクティスを適用することで、より保守性が高く、型安全なコードを実現できます。特に大規模プロジェクトでは、これらの規則を一貫して適用することで、コードの品質と可読性を大きく向上させることができます。
enum classによる型安全性の向上
暗黙の型変換テクニックを防ぐ具体的な方法
enum classは、C++における型安全性を強化する重要な機能です。以下に、具体的な実装例と防御的プログラミング手法を示します。
- 暗黙の型変換の防止
// 従来のenumでの問題 enum OldColor { RED, GREEN, BLUE }; int color = RED; // 暗黙の型変換が可能 int value = 2; OldColor c = value; // 危険な暗黙の型変換 // enum classでの改善 enum class Color { Red, Green, Blue }; // int color = Color::Red; // コンパイルエラー // Color c = 2; // コンパイルエラー // 必要な場合は明示的なキャストが必要 int color = static_cast<int>(Color::Red); // OK Color c = static_cast<Color>(2); // OK
- 型安全な演算子の実装
enum class Direction { North, South, East, West }; // 安全な演算子のオーバーロード Direction operator+(Direction d1, Direction d2) = delete; // 加算を禁止 Direction operator-(Direction d1, Direction d2) = delete; // 減算を禁止 // 必要な操作のみを許可 Direction getOppositeDirection(Direction d) { switch (d) { case Direction::North: return Direction::South; case Direction::South: return Direction::North; case Direction::East: return Direction::West; case Direction::West: return Direction::East; default: throw std::invalid_argument("Invalid direction"); } }
コンパイル時の型チェックを最大限活用する
- static_assert によるコンパイル時チェック
enum class StatusCode : uint16_t { OK = 200, NotFound = 404, ServerError = 500 }; // 基底型のサイズチェック static_assert(sizeof(StatusCode) == sizeof(uint16_t), "StatusCode must be 16 bits"); // 値の範囲チェック static_assert(static_cast<uint16_t>(StatusCode::OK) < 600, "Invalid HTTP status code");
- テンプレートを使用した型安全性の強化
template<typename EnumType> class EnumWrapper { static_assert(std::is_enum_v<EnumType>, "EnumWrapper can only be used with enum types"); public: explicit EnumWrapper(EnumType value) : m_value(value) {} EnumType get() const { return m_value; } // 型安全な比較演算子 bool operator==(const EnumWrapper& other) const { return m_value == other.m_value; } private: EnumType m_value; }; // 使用例 enum class Priority { Low, Medium, High }; EnumWrapper<Priority> p(Priority::Medium); // EnumWrapper<int> invalid; // コンパイルエラー
- constexpr による型安全な変換関数
enum class Temperature : int { Freezing = 0, Boiling = 100 }; class TemperatureConverter { public: static constexpr double toCelsius(Temperature t) { return static_cast<double>(t); } static constexpr Temperature fromCelsius(double celsius) { if constexpr (celsius <= 0.0) { return Temperature::Freezing; } else if constexpr (celsius >= 100.0) { return Temperature::Boiling; } throw std::out_of_range("Temperature out of range"); } };
- 型安全なビット演算の実装
enum class Permissions : unsigned int { None = 0, Read = 1 << 0, Write = 1 << 1, Execute = 1 << 2 }; // 型安全なビット演算子 constexpr Permissions operator|(Permissions lhs, Permissions rhs) { return static_cast<Permissions>( static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs) ); } constexpr bool hasPermission(Permissions current, Permissions required) { return (static_cast<unsigned int>(current) & static_cast<unsigned int>(required)) == static_cast<unsigned int>(required); } // 使用例 Permissions userPerms = Permissions::Read | Permissions::Write; bool canExecute = hasPermission(userPerms, Permissions::Execute); // false
これらの手法を適切に組み合わせることで、コンパイル時に多くのエラーを検出し、実行時の予期せぬ動作を防ぐことができます。特に大規模なプロジェクトでは、この層の型安全性が重要な役割を果たします。
実践的なenum classの活用パターン
ステートマシンの実装例とポイント
ステートマシンは、enum classの最も実用的な活用例の1つです。以下に、具体的な実装例を示します。
- 基本的なステートマシンの実装
class DocumentProcessor { public: enum class State { Draft, UnderReview, Approved, Published, Archived }; enum class Event { Submit, Approve, Reject, Publish, Archive }; private: State currentState{State::Draft}; // 状態遷移テーブル static constexpr bool isValidTransition(State from, Event event) { switch (from) { case State::Draft: return event == Event::Submit; case State::UnderReview: return event == Event::Approve || event == Event::Reject; case State::Approved: return event == Event::Publish; case State::Published: return event == Event::Archive; case State::Archived: return false; default: return false; } } public: bool processEvent(Event event) { if (!isValidTransition(currentState, event)) { return false; } switch (currentState) { case State::Draft: if (event == Event::Submit) { currentState = State::UnderReview; } break; case State::UnderReview: if (event == Event::Approve) { currentState = State::Approved; } else if (event == Event::Reject) { currentState = State::Draft; } break; // 他の状態の遷移処理... } return true; } };
- 型安全なステート遷移の実装
template<typename State, typename Event> class StateMachine { static_assert(std::is_enum_v<State>, "State must be an enum class"); static_assert(std::is_enum_v<Event>, "Event must be an enum class"); public: using TransitionCallback = std::function<void(State, State)>; void addTransition(State from, Event event, State to) { transitions[std::make_pair(from, event)] = to; } void addCallback(TransitionCallback callback) { callbacks.push_back(callback); } bool process(Event event) { auto key = std::make_pair(currentState, event); auto it = transitions.find(key); if (it != transitions.end()) { State oldState = currentState; currentState = it->second; for (const auto& callback : callbacks) { callback(oldState, currentState); } return true; } return false; } private: State currentState; std::map<std::pair<State, Event>, State> transitions; std::vector<TransitionCallback> callbacks; };
設定値の管理における活用方法
enum classは設定値の管理にも効果的です。以下に実践的な例を示します。
- アプリケーション設定の管理
class AppConfig { public: enum class LogLevel : uint8_t { Debug = 0, Info = 1, Warning = 2, Error = 3, Critical = 4 }; enum class Theme { Light, Dark, System }; struct Settings { LogLevel logLevel{LogLevel::Info}; Theme theme{Theme::System}; bool notifications{true}; }; // 設定値の検証と適用 bool applySettings(const Settings& newSettings) { if (!validateSettings(newSettings)) { return false; } settings = newSettings; notifySettingsChanged(); return true; } private: Settings settings; bool validateSettings(const Settings& s) { // LogLevelの範囲チェック if (static_cast<uint8_t>(s.logLevel) > static_cast<uint8_t>(LogLevel::Critical)) { return false; } return true; } void notifySettingsChanged() { // 設定変更通知の実装 } };
- 型安全な設定値アクセサ
class DatabaseConfig { public: enum class IsolationLevel { ReadUncommitted, ReadCommitted, RepeatableRead, Serializable }; enum class ConnectionMode { ReadOnly, ReadWrite, Admin }; private: struct Config { IsolationLevel isolation{IsolationLevel::ReadCommitted}; ConnectionMode mode{ConnectionMode::ReadOnly}; std::chrono::seconds timeout{30}; }; Config config; public: template<typename T> class ConfigValue { public: explicit ConfigValue(T& value) : value(value) {} operator T() const { return value; } ConfigValue& operator=(const T& newValue) { value = newValue; return *this; } private: T& value; }; ConfigValue<IsolationLevel> isolation() { return ConfigValue<IsolationLevel>(config.isolation); } ConfigValue<ConnectionMode> mode() { return ConfigValue<ConnectionMode>(config.mode); } };
これらのパターンを活用することで、型安全性を保ちながら柔軟な設定管理とステート管理を実現できます。特に大規模なアプリケーションでは、これらのパターンが保守性と信頼性の向上に大きく貢献します。
enum classのパフォーマンスと最適化
メモリ使用量の最適化テクニック
enum classのメモリ使用を最適化するための具体的な手法を解説します。
- 適切な基底型の選択
// メモリ効率の良い実装 enum class SmallEnum : uint8_t { // 1バイト A, B, C }; enum class MediumEnum : uint16_t { // 2バイト X = 256, // uint8_tでは表現できない Y, Z }; // メモリ使用量の比較 static_assert(sizeof(SmallEnum) == 1, "SmallEnum should be 1 byte"); static_assert(sizeof(MediumEnum) == 2, "MediumEnum should be 2 bytes");
- メモリアライメントの最適化
// 構造体でのenum classの配置最適化 struct OptimizedStruct { enum class Flag : uint8_t { On, Off }; Flag flag1; // 1バイト Flag flag2; // 1バイト uint16_t value; // 2バイト // 合計4バイト }; // 非最適化の例 struct UnoptimizedStruct { enum class Status : uint32_t { // 4バイト Active, Inactive }; uint8_t data; // 1バイト Status status; // 4バイト // パディングにより合計12バイト }; static_assert(sizeof(OptimizedStruct) == 4); static_assert(sizeof(UnoptimizedStruct) == 12);
実行時オーバーヘッドの削減方法
- スイッチ文の最適化
enum class Operation : uint8_t { Add, Subtract, Multiply, Divide }; // 最適化されたスイッチ文の実装 constexpr int calculate(Operation op, int a, int b) { switch (op) { // コンパイラによりジャンプテーブルが生成される case Operation::Add: return a + b; case Operation::Subtract: return a - b; case Operation::Multiply: return a * b; case Operation::Divide: return a / b; } __builtin_unreachable(); // コンパイラの最適化を補助 }
- ルックアップテーブルの活用
class EnumLookup { public: enum class ErrorCode : uint8_t { None = 0, InvalidInput = 1, NetworkError = 2, DatabaseError = 3 // 他のエラーコード }; private: // コンパイル時に生成されるルックアップテーブル static constexpr const char* ERROR_MESSAGES[] = { "No error", "Invalid input provided", "Network connection failed", "Database operation failed" }; public: // O(1)での高速なメッセージ取得 static constexpr const char* getErrorMessage(ErrorCode code) { return ERROR_MESSAGES[static_cast<size_t>(code)]; } };
- パフォーマンス最適化テクニック
class OptimizedStateManager { public: enum class State : uint8_t { Idle, Running, Paused, Stopped }; private: // ビットフィールドを使用した最適化 struct StateFlags { uint8_t isActive : 1; uint8_t isPaused : 1; uint8_t reserved : 6; }; union { State state; StateFlags flags; uint8_t rawValue; }; public: // 高速な状態チェック bool isActive() const { return flags.isActive; } bool isPaused() const { return flags.isPaused; } void setState(State newState) { state = newState; } };
- キャッシュ効率の改善
class CacheOptimizedEnum { public: enum class CacheGroup : uint8_t { Hot, // 頻繁にアクセス Warm, // 時々アクセス Cold // めったにアクセスしない }; struct Data { CacheGroup group; uint8_t padding[3]; // アライメント最適化 int64_t value; }; // キャッシュ効率を考慮したデータ構造 std::array<Data, 1024> hotData; // 頻繁にアクセス std::array<Data, 4096> warmData; // 時々アクセス std::array<Data, 8192> coldData; // めったにアクセスしない // キャッシュ効率の良いアクセス方法 Data* findData(uint32_t id, CacheGroup group) { switch (group) { case CacheGroup::Hot: return id < hotData.size() ? &hotData[id] : nullptr; case CacheGroup::Warm: return id < warmData.size() ? &warmData[id] : nullptr; case CacheGroup::Cold: return id < coldData.size() ? &coldData[id] : nullptr; } return nullptr; } };
これらの最適化テクニックを適切に組み合わせることで、enum classを使用しながらもパフォーマンスを最大限に引き出すことができます。特に組み込みシステムや高性能が要求されるアプリケーションでは、これらの最適化が重要な役割を果たします。
enum クラスを使用する際の注意点と対処法
一般的な落とし穴とその回避方法
enum classを使用する際によく遭遇する問題とその解決策を解説します。
- 型変換に関する問題
enum class Status { OK, Error }; // 問題のあるコード void problematicFunction() { Status s = Status::OK; if (s) {} // コンパイルエラー:暗黙的なboolへの変換ができない int i = s; // コンパイルエラー:暗黙的な整数への変換ができない } // 解決策:明示的な変換関数の提供 class StatusHandler { public: static bool isValid(Status s) { return s == Status::OK; } static int toInt(Status s) { return static_cast<int>(s); } }; void improvedFunction() { Status s = Status::OK; if (StatusHandler::isValid(s)) {} // OK int i = StatusHandler::toInt(s); // OK }
- シリアライゼーションの問題
enum class Color { Red, Green, Blue }; // 問題:直接的な文字列変換ができない // 解決策:変換関数の実装 class ColorConverter { public: static std::string toString(Color c) { switch (c) { case Color::Red: return "Red"; case Color::Green: return "Green"; case Color::Blue: return "Blue"; default: throw std::invalid_argument("Unknown color"); } } static Color fromString(const std::string& s) { if (s == "Red") return Color::Red; if (s == "Green") return Color::Green; if (s == "Blue") return Color::Blue; throw std::invalid_argument("Invalid color string"); } };
- 範囲チェックとバリデーション
enum class Month : uint8_t { January = 1, February, March, // ... December = 12 }; class MonthValidator { public: static bool isValid(uint8_t value) { return value >= static_cast<uint8_t>(Month::January) && value <= static_cast<uint8_t>(Month::December); } static Month fromInt(int value) { if (!isValid(static_cast<uint8_t>(value))) { throw std::out_of_range("Invalid month value"); } return static_cast<Month>(value); } };
バックワード互換性の確保
- 既存コードとの互換性維持
// 従来のenum enum OldStatus { OLD_STATUS_OK, OLD_STATUS_ERROR }; // 新しいenum class enum class NewStatus { OK, Error }; // 互換性レイヤーの実装 class StatusCompatibility { public: static OldStatus toOldStatus(NewStatus status) { switch (status) { case NewStatus::OK: return OLD_STATUS_OK; case NewStatus::Error: return OLD_STATUS_ERROR; } throw std::invalid_argument("Invalid status"); } static NewStatus toNewStatus(OldStatus status) { switch (status) { case OLD_STATUS_OK: return NewStatus::OK; case OLD_STATUS_ERROR: return NewStatus::Error; } throw std::invalid_argument("Invalid old status"); } };
- バージョン管理と移行戦略
namespace v1 { enum class ApiStatus { OK, Error }; } namespace v2 { enum class ApiStatus { OK, Error, Pending // 新しい値の追加 }; // 変換関数の提供 static ApiStatus fromV1(v1::ApiStatus oldStatus) { switch (oldStatus) { case v1::ApiStatus::OK: return ApiStatus::OK; case v1::ApiStatus::Error: return ApiStatus::Error; } throw std::invalid_argument("Invalid v1 status"); } }
- 安全な拡張方法
// 将来の拡張を考慮した設計 enum class ExtensibleStatus : uint32_t { // 十分な余地を持つ型を使用 OK = 0, Error = 1, // 予約済み範囲 Reserved_Start = 1000, Reserved_End = 2000, // カスタム拡張用の範囲 Custom_Start = 10000, Custom_End = 20000 }; class StatusExtensionManager { public: static bool isReserved(ExtensibleStatus status) { auto value = static_cast<uint32_t>(status); return value >= static_cast<uint32_t>(ExtensibleStatus::Reserved_Start) && value <= static_cast<uint32_t>(ExtensibleStatus::Reserved_End); } static bool isCustom(ExtensibleStatus status) { auto value = static_cast<uint32_t>(status); return value >= static_cast<uint32_t>(ExtensibleStatus::Custom_Start) && value <= static_cast<uint32_t>(ExtensibleStatus::Custom_End); } };
これらの注意点と対処法を理解し、適切に実装することで、enum classを安全かつ効果的に活用できます。特にレガシーシステムとの統合や長期的なメンテナンスが必要なプロジェクトでは、これらの考慮点が重要になります。
enum クラスの実務での応用例
大規模プロジェクトでの活用例
実際の開発現場でのenum classの効果的な活用例を紹介します。
- マイクロサービスでのステータス管理
namespace ServiceStatus { enum class Health { Healthy, Degraded, Unhealthy, Unknown }; enum class Availability { Active, MaintenanceMode, Suspended, Offline }; class ServiceStateManager { private: Health health{Health::Unknown}; Availability availability{Availability::Offline}; std::mutex stateMutex; public: void updateHealth(Health newHealth) { std::lock_guard<std::mutex> lock(stateMutex); health = newHealth; notifyStateChange(); } void setAvailability(Availability newAvailability) { std::lock_guard<std::mutex> lock(stateMutex); availability = newAvailability; notifyStateChange(); } // ヘルスチェックエンドポイント用のJSONレスポンス生成 std::string getHealthCheckResponse() const { std::lock_guard<std::mutex> lock(stateMutex); return formatHealthResponse(health, availability); } }; }
- 大規模ゲームエンジンでのステート管理
namespace GameEngine { enum class EntityState { Spawning, Active, Damaged, Invulnerable, Dying, Dead }; enum class AnimationPhase { Idle, Walking, Running, Jumping, Falling, Landing }; class EntityManager { private: struct Entity { EntityState state; AnimationPhase animPhase; float health; // その他のプロパティ }; std::unordered_map<uint64_t, Entity> entities; public: void updateEntityState(uint64_t entityId, EntityState newState) { if (auto it = entities.find(entityId); it != entities.end()) { auto& entity = it->second; // 状態遷移の妥当性チェック if (isValidStateTransition(entity.state, newState)) { entity.state = newState; onEntityStateChanged(entityId, newState); } } } private: bool isValidStateTransition(EntityState current, EntityState next) { // 状態遷移のバリデーション static const std::unordered_map<EntityState, std::set<EntityState>> validTransitions = { {EntityState::Spawning, {EntityState::Active}}, {EntityState::Active, {EntityState::Damaged, EntityState::Invulnerable, EntityState::Dying}}, {EntityState::Damaged, {EntityState::Active, EntityState::Dying}}, {EntityState::Invulnerable, {EntityState::Active}}, {EntityState::Dying, {EntityState::Dead}}, {EntityState::Dead, {}} }; auto it = validTransitions.find(current); return it != validTransitions.end() && it->second.contains(next); } }; }
保守性向上のための実装パターン
- 設定管理システムでの活用
class ConfigurationManager { public: enum class ConfigSource { Default, Environment, File, CommandLine, Remote }; enum class ValidationLevel { None, Basic, Strict }; private: struct ConfigEntry { std::string value; ConfigSource source; std::chrono::system_clock::time_point lastUpdate; }; std::unordered_map<std::string, ConfigEntry> configurations; ValidationLevel validationLevel{ValidationLevel::Basic}; public: template<typename T> std::optional<T> getValue(const std::string& key) const { if (auto it = configurations.find(key); it != configurations.end()) { return parseValue<T>(it->second.value); } return std::nullopt; } bool setValue(const std::string& key, const std::string& value, ConfigSource source) { if (!validateValue(key, value)) { return false; } configurations[key] = ConfigEntry{ value, source, std::chrono::system_clock::now() }; notifyConfigurationChanged(key); return true; } };
- エラーハンドリングシステム
namespace ErrorHandling { enum class Severity { Info, Warning, Error, Critical, Fatal }; enum class ErrorCategory { Network, Database, Security, BusinessLogic, System }; class ErrorLogger { private: struct ErrorContext { Severity severity; ErrorCategory category; std::string message; std::source_location location; std::chrono::system_clock::time_point timestamp; }; std::deque<ErrorContext> errorLog; std::mutex logMutex; public: void logError(Severity severity, ErrorCategory category, const std::string& message, const std::source_location& location = std::source_location::current()) { std::lock_guard<std::mutex> lock(logMutex); errorLog.push_back({ severity, category, message, location, std::chrono::system_clock::now() }); if (severity >= Severity::Critical) { notifyAdministrators(); } // ログローテーション while (errorLog.size() > maxLogSize) { errorLog.pop_front(); } } }; }
これらの実装パターンは、実際のプロジェクトで検証された効果的なアプローチです。特に以下の点で優れています:
- 型安全性の確保
- コードの可読性向上
- 保守性の向上
- デバッグのしやすさ
- チーム開発での整合性維持
実務では、これらのパターンを基礎として、プロジェクトの要件に応じてカスタマイズしていくことが推奨されます。