Support C++ 11 enum class generation via pure_enums=enum_class

Adds C++ 11 `enum class` generation to the C++ code generator. When `pure_enums=enum_class` is specified, generates strongly-typed enums with proper scoping and type safety.

## Code Generator Changes

**Modified `t_cpp_generator.cc`:**
- Added `gen_enum_class_` flag parsed from `pure_enums=enum_class` option
- Modified `generate_enum()` to emit `enum class` when both `gen_pure_enums_` and `gen_enum_class_` are set
- Fixed enum value scoping: use `static_cast<int>(Color::RED)` for array initialization
- Fixed map operations: cast enum class to int in `find()` calls (no implicit conversion)

**Test infrastructure:**
- Created `t_cpp_generator_enum_class_tests.cc` with three test cases validating default, `pure_enums`, and `pure_enums=enum_class` behavior
- Extracted common utilities to `t_cpp_generator_test_utils.h` (shared with `private_optional` tests)
- Added expected output files: `expected_Color_{default,pure_enums,enum_class}.txt`

**Build system:**
- Added CMake compile-checks for generated code (both `private_optional` and `enum_class`)
- Unified compile-check blocks with shared Boost detection
- Ensures generated code compiles as part of standard test build

## Usage

```thrift
enum Color {
  RED = 1,
  GREEN = 2,
  BLUE = 3
}
```

```bash
thrift --gen cpp:pure_enums=enum_class example.thrift
```

Generates:
```cpp
enum class Color {
  RED = 1,
  GREEN = 2,
  BLUE = 3
};
```

Backward compatible - existing behavior unchanged when option absent.

---

- [ ] Did you create an [Apache Jira](https://issues.apache.org/jira/projects/THRIFT/issues/) ticket?  ([Request account here](https://selfserve.apache.org/jira-account.html), not required for trivial changes)
- [ ] If a ticket exists: Does your pull request title follow the pattern "THRIFT-NNNN: describe my issue"?
- [x] Did you squash your changes to a single commit?  (not required, but preferred)
- [x] Did you do your best to avoid breaking changes?  If one was needed, did you label the Jira ticket with "Breaking-Change"?
- [ ] If your change does not involve any code, include `[skip ci]` anywhere in the commit message to free up build resources.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: zsy056 <1074382+zsy056@users.noreply.github.com>
diff --git a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
index f6a15b3..4c427b1 100644
--- a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
@@ -57,6 +57,7 @@
 
 
     gen_pure_enums_ = false;
+    gen_enum_class_ = false;
     use_include_prefix_ = false;
     gen_cob_style_ = false;
     gen_no_client_completion_ = false;
@@ -73,6 +74,9 @@
     for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
       if( iter->first.compare("pure_enums") == 0) {
         gen_pure_enums_ = true;
+        if (iter->second.compare("enum_class") == 0) {
+          gen_enum_class_ = true;
+        }
       } else if( iter->first.compare("include_prefix") == 0) {
         use_include_prefix_ = true;
       } else if( iter->first.compare("cob_style") == 0) {
@@ -337,6 +341,11 @@
   bool gen_pure_enums_;
 
   /**
+   * True if we should generate C++ 11 enum class for Thrift enums.
+   */
+  bool gen_enum_class_;
+
+  /**
    * True if we should generate templatized reader/writer methods.
    */
   bool gen_templates_;
@@ -609,7 +618,11 @@
     f_types_ << indent() << "struct " << tenum->get_name() << " {" << '\n';
     indent_up();
   }
-  f_types_ << indent() << "enum " << enum_name;
+  if (gen_pure_enums_ && gen_enum_class_) {
+    f_types_ << indent() << "enum class " << enum_name;
+  } else {
+    f_types_ << indent() << "enum " << enum_name;
+  }
 
   generate_enum_constant_list(f_types_, constants, "", "", true);
 
@@ -624,12 +637,20 @@
      Generate a character array of enum names for debugging purposes.
   */
   std::string prefix = "";
-  if (!gen_pure_enums_) {
+  std::string int_value_prefix = "";
+  std::string int_value_suffix = "";
+  if (!gen_pure_enums_ || gen_enum_class_) {
     prefix = tenum->get_name() + "::";
   }
+  if (gen_enum_class_) {
+    int_value_prefix = "static_cast<int>(" + tenum->get_name() + "::";
+    int_value_suffix = ")";
+  } else if (!gen_pure_enums_) {
+    int_value_prefix = tenum->get_name() + "::";
+  }
 
   f_types_impl_ << indent() << "int _k" << tenum->get_name() << "Values[] =";
-  generate_enum_constant_list(f_types_impl_, constants, prefix.c_str(), "", false);
+  generate_enum_constant_list(f_types_impl_, constants, int_value_prefix.c_str(), int_value_suffix.c_str(), false);
 
   f_types_impl_ << indent() << "const char* _k" << tenum->get_name() << "Names[] =";
   generate_enum_constant_list(f_types_impl_, constants, "\"", "\"", false);
@@ -680,7 +701,12 @@
     scope_up(out);
 
     out << indent() << "std::map<int, const char*>::const_iterator it = _"
-             << tenum->get_name() << "_VALUES_TO_NAMES.find(val);" << '\n';
+             << tenum->get_name() << "_VALUES_TO_NAMES.find(";
+    if (gen_enum_class_) {
+      out << "static_cast<int>(val));" << '\n';
+    } else {
+      out << "val);" << '\n';
+    }
     out << indent() << "if (it != _" << tenum->get_name() << "_VALUES_TO_NAMES.end()) {" << '\n';
     indent_up();
     out << indent() << "out << it->second;" << '\n';
@@ -720,7 +746,12 @@
     scope_up(out);
 
     out << indent() << "std::map<int, const char*>::const_iterator it = _"
-             << tenum->get_name() << "_VALUES_TO_NAMES.find(val);" << '\n';
+             << tenum->get_name() << "_VALUES_TO_NAMES.find(";
+    if (gen_enum_class_) {
+      out << "static_cast<int>(val));" << '\n';
+    } else {
+      out << "val);" << '\n';
+    }
     out << indent() << "if (it != _" << tenum->get_name() << "_VALUES_TO_NAMES.end()) {" << '\n';
     indent_up();
     out << indent() << "return std::string(it->second);" << '\n';
@@ -4954,6 +4985,7 @@
     "                     Omits generation of default operators ==, != and <\n"
     "    templates:       Generate templatized reader/writer methods.\n"
     "    pure_enums:      Generate pure enums instead of wrapper classes.\n"
+    "                     When 'pure_enums=enum_class', generate C++ 11 enum class.\n"
     "    include_prefix:  Use full include paths in generated files.\n"
     "    moveable_types:  Generate move constructors and assignment operators.\n"
     "    no_ostream_operators:\n"
diff --git a/compiler/cpp/tests/CMakeLists.txt b/compiler/cpp/tests/CMakeLists.txt
index 77c1524..468de6e 100644
--- a/compiler/cpp/tests/CMakeLists.txt
+++ b/compiler/cpp/tests/CMakeLists.txt
@@ -194,73 +194,117 @@
     )
 endif()
 
-# Compile-check generated C++ output for the fixture thrift file with private_optional enabled.
+# Compile-check generated C++ output for fixture thrift files.
 # This ensures the generator output is compileable (no link step required).
+# Note: These checks require Boost headers and are optional.
 if(TARGET thrift-compiler)
-    # Generated C++ includes Thrift runtime headers which may require Boost.
-    # Only enable the compile-check when Boost headers are available.
-    set(_private_optional_boost_include_dirs "")
+    # Try to find Boost for the compile checks
     find_package(Boost QUIET)
+    set(_compile_check_boost_include_dirs "")
     if(Boost_FOUND)
-        set(_private_optional_boost_include_dirs ${Boost_INCLUDE_DIRS})
+        set(_compile_check_boost_include_dirs ${Boost_INCLUDE_DIRS})
     elseif(DEFINED BOOST_ROOT)
         if(EXISTS "${BOOST_ROOT}/include/boost")
-            set(_private_optional_boost_include_dirs "${BOOST_ROOT}/include")
+            set(_compile_check_boost_include_dirs "${BOOST_ROOT}/include")
         elseif(EXISTS "${BOOST_ROOT}/boost")
-            set(_private_optional_boost_include_dirs "${BOOST_ROOT}")
+            set(_compile_check_boost_include_dirs "${BOOST_ROOT}")
         endif()
     endif()
 
-    if(_private_optional_boost_include_dirs STREQUAL "")
-        message(STATUS "Skipping generated private_optional compile-check (Boost headers not found)")
+    # Only create compile-check targets if Boost is available
+    if(NOT _compile_check_boost_include_dirs STREQUAL "")
+        set(_private_optional_thrift
+            "${CMAKE_CURRENT_SOURCE_DIR}/cpp/test_private_optional.thrift"
+        )
+        set(_private_optional_gen_out_dir
+            "${CMAKE_CURRENT_BINARY_DIR}/generated-private-optional"
+        )
+        set(_private_optional_gen_cpp_dir
+            "${_private_optional_gen_out_dir}/gen-cpp"
+        )
+        set(_private_optional_types_cpp
+            "${_private_optional_gen_cpp_dir}/test_private_optional_types.cpp"
+        )
+
+        add_custom_command(
+            OUTPUT "${_private_optional_types_cpp}"
+            COMMAND ${CMAKE_COMMAND} -E make_directory "${_private_optional_gen_out_dir}"
+            COMMAND ${CMAKE_COMMAND} -E chdir "${_private_optional_gen_out_dir}"
+                $<TARGET_FILE:thrift-compiler>
+                --gen cpp:private_optional
+                -o "${_private_optional_gen_out_dir}"
+                "${_private_optional_thrift}"
+            DEPENDS thrift-compiler "${_private_optional_thrift}"
+            VERBATIM
+        )
+
+        set_source_files_properties(
+            "${_private_optional_types_cpp}"
+            PROPERTIES GENERATED TRUE
+        )
+
+        add_library(thrift_compiler_generated_private_optional STATIC
+            "${_private_optional_types_cpp}"
+        )
+
+        target_include_directories(thrift_compiler_generated_private_optional PRIVATE
+            "${_private_optional_gen_cpp_dir}"
+            "${THRIFT_COMPILER_SOURCE_DIR}/../../lib/cpp/src"
+            "${CMAKE_CURRENT_BINARY_DIR}"
+            "${CMAKE_BINARY_DIR}"
+            ${_compile_check_boost_include_dirs}
+        )
+
+        set(_enum_class_thrift
+            "${CMAKE_CURRENT_SOURCE_DIR}/cpp/test_enum_class.thrift"
+        )
+        set(_enum_class_gen_out_dir
+            "${CMAKE_CURRENT_BINARY_DIR}/generated-enum-class"
+        )
+        set(_enum_class_gen_cpp_dir
+            "${_enum_class_gen_out_dir}/gen-cpp"
+        )
+        set(_enum_class_types_cpp
+            "${_enum_class_gen_cpp_dir}/test_enum_class_types.cpp"
+        )
+
+        add_custom_command(
+            OUTPUT "${_enum_class_types_cpp}"
+            COMMAND ${CMAKE_COMMAND} -E make_directory "${_enum_class_gen_out_dir}"
+            COMMAND ${CMAKE_COMMAND} -E chdir "${_enum_class_gen_out_dir}"
+                $<TARGET_FILE:thrift-compiler>
+                --gen cpp:pure_enums=enum_class
+                -o "${_enum_class_gen_out_dir}"
+                "${_enum_class_thrift}"
+            DEPENDS thrift-compiler "${_enum_class_thrift}"
+            VERBATIM
+        )
+
+        set_source_files_properties(
+            "${_enum_class_types_cpp}"
+            PROPERTIES GENERATED TRUE
+        )
+
+        add_library(thrift_compiler_generated_enum_class STATIC
+            "${_enum_class_types_cpp}"
+        )
+
+        target_include_directories(thrift_compiler_generated_enum_class PRIVATE
+            "${_enum_class_gen_cpp_dir}"
+            "${THRIFT_COMPILER_SOURCE_DIR}/../../lib/cpp/src"
+            "${CMAKE_CURRENT_BINARY_DIR}"
+            "${CMAKE_BINARY_DIR}"
+            ${_compile_check_boost_include_dirs}
+        )
+
+        # Build the compile-check as part of the standard test build.
+        add_dependencies(thrift_compiler_tests thrift_compiler_generated_private_optional)
+        add_dependencies(thrift_compiler_tests thrift_compiler_generated_enum_class)
     else()
-    set(_private_optional_thrift
-        "${CMAKE_CURRENT_SOURCE_DIR}/cpp/test_private_optional.thrift"
-    )
-    set(_private_optional_gen_out_dir
-        "${CMAKE_CURRENT_BINARY_DIR}/generated-private-optional"
-    )
-    set(_private_optional_gen_cpp_dir
-        "${_private_optional_gen_out_dir}/gen-cpp"
-    )
-    set(_private_optional_types_cpp
-        "${_private_optional_gen_cpp_dir}/test_private_optional_types.cpp"
-    )
-
-    add_custom_command(
-        OUTPUT "${_private_optional_types_cpp}"
-        COMMAND ${CMAKE_COMMAND} -E make_directory "${_private_optional_gen_out_dir}"
-        COMMAND ${CMAKE_COMMAND} -E chdir "${_private_optional_gen_out_dir}"
-            $<TARGET_FILE:thrift-compiler>
-            --gen cpp:private_optional
-            -o "${_private_optional_gen_out_dir}"
-            "${_private_optional_thrift}"
-        DEPENDS thrift-compiler "${_private_optional_thrift}"
-        VERBATIM
-    )
-
-    set_source_files_properties(
-        "${_private_optional_types_cpp}"
-        PROPERTIES GENERATED TRUE
-    )
-
-    add_library(thrift_compiler_generated_private_optional STATIC
-        "${_private_optional_types_cpp}"
-    )
-
-    target_include_directories(thrift_compiler_generated_private_optional PRIVATE
-        "${_private_optional_gen_cpp_dir}"
-        "${THRIFT_COMPILER_SOURCE_DIR}/../../lib/cpp/src"
-        "${CMAKE_CURRENT_BINARY_DIR}"
-        "${CMAKE_BINARY_DIR}"
-        ${_private_optional_boost_include_dirs}
-    )
-
-    # Build the compile-check as part of the standard test build.
-    add_dependencies(thrift_compiler_tests thrift_compiler_generated_private_optional)
+        message(STATUS "Skipping generated code compile-checks (Boost headers not found)")
     endif()
 else()
-    message(STATUS "Skipping generated private_optional compile-check (no thrift-compiler target)")
+    message(STATUS "Skipping generated code compile-checks (no thrift-compiler target)")
 endif()
 
 enable_testing()
diff --git a/compiler/cpp/tests/cpp/expected_Color_default.txt b/compiler/cpp/tests/cpp/expected_Color_default.txt
new file mode 100644
index 0000000..94f3226
--- /dev/null
+++ b/compiler/cpp/tests/cpp/expected_Color_default.txt
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+struct Color {
+  enum type {
+    RED = 1,
+    GREEN = 2,
+    BLUE = 3
+  };
+};
diff --git a/compiler/cpp/tests/cpp/expected_Color_enum_class.txt b/compiler/cpp/tests/cpp/expected_Color_enum_class.txt
new file mode 100644
index 0000000..f21bc8e
--- /dev/null
+++ b/compiler/cpp/tests/cpp/expected_Color_enum_class.txt
@@ -0,0 +1,22 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+enum class Color {
+  RED = 1,
+  GREEN = 2,
+  BLUE = 3
+};
diff --git a/compiler/cpp/tests/cpp/expected_Color_pure_enums.txt b/compiler/cpp/tests/cpp/expected_Color_pure_enums.txt
new file mode 100644
index 0000000..2294c62
--- /dev/null
+++ b/compiler/cpp/tests/cpp/expected_Color_pure_enums.txt
@@ -0,0 +1,22 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+enum Color {
+  RED = 1,
+  GREEN = 2,
+  BLUE = 3
+};
diff --git a/compiler/cpp/tests/cpp/t_cpp_generator_enum_class_tests.cc b/compiler/cpp/tests/cpp/t_cpp_generator_enum_class_tests.cc
new file mode 100644
index 0000000..56404ad
--- /dev/null
+++ b/compiler/cpp/tests/cpp/t_cpp_generator_enum_class_tests.cc
@@ -0,0 +1,176 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+// 
+//     http://www.apache.org/licenses/LICENSE-2.0
+// 
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "t_cpp_generator_test_utils.h"
+
+using std::string;
+using std::map;
+using cpp_generator_test_utils::read_file;
+using cpp_generator_test_utils::source_dir;
+using cpp_generator_test_utils::join_path;
+using cpp_generator_test_utils::normalize_for_compare;
+using cpp_generator_test_utils::parse_thrift_for_test;
+
+// Helper function to extract enum definition from generated header
+// Handles struct Color { enum type { ... }; }; or enum Color { ... }; or enum class Color { ... };
+string extract_enum_definition(const string& content, const string& enum_name) {
+    // First try to find struct wrapper (default behavior)
+    size_t struct_start = content.find("struct " + enum_name + " {");
+    if (struct_start != string::npos) {
+        // Find the closing brace and semicolon for the struct
+        size_t brace_count = 0;
+        size_t pos = content.find("{", struct_start);
+        if (pos == string::npos) return "";
+        
+        brace_count = 1;
+        pos++;
+        
+        while (pos < content.length() && brace_count > 0) {
+            if (content[pos] == '{') brace_count++;
+            else if (content[pos] == '}') brace_count--;
+            pos++;
+        }
+        
+        if (brace_count == 0) {
+            // Skip whitespace and semicolon
+            while (pos < content.length() && (content[pos] == ' ' || content[pos] == '\n' || content[pos] == '\r')) pos++;
+            if (pos < content.length() && content[pos] == ';') pos++;
+            return content.substr(struct_start, pos - struct_start);
+        }
+        return "";
+    }
+    
+    // Try to find enum class
+    size_t enum_start = content.find("enum class " + enum_name + " {");
+    if (enum_start == string::npos) {
+        // Try to find plain enum
+        enum_start = content.find("enum " + enum_name + " {");
+        if (enum_start == string::npos) {
+            return "";
+        }
+    }
+    
+    // Find the closing brace and semicolon
+    size_t brace_end = content.find("};", enum_start);
+    if (brace_end == string::npos) {
+        return "";
+    }
+    
+    return content.substr(enum_start, brace_end - enum_start + 2);
+}
+
+TEST_CASE("t_cpp_generator default behavior generates wrapper struct for enums", "[functional]")
+{
+    string path = join_path(source_dir(), "test_enum_class.thrift");
+    string name = "test_enum_class";
+    map<string, string> parsed_options = {}; // No options
+    string option_string = "";
+
+    std::unique_ptr<t_program> program(new t_program(path, name));
+    parse_thrift_for_test(program.get());
+    
+    std::unique_ptr<t_generator> gen(
+        t_generator_registry::get_generator(program.get(), "cpp", parsed_options, option_string));
+    REQUIRE(gen != nullptr);
+    
+    // Generate code
+    REQUIRE_NOTHROW(gen->generate_program());
+
+    // Read generated output
+    string generated_file = "gen-cpp/test_enum_class_types.h";
+    string generated_content = read_file(generated_file);
+    REQUIRE(!generated_content.empty());
+
+    // Extract enum definition
+    string enum_def = extract_enum_definition(generated_content, "Color");
+    REQUIRE(!enum_def.empty());
+
+    // Compare generated enum definition against the expected fixture.
+    string expected_path = join_path(source_dir(), "expected_Color_default.txt");
+    string expected_content = read_file(expected_path);
+    REQUIRE(!expected_content.empty());
+
+    REQUIRE(normalize_for_compare(enum_def) == normalize_for_compare(expected_content));
+}
+
+TEST_CASE("t_cpp_generator with pure_enums generates plain enum", "[functional]")
+{
+    string path = join_path(source_dir(), "test_enum_class.thrift");
+    string name = "test_enum_class";
+    map<string, string> parsed_options = {{"pure_enums", ""}};
+    string option_string = "";
+
+    std::unique_ptr<t_program> program(new t_program(path, name));
+    parse_thrift_for_test(program.get());
+    
+    std::unique_ptr<t_generator> gen(
+        t_generator_registry::get_generator(program.get(), "cpp", parsed_options, option_string));
+    REQUIRE(gen != nullptr);
+    
+    // Generate code
+    REQUIRE_NOTHROW(gen->generate_program());
+
+    // Read generated output
+    string generated_file = "gen-cpp/test_enum_class_types.h";
+    string generated_content = read_file(generated_file);
+    REQUIRE(!generated_content.empty());
+
+    // Extract enum definition
+    string enum_def = extract_enum_definition(generated_content, "Color");
+    REQUIRE(!enum_def.empty());
+
+    // Compare generated enum definition against the expected fixture.
+    string expected_path = join_path(source_dir(), "expected_Color_pure_enums.txt");
+    string expected_content = read_file(expected_path);
+    REQUIRE(!expected_content.empty());
+
+    REQUIRE(normalize_for_compare(enum_def) == normalize_for_compare(expected_content));
+}
+
+TEST_CASE("t_cpp_generator with pure_enums=enum_class generates C++ 11 enum class", "[functional]")
+{
+    string path = join_path(source_dir(), "test_enum_class.thrift");
+    string name = "test_enum_class";
+    map<string, string> parsed_options = {{"pure_enums", "enum_class"}};
+    string option_string = "";
+
+    std::unique_ptr<t_program> program(new t_program(path, name));
+    parse_thrift_for_test(program.get());
+    
+    std::unique_ptr<t_generator> gen(
+        t_generator_registry::get_generator(program.get(), "cpp", parsed_options, option_string));
+    REQUIRE(gen != nullptr);
+    
+    // Generate code
+    REQUIRE_NOTHROW(gen->generate_program());
+
+    // Read generated output
+    string generated_file = "gen-cpp/test_enum_class_types.h";
+    string generated_content = read_file(generated_file);
+    REQUIRE(!generated_content.empty());
+
+    // Extract enum definition
+    string enum_def = extract_enum_definition(generated_content, "Color");
+    REQUIRE(!enum_def.empty());
+
+    // Compare generated enum definition against the expected fixture.
+    string expected_path = join_path(source_dir(), "expected_Color_enum_class.txt");
+    string expected_content = read_file(expected_path);
+    REQUIRE(!expected_content.empty());
+
+    REQUIRE(normalize_for_compare(enum_def) == normalize_for_compare(expected_content));
+}
diff --git a/compiler/cpp/tests/cpp/t_cpp_generator_private_optional_tests.cc b/compiler/cpp/tests/cpp/t_cpp_generator_private_optional_tests.cc
index 9f48229..2d63cb4 100644
--- a/compiler/cpp/tests/cpp/t_cpp_generator_private_optional_tests.cc
+++ b/compiler/cpp/tests/cpp/t_cpp_generator_private_optional_tests.cc
@@ -15,104 +15,15 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#include "../catch/catch.hpp"
-#include <thrift/parse/t_program.h>
-#include <thrift/generate/t_generator.h>
-#include <thrift/generate/t_generator_registry.h>
-#include <thrift/globals.h>
-#include <thrift/main.h>
-#include <fstream>
-#include <memory>
-#include <string>
-#include <sstream>
-
-#include <algorithm>
+#include "t_cpp_generator_test_utils.h"
 
 using std::string;
 using std::map;
-using std::ifstream;
-using std::ofstream;
-using std::ostringstream;
-
-// Provided by compiler/cpp/tests/thrift_test_parser_support.cc
-extern std::string g_curdir;
-extern std::string g_curpath;
-
-// Helper function to read file content
-string read_file(const string& filename) {
-    ifstream file(filename);
-    if (!file.is_open()) {
-        return "";
-    }
-    ostringstream ss;
-    ss << file.rdbuf();
-    return ss.str();
-}
-
-static string source_dir() {
-    string file = __FILE__;
-    std::replace(file.begin(), file.end(), '\\', '/');
-    size_t slash = file.rfind('/');
-    return (slash == string::npos) ? string(".") : file.substr(0, slash);
-}
-
-static string join_path(const string& a, const string& b) {
-    if (a.empty()) {
-        return b;
-    }
-    if (a.back() == '/' || a.back() == '\\') {
-        return a + b;
-    }
-    return a + "/" + b;
-}
-
-static string normalize_for_compare(string s) {
-    s.erase(std::remove(s.begin(), s.end(), '\r'), s.end());
-
-    std::istringstream in(s);
-    std::ostringstream out;
-    string line;
-    bool in_block_comment = false;
-    bool first = true;
-    while (std::getline(in, line)) {
-        while (!line.empty() && (line.back() == ' ' || line.back() == '\t')) {
-            line.pop_back();
-        }
-
-        const auto first_non_ws = line.find_first_not_of(" \t");
-        if (first_non_ws == string::npos) {
-            continue;
-        }
-
-        const string trimmed = line.substr(first_non_ws);
-
-        if (in_block_comment) {
-            if (trimmed.find("*/") != string::npos) {
-                in_block_comment = false;
-            }
-            continue;
-        }
-
-        if (trimmed.size() >= 2 && trimmed.compare(0, 2, "//") == 0) {
-            continue;
-        }
-
-        if (trimmed.size() >= 2 && trimmed.compare(0, 2, "/*") == 0) {
-            if (trimmed.find("*/") == string::npos) {
-                in_block_comment = true;
-            }
-            continue;
-        }
-
-        if (!first) {
-            out << '\n';
-        }
-        first = false;
-        out << line;
-    }
-
-    return out.str();
-}
+using cpp_generator_test_utils::read_file;
+using cpp_generator_test_utils::source_dir;
+using cpp_generator_test_utils::join_path;
+using cpp_generator_test_utils::normalize_for_compare;
+using cpp_generator_test_utils::parse_thrift_for_test;
 
 // Helper function to extract class definition from generated header
 string extract_class_definition(const string& content, const string& class_name) {
@@ -129,37 +40,6 @@
     return content.substr(class_start, class_end - class_start + 2);
 }
 
-static void parse_thrift_for_test(t_program* program) {
-    REQUIRE(program != nullptr);
-
-    // These globals are used by the parser; see thrift/globals.h.
-    g_program = program;
-    g_scope = program->scope();
-    g_parent_scope = nullptr;
-    g_parent_prefix = program->get_name() + ".";
-
-    g_curpath = program->get_path();
-    g_curdir = directory_name(g_curpath);
-
-    // Pass 1: scan includes (even if none) to match the compiler behavior.
-    g_parse_mode = INCLUDES;
-    yylineno = 1;
-    yyin = std::fopen(g_curpath.c_str(), "r");
-    REQUIRE(yyin != nullptr);
-    REQUIRE(yyparse() == 0);
-    std::fclose(yyin);
-    yyin = nullptr;
-
-    // Pass 2: parse program.
-    g_parse_mode = PROGRAM;
-    yylineno = 1;
-    yyin = std::fopen(g_curpath.c_str(), "r");
-    REQUIRE(yyin != nullptr);
-    REQUIRE(yyparse() == 0);
-    std::fclose(yyin);
-    yyin = nullptr;
-}
-
 TEST_CASE("t_cpp_generator default behavior generates all public fields", "[functional]")
 {
     string path = join_path(source_dir(), "test_private_optional.thrift");
diff --git a/compiler/cpp/tests/cpp/t_cpp_generator_test_utils.h b/compiler/cpp/tests/cpp/t_cpp_generator_test_utils.h
new file mode 100644
index 0000000..5cea69a
--- /dev/null
+++ b/compiler/cpp/tests/cpp/t_cpp_generator_test_utils.h
@@ -0,0 +1,152 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+// 
+//     http://www.apache.org/licenses/LICENSE-2.0
+// 
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#ifndef T_CPP_GENERATOR_TEST_UTILS_H
+#define T_CPP_GENERATOR_TEST_UTILS_H
+
+#include "../catch/catch.hpp"
+#include <thrift/parse/t_program.h>
+#include <thrift/generate/t_generator.h>
+#include <thrift/generate/t_generator_registry.h>
+#include <thrift/globals.h>
+#include <thrift/main.h>
+#include <fstream>
+#include <memory>
+#include <string>
+#include <sstream>
+#include <algorithm>
+
+// Provided by compiler/cpp/tests/thrift_test_parser_support.cc
+extern std::string g_curdir;
+extern std::string g_curpath;
+
+namespace cpp_generator_test_utils {
+
+// Helper function to read file content
+inline std::string read_file(const std::string& filename) {
+    std::ifstream file(filename);
+    if (!file.is_open()) {
+        return "";
+    }
+    std::ostringstream ss;
+    ss << file.rdbuf();
+    return ss.str();
+}
+
+// Get the source directory of the current test file
+inline std::string source_dir() {
+    std::string file = __FILE__;
+    std::replace(file.begin(), file.end(), '\\', '/');
+    size_t slash = file.rfind('/');
+    return (slash == std::string::npos) ? std::string(".") : file.substr(0, slash);
+}
+
+// Join two path components
+inline std::string join_path(const std::string& a, const std::string& b) {
+    if (a.empty()) {
+        return b;
+    }
+    if (a.back() == '/' || a.back() == '\\') {
+        return a + b;
+    }
+    return a + "/" + b;
+}
+
+// Normalize generated code for comparison by removing comments and extra whitespace
+inline std::string normalize_for_compare(std::string s) {
+    s.erase(std::remove(s.begin(), s.end(), '\r'), s.end());
+
+    std::istringstream in(s);
+    std::ostringstream out;
+    std::string line;
+    bool in_block_comment = false;
+    bool first = true;
+    while (std::getline(in, line)) {
+        while (!line.empty() && (line.back() == ' ' || line.back() == '\t')) {
+            line.pop_back();
+        }
+
+        const auto first_non_ws = line.find_first_not_of(" \t");
+        if (first_non_ws == std::string::npos) {
+            continue;
+        }
+
+        const std::string trimmed = line.substr(first_non_ws);
+
+        if (in_block_comment) {
+            if (trimmed.find("*/") != std::string::npos) {
+                in_block_comment = false;
+            }
+            continue;
+        }
+
+        if (trimmed.size() >= 2 && trimmed.compare(0, 2, "//") == 0) {
+            continue;
+        }
+
+        if (trimmed.size() >= 2 && trimmed.compare(0, 2, "/*") == 0) {
+            if (trimmed.find("*/") == std::string::npos) {
+                in_block_comment = true;
+            }
+            continue;
+        }
+
+        if (!first) {
+            out << '\n';
+        }
+        first = false;
+        out << line;
+    }
+
+    return out.str();
+}
+
+// Parse a thrift program for testing
+inline void parse_thrift_for_test(t_program* program) {
+    REQUIRE(program != nullptr);
+
+    // These globals are used by the parser; see thrift/globals.h.
+    g_program = program;
+    g_scope = program->scope();
+    g_parent_scope = nullptr;
+    g_parent_prefix = program->get_name() + ".";
+
+    g_curpath = program->get_path();
+    g_curdir = directory_name(g_curpath);
+
+    // Pass 1: scan includes (even if none) to match the compiler behavior.
+    g_parse_mode = INCLUDES;
+    yylineno = 1;
+    yyin = std::fopen(g_curpath.c_str(), "r");
+    REQUIRE(yyin != nullptr);
+    REQUIRE(yyparse() == 0);
+    std::fclose(yyin);
+    yyin = nullptr;
+
+    // Pass 2: parse program.
+    g_parse_mode = PROGRAM;
+    yylineno = 1;
+    yyin = std::fopen(g_curpath.c_str(), "r");
+    REQUIRE(yyin != nullptr);
+    REQUIRE(yyparse() == 0);
+    std::fclose(yyin);
+    yyin = nullptr;
+}
+
+} // namespace cpp_generator_test_utils
+
+#endif // T_CPP_GENERATOR_TEST_UTILS_H
diff --git a/compiler/cpp/tests/cpp/test_enum_class.thrift b/compiler/cpp/tests/cpp/test_enum_class.thrift
new file mode 100644
index 0000000..bf85e32
--- /dev/null
+++ b/compiler/cpp/tests/cpp/test_enum_class.thrift
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace cpp test.enum_class
+
+enum Color {
+  RED = 1,
+  GREEN = 2,
+  BLUE = 3
+}
+
+enum Status {
+  ACTIVE = 0,
+  INACTIVE = 1,
+  PENDING = 2
+}