From 0832d857448f9f6cadb64472a9c56554aaef2415 Mon Sep 17 00:00:00 2001 From: caleb2h Date: Sat, 2 Aug 2025 10:31:14 -0700 Subject: [PATCH 1/3] Apply Google C++ code style to all source files - Add .clang-format configuration with Google style settings - Format all header and source files according to Google C++ style guide - 2-space indentation, 80-column limit, sorted includes - No functional changes, only formatting --- .clang-format | 51 ++ include/mcp/memory_utils.h | 23 +- include/mcp/variant.h | 570 ++++++++++---------- tests/test_variant.cpp | 875 +++++++++++++++--------------- tests/test_variant_advanced.cpp | 635 +++++++++++----------- tests/test_variant_extensive.cpp | 896 ++++++++++++++++--------------- 6 files changed, 1584 insertions(+), 1466 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..711e1e1e --- /dev/null +++ b/.clang-format @@ -0,0 +1,51 @@ +--- +# Google C++ Style Guide +# https://google.github.io/styleguide/cppguide.html +BasedOnStyle: Google +IndentWidth: 2 +ColumnLimit: 80 +--- +Language: Cpp +# Force pointers to the type for C++. +DerivePointerAlignment: false +PointerAlignment: Left +# Other adjustments +AccessModifierOffset: -1 +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: true +BinPackParameters: false +BreakBeforeBraces: Attach +BreakConstructorInitializers: BeforeColon +ConstructorInitializerAllOnOneLineOrOnePerLine: true +Cpp11BracedListStyle: true +IncludeBlocks: Regroup +IncludeCategories: + # Standard library headers + - Regex: '^<[^/]+>$' + Priority: 1 + # Other library headers + - Regex: '^<.+>$' + Priority: 2 + # Project headers with quotes + - Regex: '^"mcp/.+"$' + Priority: 3 + # Other project headers + - Regex: '^".+"$' + Priority: 4 +IndentCaseLabels: true +KeepEmptyLinesAtTheStartOfBlocks: false +NamespaceIndentation: None +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++11 +UseTab: Never \ No newline at end of file diff --git a/include/mcp/memory_utils.h b/include/mcp/memory_utils.h index bee9838d..0123853f 100644 --- a/include/mcp/memory_utils.h +++ b/include/mcp/memory_utils.h @@ -7,25 +7,26 @@ namespace mcp { // C++11 compatible make_unique implementation -template +template typename std::enable_if::value, std::unique_ptr>::type make_unique(Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); + return std::unique_ptr(new T(std::forward(args)...)); } // Array version for make_unique -template -typename std::enable_if::value && std::extent::value == 0, std::unique_ptr>::type +template +typename std::enable_if::value && std::extent::value == 0, + std::unique_ptr>::type make_unique(std::size_t size) { - typedef typename std::remove_extent::type element_type; - return std::unique_ptr(new element_type[size]()); + typedef typename std::remove_extent::type element_type; + return std::unique_ptr(new element_type[size]()); } // Disable make_unique for arrays with known bounds -template -typename std::enable_if::value != 0, void>::type -make_unique(Args&&...) = delete; +template +typename std::enable_if::value != 0, void>::type make_unique( + Args&&...) = delete; -} // namespace mcp +} // namespace mcp -#endif // MCP_MEMORY_UTILS_H \ No newline at end of file +#endif // MCP_MEMORY_UTILS_H \ No newline at end of file diff --git a/include/mcp/variant.h b/include/mcp/variant.h index 9c23a1a4..c403ccd9 100644 --- a/include/mcp/variant.h +++ b/include/mcp/variant.h @@ -1,390 +1,422 @@ #ifndef MCP_VARIANT_H #define MCP_VARIANT_H +#include +#include #include -#include #include -#include -#include +#include namespace mcp { // Forward declarations -template +template class variant; // Helper to get the index of a type in a type list -template +template struct type_index; -template +template struct type_index { - static constexpr std::size_t value = std::is_same::value ? 0 : 1 + type_index::value; + static constexpr std::size_t value = + std::is_same::value ? 0 : 1 + type_index::value; }; -template +template struct type_index { - static constexpr std::size_t value = std::is_same::value ? 0 : 1; + static constexpr std::size_t value = std::is_same::value ? 0 : 1; }; // Helper to check if a type is in a type list -template +template struct contains_type; -template +template struct contains_type : std::false_type {}; -template -struct contains_type - : std::conditional::value, std::true_type, contains_type>::type {}; +template +struct contains_type + : std::conditional::value, + std::true_type, + contains_type>::type {}; // Helper to check if a type is convertible to any type in the list -template +template struct is_convertible_to_any; -template +template struct is_convertible_to_any : std::false_type {}; -template -struct is_convertible_to_any - : std::conditional::value, std::true_type, is_convertible_to_any>::type {}; +template +struct is_convertible_to_any + : std::conditional::value, + std::true_type, + is_convertible_to_any>::type {}; // Helper to find the first type that T is convertible to -template +template struct find_convertible_type; -template +template struct find_convertible_type { - static constexpr std::size_t index = static_cast(-1); - using type = void; + static constexpr std::size_t index = static_cast(-1); + using type = void; }; -template +template struct find_convertible_type { -private: - static constexpr bool is_conv = std::is_convertible::value; - using rest_result = find_convertible_type; - -public: - static constexpr std::size_t index = is_conv ? I : rest_result::index; - using type = typename std::conditional::type; + private: + static constexpr bool is_conv = std::is_convertible::value; + using rest_result = find_convertible_type; + + public: + static constexpr std::size_t index = is_conv ? I : rest_result::index; + using type = typename std:: + conditional::type; }; // Helper to get the type at a given index -template +template struct type_at_index; -template +template struct type_at_index { - using type = typename type_at_index::type; + using type = typename type_at_index::type; }; -template +template struct type_at_index<0, First, Rest...> { - using type = First; + using type = First; }; // Helper to get the maximum size and alignment -template +template struct variant_storage_traits; -template +template struct variant_storage_traits { - static constexpr std::size_t size = sizeof(T); - static constexpr std::size_t alignment = alignof(T); + static constexpr std::size_t size = sizeof(T); + static constexpr std::size_t alignment = alignof(T); }; -template +template struct variant_storage_traits { - static constexpr std::size_t size = sizeof(T) > variant_storage_traits::size - ? sizeof(T) : variant_storage_traits::size; - static constexpr std::size_t alignment = alignof(T) > variant_storage_traits::alignment - ? alignof(T) : variant_storage_traits::alignment; + static constexpr std::size_t size = + sizeof(T) > variant_storage_traits::size + ? sizeof(T) + : variant_storage_traits::size; + static constexpr std::size_t alignment = + alignof(T) > variant_storage_traits::alignment + ? alignof(T) + : variant_storage_traits::alignment; }; // Visitor helper -template +template struct visitor_helper; // Bad variant access exception class bad_variant_access : public std::exception { -public: - const char* what() const noexcept override { - return "bad variant access"; - } + public: + const char* what() const noexcept override { return "bad variant access"; } }; // Main variant class -template +template class variant { -private: - static constexpr std::size_t storage_size = variant_storage_traits::size; - static constexpr std::size_t storage_alignment = variant_storage_traits::alignment; - - typename std::aligned_storage::type storage_; - std::size_t type_index_; - - // Destructor dispatcher - template - typename std::enable_if::type - destroy_impl() {} - - template - typename std::enable_if::type - destroy_impl() { - if (type_index_ == I) { - using T = typename type_at_index::type; - reinterpret_cast(&storage_)->~T(); - } else { - destroy_impl(); - } - } - - // Copy constructor dispatcher - template - typename std::enable_if::type - copy_construct_impl(const variant&) {} - - template - typename std::enable_if::type - copy_construct_impl(const variant& other) { - if (other.type_index_ == I) { - using T = typename type_at_index::type; - new (&storage_) T(*reinterpret_cast(&other.storage_)); - type_index_ = I; - } else { - copy_construct_impl(other); - } - } - - // Move constructor dispatcher - template - typename std::enable_if::type - move_construct_impl(variant&&) {} - - template - typename std::enable_if::type - move_construct_impl(variant&& other) { - if (other.type_index_ == I) { - using T = typename type_at_index::type; - new (&storage_) T(std::move(*reinterpret_cast(&other.storage_))); - type_index_ = I; - } else { - move_construct_impl(std::move(other)); - } - } - -public: - // Default constructor - constructs the first alternative - variant() : type_index_(0) { - using T = typename type_at_index<0, Types...>::type; - new (&storage_) T(); + private: + static constexpr std::size_t storage_size = + variant_storage_traits::size; + static constexpr std::size_t storage_alignment = + variant_storage_traits::alignment; + + typename std::aligned_storage::type storage_; + std::size_t type_index_; + + // Destructor dispatcher + template + typename std::enable_if::type destroy_impl() {} + + template + typename std::enable_if < I::type destroy_impl() { + if (type_index_ == I) { + using T = typename type_at_index::type; + reinterpret_cast(&storage_)->~T(); + } else { + destroy_impl(); } - - // Constructor from a value - exact match - template::type, Types...>::value>::type> - variant(T&& value) : type_index_(type_index::type, Types...>::value) { - using DecayedT = typename std::decay::type; - new (&storage_) DecayedT(std::forward(value)); + } + + // Copy constructor dispatcher + template + typename std::enable_if::type copy_construct_impl( + const variant&) {} + + template + typename std::enable_if < + I::type copy_construct_impl(const variant& other) { + if (other.type_index_ == I) { + using T = typename type_at_index::type; + new (&storage_) T(*reinterpret_cast(&other.storage_)); + type_index_ = I; + } else { + copy_construct_impl(other); } - - // Constructor from a value - implicit conversion - template::type, Types...>::value>::type, - typename = typename std::enable_if::value>::type> - variant(T&& value) : type_index_(find_convertible_type::index) { - using TargetType = typename find_convertible_type::type; - new (&storage_) TargetType(std::forward(value)); + } + + // Move constructor dispatcher + template + typename std::enable_if::type move_construct_impl( + variant&&) {} + + template + typename std::enable_if < + I::type move_construct_impl(variant&& other) { + if (other.type_index_ == I) { + using T = typename type_at_index::type; + new (&storage_) T(std::move(*reinterpret_cast(&other.storage_))); + type_index_ = I; + } else { + move_construct_impl(std::move(other)); } - - // Copy constructor - variant(const variant& other) : type_index_(static_cast(-1)) { - copy_construct_impl(other); + } + + public: + // Default constructor - constructs the first alternative + variant() : type_index_(0) { + using T = typename type_at_index<0, Types...>::type; + new (&storage_) T(); + } + + // Constructor from a value - exact match + template < + typename T, + typename = typename std::enable_if< + contains_type::type, Types...>::value>::type> + variant(T&& value) + : type_index_(type_index::type, Types...>::value) { + using DecayedT = typename std::decay::type; + new (&storage_) DecayedT(std::forward(value)); + } + + // Constructor from a value - implicit conversion + template < + typename T, + typename = typename std::enable_if< + !contains_type::type, Types...>::value>::type, + typename = typename std::enable_if< + is_convertible_to_any::value>::type> + variant(T&& value) + : type_index_(find_convertible_type::index) { + using TargetType = typename find_convertible_type::type; + new (&storage_) TargetType(std::forward(value)); + } + + // Copy constructor + variant(const variant& other) : type_index_(static_cast(-1)) { + copy_construct_impl(other); + } + + // Move constructor + variant(variant&& other) noexcept + : type_index_(static_cast(-1)) { + move_construct_impl(std::move(other)); + } + + // Destructor + ~variant() { destroy_impl(); } + + // Copy assignment + variant& operator=(const variant& other) { + if (this != &other) { + destroy_impl(); + copy_construct_impl(other); } - - // Move constructor - variant(variant&& other) noexcept : type_index_(static_cast(-1)) { - move_construct_impl(std::move(other)); + return *this; + } + + // Move assignment + variant& operator=(variant&& other) noexcept { + if (this != &other) { + destroy_impl(); + move_construct_impl(std::move(other)); } - - // Destructor - ~variant() { - destroy_impl(); + return *this; + } + + // Assignment from value - exact match + template < + typename T, + typename = typename std::enable_if< + contains_type::type, Types...>::value>::type> + variant& operator=(T&& value) { + using DecayedT = typename std::decay::type; + destroy_impl(); + new (&storage_) DecayedT(std::forward(value)); + type_index_ = type_index::value; + return *this; + } + + // Assignment from value - implicit conversion + template < + typename T, + typename = typename std::enable_if< + !contains_type::type, Types...>::value>::type, + typename = typename std::enable_if< + is_convertible_to_any::value>::type, + typename = void> + variant& operator=(T&& value) { + using TargetType = typename find_convertible_type::type; + destroy_impl(); + new (&storage_) TargetType(std::forward(value)); + type_index_ = find_convertible_type::index; + return *this; + } + + // Get the index of the current alternative + std::size_t index() const noexcept { return type_index_; } + + // Check if the variant holds a specific type + template + bool holds_alternative() const noexcept { + return type_index_ == type_index::value; + } + + // Get a reference to the stored value + template + T& get() { + if (!holds_alternative()) { + throw bad_variant_access(); } - - // Copy assignment - variant& operator=(const variant& other) { - if (this != &other) { - destroy_impl(); - copy_construct_impl(other); - } - return *this; - } - - // Move assignment - variant& operator=(variant&& other) noexcept { - if (this != &other) { - destroy_impl(); - move_construct_impl(std::move(other)); - } - return *this; - } - - // Assignment from value - exact match - template::type, Types...>::value>::type> - variant& operator=(T&& value) { - using DecayedT = typename std::decay::type; - destroy_impl(); - new (&storage_) DecayedT(std::forward(value)); - type_index_ = type_index::value; - return *this; - } - - // Assignment from value - implicit conversion - template::type, Types...>::value>::type, - typename = typename std::enable_if::value>::type, - typename = void> - variant& operator=(T&& value) { - using TargetType = typename find_convertible_type::type; - destroy_impl(); - new (&storage_) TargetType(std::forward(value)); - type_index_ = find_convertible_type::index; - return *this; - } - - // Get the index of the current alternative - std::size_t index() const noexcept { - return type_index_; - } - - // Check if the variant holds a specific type - template - bool holds_alternative() const noexcept { - return type_index_ == type_index::value; - } - - // Get a reference to the stored value - template - T& get() { - if (!holds_alternative()) { - throw bad_variant_access(); - } - return *reinterpret_cast(&storage_); - } - - template - const T& get() const { - if (!holds_alternative()) { - throw bad_variant_access(); - } - return *reinterpret_cast(&storage_); + return *reinterpret_cast(&storage_); + } + + template + const T& get() const { + if (!holds_alternative()) { + throw bad_variant_access(); } - - // Get a pointer to the stored value (returns nullptr if wrong type) - template - T* get_if() noexcept { - if (!holds_alternative()) { - return nullptr; - } - return reinterpret_cast(&storage_); + return *reinterpret_cast(&storage_); + } + + // Get a pointer to the stored value (returns nullptr if wrong type) + template + T* get_if() noexcept { + if (!holds_alternative()) { + return nullptr; } - - template - const T* get_if() const noexcept { - if (!holds_alternative()) { - return nullptr; - } - return reinterpret_cast(&storage_); + return reinterpret_cast(&storage_); + } + + template + const T* get_if() const noexcept { + if (!holds_alternative()) { + return nullptr; } - - // Visit helper - moved outside class to avoid incomplete type issues + return reinterpret_cast(&storage_); + } + + // Visit helper - moved outside class to avoid incomplete type issues }; // Visit functions - defined outside class -template -auto visit(Visitor&& vis, variant& v) -> decltype(vis(v.template get::type>())) { - return visitor_helper::visit(std::forward(vis), v, v.index()); +template +auto visit(Visitor&& vis, variant& v) -> decltype(vis( + v.template get::type>())) { + return visitor_helper::visit(std::forward(vis), v, + v.index()); } -template -auto visit(Visitor&& vis, const variant& v) -> decltype(vis(v.template get::type>())) { - return visitor_helper::visit(std::forward(vis), v, v.index()); +template +auto visit(Visitor&& vis, const variant& v) -> decltype(vis( + v.template get::type>())) { + return visitor_helper::visit(std::forward(vis), v, + v.index()); } // Visitor implementation -template +template struct visitor_helper { - template - static typename std::enable_if()(std::declval().template get::type>()))>::type - visit(Visitor&& vis, Variant&& v, std::size_t) { - using T = typename type_at_index::type; - return vis(std::forward(v).template get()); - } - - template - static typename std::enable_if()(std::declval().template get::type>()))>::type - visit(Visitor&& vis, Variant&& v, std::size_t index) { - if (index == I) { - using T = typename type_at_index::type; - return vis(std::forward(v).template get()); - } - return visit(std::forward(vis), std::forward(v), index); + template + static typename std::enable_if< + I == sizeof...(Types) - 1, + decltype(std::declval()( + std::declval() + .template get::type>()))>:: + type + visit(Visitor&& vis, Variant&& v, std::size_t) { + using T = typename type_at_index::type; + return vis(std::forward(v).template get()); + } + + template + static typename std::enable_if < + I()( + std::declval() + .template get::type>()))>:: + type + visit(Visitor&& vis, Variant&& v, std::size_t index) { + if (index == I) { + using T = typename type_at_index::type; + return vis(std::forward(v).template get()); } + return visit(std::forward(vis), + std::forward(v), index); + } }; // Helper function to create a variant (similar to std::make_variant) -template +template variant make_variant(T&& value) { - return variant(std::forward(value)); + return variant(std::forward(value)); } // Overload pattern for visitor - C++11 compatible version -template +template struct overload_impl : F, overload_impl { - overload_impl(F f, Fs... fs) : F(f), overload_impl(fs...) {} - using F::operator(); - using overload_impl::operator(); + overload_impl(F f, Fs... fs) : F(f), overload_impl(fs...) {} + using F::operator(); + using overload_impl::operator(); }; -template +template struct overload_impl : F { - overload_impl(F f) : F(f) {} - using F::operator(); + overload_impl(F f) : F(f) {} + using F::operator(); }; -template +template overload_impl make_overload(Fs... fs) { - return overload_impl(fs...); + return overload_impl(fs...); } // C++11 requires out-of-class definitions for static constexpr members -template +template constexpr std::size_t type_index::value; -template +template constexpr std::size_t type_index::value; -template +template constexpr std::size_t find_convertible_type::index; -template +template constexpr std::size_t find_convertible_type::index; -template +template constexpr std::size_t variant_storage_traits::size; -template +template constexpr std::size_t variant_storage_traits::alignment; -template +template constexpr std::size_t variant_storage_traits::size; -template +template constexpr std::size_t variant_storage_traits::alignment; -} // namespace mcp +} // namespace mcp -#endif // MCP_VARIANT_H \ No newline at end of file +#endif // MCP_VARIANT_H \ No newline at end of file diff --git a/tests/test_variant.cpp b/tests/test_variant.cpp index 022956b4..35eae312 100644 --- a/tests/test_variant.cpp +++ b/tests/test_variant.cpp @@ -1,49 +1,51 @@ -#include -#include "mcp/variant.h" -#include "mcp/memory_utils.h" -#include -#include -#include #include -#include #include -#include #include +#include #include +#include +#include +#include +#include + +#include + +#include "mcp/memory_utils.h" +#include "mcp/variant.h" // Test fixture for tracking construction/destruction struct LifecycleTracker { - static std::atomic constructions; - static std::atomic destructions; - static std::atomic copies; - static std::atomic moves; - - int id; - - LifecycleTracker() : id(constructions++) {} - LifecycleTracker(int i) : id(i) { constructions++; } - LifecycleTracker(const LifecycleTracker& other) : id(other.id) { copies++; } - LifecycleTracker(LifecycleTracker&& other) : id(other.id) { moves++; } - ~LifecycleTracker() { destructions++; } - - LifecycleTracker& operator=(const LifecycleTracker& other) { - id = other.id; - copies++; - return *this; - } - - LifecycleTracker& operator=(LifecycleTracker&& other) { - id = other.id; - moves++; - return *this; - } - - static void reset() { - constructions = 0; - destructions = 0; - copies = 0; - moves = 0; - } + static std::atomic constructions; + static std::atomic destructions; + static std::atomic copies; + static std::atomic moves; + + int id; + + LifecycleTracker() : id(constructions++) {} + LifecycleTracker(int i) : id(i) { constructions++; } + LifecycleTracker(const LifecycleTracker& other) : id(other.id) { copies++; } + LifecycleTracker(LifecycleTracker&& other) : id(other.id) { moves++; } + ~LifecycleTracker() { destructions++; } + + LifecycleTracker& operator=(const LifecycleTracker& other) { + id = other.id; + copies++; + return *this; + } + + LifecycleTracker& operator=(LifecycleTracker&& other) { + id = other.id; + moves++; + return *this; + } + + static void reset() { + constructions = 0; + destructions = 0; + copies = 0; + moves = 0; + } }; std::atomic LifecycleTracker::constructions(0); @@ -53,496 +55,509 @@ std::atomic LifecycleTracker::moves(0); // Move-only type for testing struct MoveOnlyType { - std::unique_ptr data; - - MoveOnlyType() : data(mcp::make_unique(0)) {} - MoveOnlyType(int val) : data(mcp::make_unique(val)) {} - MoveOnlyType(const MoveOnlyType&) = delete; - MoveOnlyType& operator=(const MoveOnlyType&) = delete; - MoveOnlyType(MoveOnlyType&&) = default; - MoveOnlyType& operator=(MoveOnlyType&&) = default; - - int value() const { return data ? *data : -1; } + std::unique_ptr data; + + MoveOnlyType() : data(mcp::make_unique(0)) {} + MoveOnlyType(int val) : data(mcp::make_unique(val)) {} + MoveOnlyType(const MoveOnlyType&) = delete; + MoveOnlyType& operator=(const MoveOnlyType&) = delete; + MoveOnlyType(MoveOnlyType&&) = default; + MoveOnlyType& operator=(MoveOnlyType&&) = default; + + int value() const { return data ? *data : -1; } }; // Complex type with multiple members struct ComplexType { - int id; - std::string name; - std::vector values; - std::map metadata; - - ComplexType() : id(0), name("default") {} - ComplexType(int i, const std::string& n) : id(i), name(n) {} - - bool operator==(const ComplexType& other) const { - return id == other.id && name == other.name && - values == other.values && metadata == other.metadata; - } + int id; + std::string name; + std::vector values; + std::map metadata; + + ComplexType() : id(0), name("default") {} + ComplexType(int i, const std::string& n) : id(i), name(n) {} + + bool operator==(const ComplexType& other) const { + return id == other.id && name == other.name && values == other.values && + metadata == other.metadata; + } }; // Recursive type for stress testing struct RecursiveType { - int value; - std::vector> children; - - RecursiveType(int v) : value(v) {} + int value; + std::vector> children; + + RecursiveType(int v) : value(v) {} }; // Exception-throwing type for testing exception safety struct ThrowingType { - static bool should_throw; - int value; - - ThrowingType(int v) : value(v) { - if (should_throw) throw std::runtime_error("Construction failed"); - } - - ThrowingType(const ThrowingType& other) : value(other.value) { - if (should_throw) throw std::runtime_error("Copy failed"); - } - - ThrowingType(ThrowingType&& other) : value(other.value) { - if (should_throw) throw std::runtime_error("Move failed"); - } + static bool should_throw; + int value; + + ThrowingType(int v) : value(v) { + if (should_throw) + throw std::runtime_error("Construction failed"); + } + + ThrowingType(const ThrowingType& other) : value(other.value) { + if (should_throw) + throw std::runtime_error("Copy failed"); + } + + ThrowingType(ThrowingType&& other) : value(other.value) { + if (should_throw) + throw std::runtime_error("Move failed"); + } }; bool ThrowingType::should_throw = false; class VariantTest : public ::testing::Test { -protected: - void SetUp() override { - LifecycleTracker::reset(); - ThrowingType::should_throw = false; - } + protected: + void SetUp() override { + LifecycleTracker::reset(); + ThrowingType::should_throw = false; + } }; // Basic functionality tests TEST_F(VariantTest, DefaultConstruction) { - mcp::variant v; - EXPECT_EQ(v.index(), 0u); - EXPECT_TRUE(v.holds_alternative()); - EXPECT_EQ(v.get(), 0); + mcp::variant v; + EXPECT_EQ(v.index(), 0u); + EXPECT_TRUE(v.holds_alternative()); + EXPECT_EQ(v.get(), 0); } TEST_F(VariantTest, ValueConstruction) { - // Test each type in the variant - mcp::variant> v1(42); - EXPECT_EQ(v1.get(), 42); - - mcp::variant> v2(3.14159); - EXPECT_DOUBLE_EQ(v2.get(), 3.14159); - - mcp::variant> v3(std::string("hello world")); - EXPECT_EQ(v3.get(), "hello world"); - - mcp::variant> v4(std::vector{1, 2, 3, 4, 5}); - EXPECT_EQ(v4.get>().size(), 5u); + // Test each type in the variant + mcp::variant> v1(42); + EXPECT_EQ(v1.get(), 42); + + mcp::variant> v2(3.14159); + EXPECT_DOUBLE_EQ(v2.get(), 3.14159); + + mcp::variant> v3( + std::string("hello world")); + EXPECT_EQ(v3.get(), "hello world"); + + mcp::variant> v4( + std::vector{1, 2, 3, 4, 5}); + EXPECT_EQ(v4.get>().size(), 5u); } // Lifecycle management tests TEST_F(VariantTest, LifecycleTracking) { - // Track initial state - int initial_constructions = LifecycleTracker::constructions; - int initial_destructions = LifecycleTracker::destructions; - - { - mcp::variant v1(LifecycleTracker(42)); - // At least one construction and move should have happened - EXPECT_GT(LifecycleTracker::constructions, initial_constructions); - EXPECT_GT(LifecycleTracker::moves, 0); - - mcp::variant v2(v1); // copy - EXPECT_GT(LifecycleTracker::copies, 0); - - int before_assign = LifecycleTracker::destructions; - v2 = 10; // destroy LifecycleTracker - EXPECT_GT(LifecycleTracker::destructions, before_assign); - - int before_copy = LifecycleTracker::copies; - v2 = v1; // copy construct again - EXPECT_GT(LifecycleTracker::copies, before_copy); - } - - // All objects should be destroyed now - int constructions = LifecycleTracker::constructions - initial_constructions; - int total_destroyed = LifecycleTracker::destructions - initial_destructions; - - // The lifecycle is correct if all objects are eventually destroyed - // We have 1 construction + 2 copies = 3 objects - // Plus 1 temporary that was destroyed after move = 4 destructions - EXPECT_EQ(total_destroyed, constructions + LifecycleTracker::copies + 1); + // Track initial state + int initial_constructions = LifecycleTracker::constructions; + int initial_destructions = LifecycleTracker::destructions; + + { + mcp::variant v1(LifecycleTracker(42)); + // At least one construction and move should have happened + EXPECT_GT(LifecycleTracker::constructions, initial_constructions); + EXPECT_GT(LifecycleTracker::moves, 0); + + mcp::variant v2(v1); // copy + EXPECT_GT(LifecycleTracker::copies, 0); + + int before_assign = LifecycleTracker::destructions; + v2 = 10; // destroy LifecycleTracker + EXPECT_GT(LifecycleTracker::destructions, before_assign); + + int before_copy = LifecycleTracker::copies; + v2 = v1; // copy construct again + EXPECT_GT(LifecycleTracker::copies, before_copy); + } + + // All objects should be destroyed now + int constructions = LifecycleTracker::constructions - initial_constructions; + int total_destroyed = LifecycleTracker::destructions - initial_destructions; + + // The lifecycle is correct if all objects are eventually destroyed + // We have 1 construction + 2 copies = 3 objects + // Plus 1 temporary that was destroyed after move = 4 destructions + EXPECT_EQ(total_destroyed, constructions + LifecycleTracker::copies + 1); } // Move semantics tests TEST_F(VariantTest, MoveOnlyTypes) { - mcp::variant v1(MoveOnlyType(42)); - EXPECT_EQ(v1.get().value(), 42); - - mcp::variant v2(std::move(v1)); - EXPECT_EQ(v2.get().value(), 42); - - v2 = MoveOnlyType(100); - EXPECT_EQ(v2.get().value(), 100); + mcp::variant v1(MoveOnlyType(42)); + EXPECT_EQ(v1.get().value(), 42); + + mcp::variant v2(std::move(v1)); + EXPECT_EQ(v2.get().value(), 42); + + v2 = MoveOnlyType(100); + EXPECT_EQ(v2.get().value(), 100); } TEST_F(VariantTest, MoveSemantics) { - std::string long_string(1000, 'x'); - mcp::variant v1(long_string); - - std::string* ptr1 = v1.get_if(); - ASSERT_NE(ptr1, nullptr); - - mcp::variant v2(std::move(v1)); - EXPECT_EQ(v2.get(), long_string); + std::string long_string(1000, 'x'); + mcp::variant v1(long_string); + + std::string* ptr1 = v1.get_if(); + ASSERT_NE(ptr1, nullptr); + + mcp::variant v2(std::move(v1)); + EXPECT_EQ(v2.get(), long_string); } // Copy semantics tests TEST_F(VariantTest, DeepCopy) { - ComplexType complex; - complex.id = 42; - complex.name = "test"; - complex.values = {1.1, 2.2, 3.3}; - complex.metadata["key1"] = 100; - complex.metadata["key2"] = 200; - - mcp::variant v1(complex); - mcp::variant v2(v1); - - // Modify original - v1.get().id = 99; - v1.get().values.push_back(4.4); - - // Copy should be unchanged - EXPECT_EQ(v2.get().id, 42); - EXPECT_EQ(v2.get().values.size(), 3u); + ComplexType complex; + complex.id = 42; + complex.name = "test"; + complex.values = {1.1, 2.2, 3.3}; + complex.metadata["key1"] = 100; + complex.metadata["key2"] = 200; + + mcp::variant v1(complex); + mcp::variant v2(v1); + + // Modify original + v1.get().id = 99; + v1.get().values.push_back(4.4); + + // Copy should be unchanged + EXPECT_EQ(v2.get().id, 42); + EXPECT_EQ(v2.get().values.size(), 3u); } // Exception safety tests TEST_F(VariantTest, ExceptionSafety) { - mcp::variant v(42); // int type - - ThrowingType::should_throw = true; - - // Copy construction should fail but leave v unchanged - try { - mcp::variant v2(ThrowingType(10)); - FAIL() << "Expected exception"; - } catch (...) { - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get(), 42); - } - - // Assignment should fail but leave v unchanged - try { - v = ThrowingType(20); - FAIL() << "Expected exception"; - } catch (...) { - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get(), 42); - } + mcp::variant v(42); // int type + + ThrowingType::should_throw = true; + + // Copy construction should fail but leave v unchanged + try { + mcp::variant v2(ThrowingType(10)); + FAIL() << "Expected exception"; + } catch (...) { + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get(), 42); + } + + // Assignment should fail but leave v unchanged + try { + v = ThrowingType(20); + FAIL() << "Expected exception"; + } catch (...) { + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get(), 42); + } } // Visitor pattern intensive tests TEST_F(VariantTest, VisitorAllTypes) { - using TestVariant = mcp::variant; - - struct TypeNameVisitor { - std::string operator()(bool) const { return "bool"; } - std::string operator()(char) const { return "char"; } - std::string operator()(int) const { return "int"; } - std::string operator()(long) const { return "long"; } - std::string operator()(float) const { return "float"; } - std::string operator()(double) const { return "double"; } - std::string operator()(const std::string&) const { return "string"; } - }; - - TestVariant v; - - v = true; - EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "bool"); - - v = 'A'; - EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "char"); - - v = 42; - EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "int"); - - v = 42L; - EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "long"); - - v = 3.14f; - EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "float"); - - v = 3.14159; - EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "double"); - - v = std::string("test"); - EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "string"); + using TestVariant = + mcp::variant; + + struct TypeNameVisitor { + std::string operator()(bool) const { return "bool"; } + std::string operator()(char) const { return "char"; } + std::string operator()(int) const { return "int"; } + std::string operator()(long) const { return "long"; } + std::string operator()(float) const { return "float"; } + std::string operator()(double) const { return "double"; } + std::string operator()(const std::string&) const { return "string"; } + }; + + TestVariant v; + + v = true; + EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "bool"); + + v = 'A'; + EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "char"); + + v = 42; + EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "int"); + + v = 42L; + EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "long"); + + v = 3.14f; + EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "float"); + + v = 3.14159; + EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "double"); + + v = std::string("test"); + EXPECT_EQ(mcp::visit(TypeNameVisitor{}, v), "string"); } TEST_F(VariantTest, VisitorWithState) { - mcp::variant v; - - struct AccumulatorVisitor { - double sum = 0; - void operator()(int i) { sum += i; } - void operator()(double d) { sum += d; } - void operator()(const std::string& s) { sum += s.length(); } - }; - - AccumulatorVisitor acc; - - v = 10; - mcp::visit(std::ref(acc), v); - - v = 5.5; - mcp::visit(std::ref(acc), v); - - v = std::string("hello"); - mcp::visit(std::ref(acc), v); - - EXPECT_DOUBLE_EQ(acc.sum, 20.5); + mcp::variant v; + + struct AccumulatorVisitor { + double sum = 0; + void operator()(int i) { sum += i; } + void operator()(double d) { sum += d; } + void operator()(const std::string& s) { sum += s.length(); } + }; + + AccumulatorVisitor acc; + + v = 10; + mcp::visit(std::ref(acc), v); + + v = 5.5; + mcp::visit(std::ref(acc), v); + + v = std::string("hello"); + mcp::visit(std::ref(acc), v); + + EXPECT_DOUBLE_EQ(acc.sum, 20.5); } // Overload pattern tests TEST_F(VariantTest, ComplexOverloadPattern) { - using TestVariant = mcp::variant>; - - auto visitor = mcp::make_overload( - [](int i) -> std::string { return "int: " + std::to_string(i); }, - [](double d) -> std::string { return "double: " + std::to_string(d); }, - [](const std::string& s) -> std::string { return "string: " + s; }, - [](const std::vector& v) -> std::string { - return "vector: size=" + std::to_string(v.size()); - } - ); - - TestVariant v(42); - EXPECT_EQ(mcp::visit(visitor, v), "int: 42"); - - v = 3.14; - EXPECT_EQ(mcp::visit(visitor, v), "double: 3.140000"); - - v = std::string("hello"); - EXPECT_EQ(mcp::visit(visitor, v), "string: hello"); - - v = std::vector{1, 2, 3}; - EXPECT_EQ(mcp::visit(visitor, v), "vector: size=3"); + using TestVariant = mcp::variant>; + + auto visitor = mcp::make_overload( + [](int i) -> std::string { return "int: " + std::to_string(i); }, + [](double d) -> std::string { return "double: " + std::to_string(d); }, + [](const std::string& s) -> std::string { return "string: " + s; }, + [](const std::vector& v) -> std::string { + return "vector: size=" + std::to_string(v.size()); + }); + + TestVariant v(42); + EXPECT_EQ(mcp::visit(visitor, v), "int: 42"); + + v = 3.14; + EXPECT_EQ(mcp::visit(visitor, v), "double: 3.140000"); + + v = std::string("hello"); + EXPECT_EQ(mcp::visit(visitor, v), "string: hello"); + + v = std::vector{1, 2, 3}; + EXPECT_EQ(mcp::visit(visitor, v), "vector: size=3"); } // Recursive variant tests TEST_F(VariantTest, RecursiveVariant) { - RecursiveType root(1); - root.children.push_back(2); - root.children.push_back(RecursiveType(3)); - - RecursiveType child(4); - child.children.push_back(5); - root.children.push_back(std::move(child)); - - mcp::variant v(std::move(root)); - - struct CountVisitor { - int count = 0; - void operator()(int) { count++; } - void operator()(const RecursiveType& r) { - count++; - for (const auto& child : r.children) { - mcp::visit(std::ref(*this), child); - } - } - }; - - CountVisitor counter; - mcp::visit(std::ref(counter), v); - EXPECT_EQ(counter.count, 5); // root + 2 ints + 2 recursive types + RecursiveType root(1); + root.children.push_back(2); + root.children.push_back(RecursiveType(3)); + + RecursiveType child(4); + child.children.push_back(5); + root.children.push_back(std::move(child)); + + mcp::variant v(std::move(root)); + + struct CountVisitor { + int count = 0; + void operator()(int) { count++; } + void operator()(const RecursiveType& r) { + count++; + for (const auto& child : r.children) { + mcp::visit(std::ref(*this), child); + } + } + }; + + CountVisitor counter; + mcp::visit(std::ref(counter), v); + EXPECT_EQ(counter.count, 5); // root + 2 ints + 2 recursive types } // Alignment and size tests TEST_F(VariantTest, AlignmentAndSize) { - struct Aligned16 { - alignas(16) char data[16]; - }; - - struct Aligned32 { - alignas(32) char data[32]; - }; - - using AlignedVariant = mcp::variant; - - // Check that variant respects alignment - AlignedVariant v1; - EXPECT_EQ(reinterpret_cast(&v1) % alignof(AlignedVariant), 0u); - - v1 = Aligned32{}; - EXPECT_TRUE(v1.holds_alternative()); + struct Aligned16 { + alignas(16) char data[16]; + }; + + struct Aligned32 { + alignas(32) char data[32]; + }; + + using AlignedVariant = mcp::variant; + + // Check that variant respects alignment + AlignedVariant v1; + EXPECT_EQ(reinterpret_cast(&v1) % alignof(AlignedVariant), 0u); + + v1 = Aligned32{}; + EXPECT_TRUE(v1.holds_alternative()); } // Stress tests with many types TEST_F(VariantTest, ManyTypes) { - using MegaVariant = mcp::variant< - bool, char, short, int, long, long long, - unsigned char, unsigned short, unsigned int, unsigned long, - float, double, long double, - std::string, std::vector, std::map, - std::set, std::unique_ptr - >; - - MegaVariant v; - - // Test each type - v = false; - EXPECT_EQ(v.index(), 0u); - - v = std::string("test"); - EXPECT_EQ(v.index(), 13u); - - v = mcp::make_unique(42); - EXPECT_EQ(v.index(), 17u); - EXPECT_EQ(*v.get>(), 42); + using MegaVariant = + mcp::variant, + std::map, std::set, std::unique_ptr>; + + MegaVariant v; + + // Test each type + v = false; + EXPECT_EQ(v.index(), 0u); + + v = std::string("test"); + EXPECT_EQ(v.index(), 13u); + + v = mcp::make_unique(42); + EXPECT_EQ(v.index(), 17u); + EXPECT_EQ(*v.get>(), 42); } // Performance stress test TEST_F(VariantTest, PerformanceStress) { - using PerfVariant = mcp::variant>; - - const int iterations = 10000; - std::vector variants; - variants.reserve(iterations); - - // Create many variants - for (int i = 0; i < iterations; ++i) { - switch (i % 4) { - case 0: variants.emplace_back(i); break; - case 1: variants.emplace_back(static_cast(i) * 1.5); break; - case 2: variants.emplace_back("string_" + std::to_string(i)); break; - case 3: variants.emplace_back(std::vector{i, i+1, i+2}); break; - } - } - - // Visit all variants - struct SumVisitor { - double sum = 0; - void operator()(int i) { sum += i; } - void operator()(double d) { sum += d; } - void operator()(const std::string& s) { sum += s.length(); } - void operator()(const std::vector& v) { sum += v.size(); } - }; - - SumVisitor visitor; - for (const auto& v : variants) { - mcp::visit(std::ref(visitor), v); + using PerfVariant = mcp::variant>; + + const int iterations = 10000; + std::vector variants; + variants.reserve(iterations); + + // Create many variants + for (int i = 0; i < iterations; ++i) { + switch (i % 4) { + case 0: + variants.emplace_back(i); + break; + case 1: + variants.emplace_back(static_cast(i) * 1.5); + break; + case 2: + variants.emplace_back("string_" + std::to_string(i)); + break; + case 3: + variants.emplace_back(std::vector{i, i + 1, i + 2}); + break; } - - EXPECT_GT(visitor.sum, 0); + } + + // Visit all variants + struct SumVisitor { + double sum = 0; + void operator()(int i) { sum += i; } + void operator()(double d) { sum += d; } + void operator()(const std::string& s) { sum += s.length(); } + void operator()(const std::vector& v) { sum += v.size(); } + }; + + SumVisitor visitor; + for (const auto& v : variants) { + mcp::visit(std::ref(visitor), v); + } + + EXPECT_GT(visitor.sum, 0); } // Edge cases TEST_F(VariantTest, EmptyTypes) { - struct Empty {}; - mcp::variant v; - EXPECT_TRUE(v.holds_alternative()); + struct Empty {}; + mcp::variant v; + EXPECT_TRUE(v.holds_alternative()); } TEST_F(VariantTest, SingleType) { - mcp::variant v(42); - EXPECT_EQ(v.get(), 42); - - v = 100; - EXPECT_EQ(v.get(), 100); + mcp::variant v(42); + EXPECT_EQ(v.get(), 42); + + v = 100; + EXPECT_EQ(v.get(), 100); } TEST_F(VariantTest, ConstCorrectness) { - const mcp::variant cv(42); - - // These should compile - const int& i = cv.get(); - const int* pi = cv.get_if(); - - EXPECT_EQ(i, 42); - EXPECT_NE(pi, nullptr); - EXPECT_EQ(*pi, 42); - - // Visitor with const variant - struct ConstVisitor { - int operator()(const int& i) const { return i * 2; } - int operator()(const std::string& s) const { return static_cast(s.length()); } - }; - - EXPECT_EQ(mcp::visit(ConstVisitor{}, cv), 84); + const mcp::variant cv(42); + + // These should compile + const int& i = cv.get(); + const int* pi = cv.get_if(); + + EXPECT_EQ(i, 42); + EXPECT_NE(pi, nullptr); + EXPECT_EQ(*pi, 42); + + // Visitor with const variant + struct ConstVisitor { + int operator()(const int& i) const { return i * 2; } + int operator()(const std::string& s) const { + return static_cast(s.length()); + } + }; + + EXPECT_EQ(mcp::visit(ConstVisitor{}, cv), 84); } // Self-assignment tests TEST_F(VariantTest, SelfAssignment) { - mcp::variant> v("hello"); - - // Self-assignment through reference to avoid compiler warning - auto& v_ref = v; - v = v_ref; - EXPECT_EQ(v.get(), "hello"); - - // Self-move is undefined behavior, so test move from temporary - mcp::variant> temp("world"); - v = std::move(temp); - EXPECT_EQ(v.get(), "world"); + mcp::variant> v("hello"); + + // Self-assignment through reference to avoid compiler warning + auto& v_ref = v; + v = v_ref; + EXPECT_EQ(v.get(), "hello"); + + // Self-move is undefined behavior, so test move from temporary + mcp::variant> temp("world"); + v = std::move(temp); + EXPECT_EQ(v.get(), "world"); } // Bad access tests TEST_F(VariantTest, BadAccess) { - mcp::variant v(42); - - EXPECT_THROW(v.get(), mcp::bad_variant_access); - EXPECT_THROW(v.get(), mcp::bad_variant_access); - - try { - v.get(); - } catch (const mcp::bad_variant_access& e) { - EXPECT_STREQ(e.what(), "bad variant access"); - } + mcp::variant v(42); + + EXPECT_THROW(v.get(), mcp::bad_variant_access); + EXPECT_THROW(v.get(), mcp::bad_variant_access); + + try { + v.get(); + } catch (const mcp::bad_variant_access& e) { + EXPECT_STREQ(e.what(), "bad variant access"); + } } // Type deduction tests TEST_F(VariantTest, TypeDeduction) { - auto v1 = mcp::make_variant(42); - EXPECT_EQ(v1.get(), 42); - - auto v2 = mcp::make_variant(3.14); - EXPECT_DOUBLE_EQ(v2.get(), 3.14); + auto v1 = mcp::make_variant(42); + EXPECT_EQ(v1.get(), 42); + + auto v2 = mcp::make_variant(3.14); + EXPECT_DOUBLE_EQ(v2.get(), 3.14); } // Variant of variants TEST_F(VariantTest, NestedVariants) { - using InnerVariant = mcp::variant; - using OuterVariant = mcp::variant; - - OuterVariant v1(InnerVariant(42)); - EXPECT_TRUE(v1.holds_alternative()); - EXPECT_EQ(v1.get().get(), 42); - - OuterVariant v2(InnerVariant("nested")); - EXPECT_EQ(v2.get().get(), "nested"); + using InnerVariant = mcp::variant; + using OuterVariant = mcp::variant; + + OuterVariant v1(InnerVariant(42)); + EXPECT_TRUE(v1.holds_alternative()); + EXPECT_EQ(v1.get().get(), 42); + + OuterVariant v2(InnerVariant("nested")); + EXPECT_EQ(v2.get().get(), "nested"); } // Thread safety test (variants should be thread-safe for const access) TEST_F(VariantTest, ThreadSafeConstAccess) { - const mcp::variant v(42); - std::atomic sum(0); - - std::vector threads; - for (int i = 0; i < 10; ++i) { - threads.emplace_back([&v, &sum]() { - for (int j = 0; j < 1000; ++j) { - if (v.holds_alternative()) { - sum += v.get(); - } - } - }); - } - - for (auto& t : threads) { - t.join(); - } - - EXPECT_EQ(sum.load(), 42 * 10 * 1000); + const mcp::variant v(42); + std::atomic sum(0); + + std::vector threads; + for (int i = 0; i < 10; ++i) { + threads.emplace_back([&v, &sum]() { + for (int j = 0; j < 1000; ++j) { + if (v.holds_alternative()) { + sum += v.get(); + } + } + }); + } + + for (auto& t : threads) { + t.join(); + } + + EXPECT_EQ(sum.load(), 42 * 10 * 1000); } \ No newline at end of file diff --git a/tests/test_variant_advanced.cpp b/tests/test_variant_advanced.cpp index 7b67cdd9..6983c201 100644 --- a/tests/test_variant_advanced.cpp +++ b/tests/test_variant_advanced.cpp @@ -1,414 +1,415 @@ -#include -#include "mcp/variant.h" -#include "mcp/memory_utils.h" -#include -#include +#include +#include #include -#include #include +#include +#include #include -#include -#include +#include + +#include + +#include "mcp/memory_utils.h" +#include "mcp/variant.h" // Advanced test scenarios for mcp::variant // Focus on edge cases, SFINAE, and complex type interactions // Helper to test if a type is constructible -template +template class is_constructible_test { - template - static auto test(int) -> decltype(U(std::declval()...), std::true_type{}); - - template - static std::false_type test(...); - -public: - static constexpr bool value = decltype(test(0))::value; + template + static auto test(int) -> decltype(U(std::declval()...), std::true_type{}); + + template + static std::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; }; // Test types with special characteristics struct ExplicitConversion { - explicit ExplicitConversion(int) {} + explicit ExplicitConversion(int) {} }; struct MultipleConversions { - MultipleConversions(int) {} - MultipleConversions(double) {} - MultipleConversions(const std::string&) {} + MultipleConversions(int) {} + MultipleConversions(double) {} + MultipleConversions(const std::string&) {} }; struct BaseClass { - virtual ~BaseClass() = default; - virtual int value() const { return 1; } + virtual ~BaseClass() = default; + virtual int value() const { return 1; } }; struct DerivedClass : BaseClass { - int value() const override { return 2; } + int value() const override { return 2; } }; struct AbstractBase { - virtual ~AbstractBase() = default; - virtual void pure() = 0; + virtual ~AbstractBase() = default; + virtual void pure() = 0; }; struct ConcreteImpl : AbstractBase { - void pure() override {} - int data = 42; + void pure() override {} + int data = 42; }; // Complex visitor scenarios class AdvancedVariantTest : public ::testing::Test { -protected: - void SetUp() override {} - void TearDown() override {} + protected: + void SetUp() override {} + void TearDown() override {} }; // Test variant with inheritance hierarchies TEST_F(AdvancedVariantTest, PolymorphicTypes) { - // Store pointers to polymorphic types - mcp::variant v; - - BaseClass base; - DerivedClass derived; - - v = &base; - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get()->value(), 1); - - v = &derived; - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get()->value(), 2); - - // Store by value using smart pointers - mcp::variant, std::unique_ptr> v2; - - v2 = mcp::make_unique(); - EXPECT_EQ(v2.get>()->value(), 1); - - v2 = mcp::make_unique(); - EXPECT_EQ(v2.get>()->value(), 2); + // Store pointers to polymorphic types + mcp::variant v; + + BaseClass base; + DerivedClass derived; + + v = &base; + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get()->value(), 1); + + v = &derived; + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get()->value(), 2); + + // Store by value using smart pointers + mcp::variant, std::unique_ptr> v2; + + v2 = mcp::make_unique(); + EXPECT_EQ(v2.get>()->value(), 1); + + v2 = mcp::make_unique(); + EXPECT_EQ(v2.get>()->value(), 2); } // Test SFINAE and overload resolution TEST_F(AdvancedVariantTest, OverloadResolution) { - // Test that variant correctly selects the best overload - mcp::variant, int> v; - - // Should select std::string for const char* - v = "hello"; - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get(), "hello"); - - // Should select int for numeric literals - v = 42; - EXPECT_EQ(v.index(), 2u); - EXPECT_EQ(v.get(), 42); - - // Should handle explicit construction - v = std::vector{'a', 'b', 'c'}; - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get>().size(), 3u); + // Test that variant correctly selects the best overload + mcp::variant, int> v; + + // Should select std::string for const char* + v = "hello"; + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get(), "hello"); + + // Should select int for numeric literals + v = 42; + EXPECT_EQ(v.index(), 2u); + EXPECT_EQ(v.get(), 42); + + // Should handle explicit construction + v = std::vector{'a', 'b', 'c'}; + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get>().size(), 3u); } // Test with function types and function pointers TEST_F(AdvancedVariantTest, FunctionTypes) { - using FuncPtr = int(*)(int); - - auto square = [](int x) -> int { return x * x; }; - auto cube = [](int x) -> int { return x * x * x; }; - - // Function pointer variant - mcp::variant> v; - - v = +square; // Convert lambda to function pointer - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get()(5), 25); - - v = std::function(cube); - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get>()(3), 27); + using FuncPtr = int (*)(int); + + auto square = [](int x) -> int { return x * x; }; + auto cube = [](int x) -> int { return x * x * x; }; + + // Function pointer variant + mcp::variant> v; + + v = +square; // Convert lambda to function pointer + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get()(5), 25); + + v = std::function(cube); + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get>()(3), 27); } // Test variant with array types TEST_F(AdvancedVariantTest, ArrayTypes) { - // C-style arrays decay to pointers, so we use std::array - mcp::variant, std::array> v; - - v = std::array{{1, 2, 3}}; - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ((v.get>()[1]), 2); - - v = std::array{{1.1, 2.2, 3.3}}; - EXPECT_EQ(v.index(), 1u); - EXPECT_DOUBLE_EQ((v.get>()[2]), 3.3); + // C-style arrays decay to pointers, so we use std::array + mcp::variant, std::array> v; + + v = std::array{{1, 2, 3}}; + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ((v.get>()[1]), 2); + + v = std::array{{1.1, 2.2, 3.3}}; + EXPECT_EQ(v.index(), 1u); + EXPECT_DOUBLE_EQ((v.get>()[2]), 3.3); } // Test with nested variants TEST_F(AdvancedVariantTest, NestedVariants) { - using Inner = mcp::variant; - using Outer = mcp::variant>; - - Outer v; - - // Single nested variant - v = Inner(42); - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get().get(), 42); - - // Vector of variants - std::vector vec; - vec.push_back(Inner(10)); - vec.push_back(Inner("test")); - vec.push_back(Inner(20)); - - v = vec; - EXPECT_EQ(v.index(), 2u); - auto& stored_vec = v.get>(); - EXPECT_EQ(stored_vec.size(), 3u); - EXPECT_EQ(stored_vec[0].get(), 10); - EXPECT_EQ(stored_vec[1].get(), "test"); - EXPECT_EQ(stored_vec[2].get(), 20); + using Inner = mcp::variant; + using Outer = mcp::variant>; + + Outer v; + + // Single nested variant + v = Inner(42); + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get().get(), 42); + + // Vector of variants + std::vector vec; + vec.push_back(Inner(10)); + vec.push_back(Inner("test")); + vec.push_back(Inner(20)); + + v = vec; + EXPECT_EQ(v.index(), 2u); + auto& stored_vec = v.get>(); + EXPECT_EQ(stored_vec.size(), 3u); + EXPECT_EQ(stored_vec[0].get(), 10); + EXPECT_EQ(stored_vec[1].get(), "test"); + EXPECT_EQ(stored_vec[2].get(), 20); } // Test with reference wrapper types TEST_F(AdvancedVariantTest, ReferenceWrappers) { - int x = 42; - std::string s = "hello"; - - // Note: std::reference_wrapper requires initialization, so we can't default construct - mcp::variant, std::reference_wrapper> v(0); - - v = std::ref(x); - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get>().get(), 42); - - // Modify through reference - v.get>().get() = 100; - EXPECT_EQ(x, 100); - - v = std::ref(s); - EXPECT_EQ(v.index(), 2u); - EXPECT_EQ(v.get>().get(), "hello"); + int x = 42; + std::string s = "hello"; + + // Note: std::reference_wrapper requires initialization, so we can't default + // construct + mcp::variant, + std::reference_wrapper> + v(0); + + v = std::ref(x); + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get>().get(), 42); + + // Modify through reference + v.get>().get() = 100; + EXPECT_EQ(x, 100); + + v = std::ref(s); + EXPECT_EQ(v.index(), 2u); + EXPECT_EQ(v.get>().get(), "hello"); } // Test visitor with generic lambdas (C++11 style) TEST_F(AdvancedVariantTest, GenericVisitor) { - mcp::variant v(3.14); - - // In C++11, we need explicit overloads instead of template member functions - struct ToStringVisitor { - std::string operator()(int value) const { - std::stringstream ss; - ss << value; - return ss.str(); - } - std::string operator()(double value) const { - std::stringstream ss; - ss << value; - return ss.str(); - } - std::string operator()(const std::string& value) const { - return value; - } - }; - - std::string result = mcp::visit(ToStringVisitor{}, v); - EXPECT_EQ(result.substr(0, 4), "3.14"); - - v = 42; - result = mcp::visit(ToStringVisitor{}, v); - EXPECT_EQ(result, "42"); - - v = std::string("test"); - result = mcp::visit(ToStringVisitor{}, v); - EXPECT_EQ(result, "test"); + mcp::variant v(3.14); + + // In C++11, we need explicit overloads instead of template member functions + struct ToStringVisitor { + std::string operator()(int value) const { + std::stringstream ss; + ss << value; + return ss.str(); + } + std::string operator()(double value) const { + std::stringstream ss; + ss << value; + return ss.str(); + } + std::string operator()(const std::string& value) const { return value; } + }; + + std::string result = mcp::visit(ToStringVisitor{}, v); + EXPECT_EQ(result.substr(0, 4), "3.14"); + + v = 42; + result = mcp::visit(ToStringVisitor{}, v); + EXPECT_EQ(result, "42"); + + v = std::string("test"); + result = mcp::visit(ToStringVisitor{}, v); + EXPECT_EQ(result, "test"); } // Test with volatile and const volatile types TEST_F(AdvancedVariantTest, CVQualifiedTypes) { - // Note: storing cv-qualified types directly is unusual - // More common is cv-qualified access to variant - mcp::variant v(42); - - const mcp::variant& cv = v; - EXPECT_EQ(cv.get(), 42); - - // Test that const visitor works - struct ConstVisitor { - int operator()(const int& i) const { return i * 2; } - int operator()(const double& d) const { return static_cast(d * 2); } - }; - - EXPECT_EQ(mcp::visit(ConstVisitor{}, cv), 84); + // Note: storing cv-qualified types directly is unusual + // More common is cv-qualified access to variant + mcp::variant v(42); + + const mcp::variant& cv = v; + EXPECT_EQ(cv.get(), 42); + + // Test that const visitor works + struct ConstVisitor { + int operator()(const int& i) const { return i * 2; } + int operator()(const double& d) const { return static_cast(d * 2); } + }; + + EXPECT_EQ(mcp::visit(ConstVisitor{}, cv), 84); } // Test exception specifications TEST_F(AdvancedVariantTest, NoexceptOperations) { - using V = mcp::variant; - - // Move construction should be noexcept for these types - static_assert(noexcept(V(std::declval())), - "Move constructor should be noexcept"); - - // Index and holds_alternative should be noexcept - V v(42); - static_assert(noexcept(v.index()), "index() should be noexcept"); - static_assert(noexcept(v.holds_alternative()), - "holds_alternative should be noexcept"); + using V = mcp::variant; + + // Move construction should be noexcept for these types + static_assert(noexcept(V(std::declval())), + "Move constructor should be noexcept"); + + // Index and holds_alternative should be noexcept + V v(42); + static_assert(noexcept(v.index()), "index() should be noexcept"); + static_assert(noexcept(v.holds_alternative()), + "holds_alternative should be noexcept"); } // Test with incomplete types (through pointers) TEST_F(AdvancedVariantTest, IncompleteTypes) { - struct Incomplete; // Forward declaration - - // Can store pointers to incomplete types - mcp::variant v; - v = static_cast(nullptr); - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get(), nullptr); - - v = 42; - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get(), 42); + struct Incomplete; // Forward declaration + + // Can store pointers to incomplete types + mcp::variant v; + v = static_cast(nullptr); + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get(), nullptr); + + v = 42; + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get(), 42); } // Test variant with empty types TEST_F(AdvancedVariantTest, EmptyTypes) { - struct Empty {}; - struct AlsoEmpty {}; - - mcp::variant v; - - // Default constructs to first alternative - EXPECT_EQ(v.index(), 0u); - - v = AlsoEmpty{}; - EXPECT_EQ(v.index(), 1u); - - v = 42; - EXPECT_EQ(v.index(), 2u); - EXPECT_EQ(v.get(), 42); + struct Empty {}; + struct AlsoEmpty {}; + + mcp::variant v; + + // Default constructs to first alternative + EXPECT_EQ(v.index(), 0u); + + v = AlsoEmpty{}; + EXPECT_EQ(v.index(), 1u); + + v = 42; + EXPECT_EQ(v.index(), 2u); + EXPECT_EQ(v.get(), 42); } // Test with types that have unusual alignment requirements TEST_F(AdvancedVariantTest, AlignmentRequirements) { - struct alignas(32) HighlyAligned { - char data[32]; - }; - - struct alignas(64) VeryHighlyAligned { - char data[64]; - }; - - mcp::variant v; - - // Check that variant respects alignment - void* addr = &v; - EXPECT_EQ(reinterpret_cast(addr) % alignof(decltype(v)), 0u); - - v = HighlyAligned{}; - EXPECT_EQ(v.index(), 1u); - - v = VeryHighlyAligned{}; - EXPECT_EQ(v.index(), 2u); + struct alignas(32) HighlyAligned { + char data[32]; + }; + + struct alignas(64) VeryHighlyAligned { + char data[64]; + }; + + mcp::variant v; + + // Check that variant respects alignment + void* addr = &v; + EXPECT_EQ(reinterpret_cast(addr) % alignof(decltype(v)), 0u); + + v = HighlyAligned{}; + EXPECT_EQ(v.index(), 1u); + + v = VeryHighlyAligned{}; + EXPECT_EQ(v.index(), 2u); } // Test with recursive data structures TEST_F(AdvancedVariantTest, RecursiveDataStructure) { - struct Node; - using NodePtr = std::shared_ptr; - - struct Node { - mcp::variant data; - - explicit Node(int val) : data(val) {} - explicit Node(NodePtr child) : data(child) {} - }; - - // Create a tree-like structure - auto leaf1 = std::make_shared(10); - auto leaf2 = std::make_shared(20); - auto branch = std::make_shared(leaf1); - auto root = std::make_shared(branch); - - // Navigate the structure - EXPECT_TRUE(root->data.holds_alternative()); - auto& child = root->data.get(); - EXPECT_TRUE(child->data.holds_alternative()); - auto& grandchild = child->data.get(); - EXPECT_TRUE(grandchild->data.holds_alternative()); - EXPECT_EQ(grandchild->data.get(), 10); + struct Node; + using NodePtr = std::shared_ptr; + + struct Node { + mcp::variant data; + + explicit Node(int val) : data(val) {} + explicit Node(NodePtr child) : data(child) {} + }; + + // Create a tree-like structure + auto leaf1 = std::make_shared(10); + auto leaf2 = std::make_shared(20); + auto branch = std::make_shared(leaf1); + auto root = std::make_shared(branch); + + // Navigate the structure + EXPECT_TRUE(root->data.holds_alternative()); + auto& child = root->data.get(); + EXPECT_TRUE(child->data.holds_alternative()); + auto& grandchild = child->data.get(); + EXPECT_TRUE(grandchild->data.holds_alternative()); + EXPECT_EQ(grandchild->data.get(), 10); } // Test variant destruction order TEST_F(AdvancedVariantTest, DestructionOrder) { - static int destruction_count = 0; - - struct CountingDestructor { - ~CountingDestructor() { - destruction_count++; - } - }; - - destruction_count = 0; - - { - mcp::variant v1; - mcp::variant v2; - mcp::variant v3; - - EXPECT_EQ(destruction_count, 0); // No destructions yet - - v2 = 42; // Should destroy the CountingDestructor in v2 - EXPECT_EQ(destruction_count, 1); - - // v3 and v1 still contain CountingDestructor - } - - // After the scope, v3 and v1 should be destroyed - EXPECT_EQ(destruction_count, 3); // 1 from v2 assignment + 2 from scope exit + static int destruction_count = 0; + + struct CountingDestructor { + ~CountingDestructor() { destruction_count++; } + }; + + destruction_count = 0; + + { + mcp::variant v1; + mcp::variant v2; + mcp::variant v3; + + EXPECT_EQ(destruction_count, 0); // No destructions yet + + v2 = 42; // Should destroy the CountingDestructor in v2 + EXPECT_EQ(destruction_count, 1); + + // v3 and v1 still contain CountingDestructor + } + + // After the scope, v3 and v1 should be destroyed + EXPECT_EQ(destruction_count, 3); // 1 from v2 assignment + 2 from scope exit } // Test with types that overload operator& TEST_F(AdvancedVariantTest, OverloadedAddressOf) { - struct EvilType { - int value; - explicit EvilType(int v) : value(v) {} - - // Overload operator& to return something unexpected - int operator&() const { return 666; } - }; - - mcp::variant v(EvilType(42)); - - // Should still work correctly despite overloaded operator& - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get().value, 42); - - // get_if should work correctly - auto* p = v.get_if(); - ASSERT_NE(p, nullptr); - EXPECT_EQ(p->value, 42); + struct EvilType { + int value; + explicit EvilType(int v) : value(v) {} + + // Overload operator& to return something unexpected + int operator&() const { return 666; } + }; + + mcp::variant v(EvilType(42)); + + // Should still work correctly despite overloaded operator& + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get().value, 42); + + // get_if should work correctly + auto* p = v.get_if(); + ASSERT_NE(p, nullptr); + EXPECT_EQ(p->value, 42); } // Test variant with union-like types TEST_F(AdvancedVariantTest, UnionLikeTypes) { - union SimpleUnion { - int i; - float f; - SimpleUnion() : i(0) {} - }; - - mcp::variant v; - - SimpleUnion u; - u.f = 3.14f; - v = u; - - EXPECT_EQ(v.index(), 0u); - // Note: accessing u.f after assignment is implementation-defined - - v = 2.718; - EXPECT_EQ(v.index(), 1u); - EXPECT_DOUBLE_EQ(v.get(), 2.718); + union SimpleUnion { + int i; + float f; + SimpleUnion() : i(0) {} + }; + + mcp::variant v; + + SimpleUnion u; + u.f = 3.14f; + v = u; + + EXPECT_EQ(v.index(), 0u); + // Note: accessing u.f after assignment is implementation-defined + + v = 2.718; + EXPECT_EQ(v.index(), 1u); + EXPECT_DOUBLE_EQ(v.get(), 2.718); } \ No newline at end of file diff --git a/tests/test_variant_extensive.cpp b/tests/test_variant_extensive.cpp index 50a6e768..5707009d 100644 --- a/tests/test_variant_extensive.cpp +++ b/tests/test_variant_extensive.cpp @@ -1,581 +1,599 @@ -#include -#include "mcp/variant.h" -#include "mcp/memory_utils.h" -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include + +#include + +#include "mcp/memory_utils.h" +#include "mcp/variant.h" // Test suite inspired by C++17 std::variant test patterns // Adapted for C++11 compatibility // Helper types for testing struct NonDefaultConstructible { - NonDefaultConstructible() = delete; - explicit NonDefaultConstructible(int v) : value(v) {} - int value; + NonDefaultConstructible() = delete; + explicit NonDefaultConstructible(int v) : value(v) {} + int value; }; struct NonCopyable { - NonCopyable() = default; - NonCopyable(const NonCopyable&) = delete; - NonCopyable& operator=(const NonCopyable&) = delete; - NonCopyable(NonCopyable&&) = default; - NonCopyable& operator=(NonCopyable&&) = default; + NonCopyable() = default; + NonCopyable(const NonCopyable&) = delete; + NonCopyable& operator=(const NonCopyable&) = delete; + NonCopyable(NonCopyable&&) = default; + NonCopyable& operator=(NonCopyable&&) = default; }; struct NonMovable { - NonMovable() = default; - NonMovable(const NonMovable&) = default; - NonMovable& operator=(const NonMovable&) = default; - NonMovable(NonMovable&&) = delete; - NonMovable& operator=(NonMovable&&) = delete; + NonMovable() = default; + NonMovable(const NonMovable&) = default; + NonMovable& operator=(const NonMovable&) = default; + NonMovable(NonMovable&&) = delete; + NonMovable& operator=(NonMovable&&) = delete; }; struct ConvertibleToInt { - operator int() const { return 42; } + operator int() const { return 42; } }; struct ConvertibleFromInt { - ConvertibleFromInt(int) {} + ConvertibleFromInt(int) {} }; struct Counter { - static int alive; - Counter() { ++alive; } - Counter(const Counter&) { ++alive; } - Counter(Counter&&) { ++alive; } - ~Counter() { --alive; } + static int alive; + Counter() { ++alive; } + Counter(const Counter&) { ++alive; } + Counter(Counter&&) { ++alive; } + ~Counter() { --alive; } }; int Counter::alive = 0; // SFINAE test helpers -template +template struct is_default_constructible : std::false_type {}; -template -struct is_default_constructible::value - >::type> : std::true_type {}; +template +struct is_default_constructible< + T, + typename std::enable_if::value>::type> + : std::true_type {}; // Test fixture class VariantExtensiveTest : public ::testing::Test { -protected: - void SetUp() override { - Counter::alive = 0; - } - - void TearDown() override { - EXPECT_EQ(Counter::alive, 0); - } + protected: + void SetUp() override { Counter::alive = 0; } + + void TearDown() override { EXPECT_EQ(Counter::alive, 0); } }; // Construction tests TEST_F(VariantExtensiveTest, DefaultConstructionFirstAlternative) { - // Default constructs the first alternative - { - mcp::variant v; - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get(), 0); - } - - { - mcp::variant v; - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get(), ""); - } + // Default constructs the first alternative + { + mcp::variant v; + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get(), 0); + } + + { + mcp::variant v; + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get(), ""); + } } TEST_F(VariantExtensiveTest, DirectInitialization) { - // Direct initialization selects the best match - { - mcp::variant v(42); - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get(), 42); - } - - { - mcp::variant v("hello"); - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get(), "hello"); - } - - { - mcp::variant v(std::string("world")); - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get(), "world"); - } + // Direct initialization selects the best match + { + mcp::variant v(42); + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get(), 42); + } + + { + mcp::variant v("hello"); + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get(), "hello"); + } + + { + mcp::variant v(std::string("world")); + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get(), "world"); + } } TEST_F(VariantExtensiveTest, ConvertingConstruction) { - // Test implicit conversions - { - mcp::variant v("test"); // const char* -> std::string - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get(), "test"); - } - - { - // Direct double construction to avoid ambiguity - mcp::variant v(3.14); // double literal - EXPECT_EQ(v.index(), 1u); - EXPECT_DOUBLE_EQ(v.get(), 3.14); - } - - { - ConvertibleToInt cti; - mcp::variant v(cti); - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get(), 42); - } + // Test implicit conversions + { + mcp::variant v("test"); // const char* -> std::string + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get(), "test"); + } + + { + // Direct double construction to avoid ambiguity + mcp::variant v(3.14); // double literal + EXPECT_EQ(v.index(), 1u); + EXPECT_DOUBLE_EQ(v.get(), 3.14); + } + + { + ConvertibleToInt cti; + mcp::variant v(cti); + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get(), 42); + } } // Copy/Move tests TEST_F(VariantExtensiveTest, CopyConstructionPreservesValue) { - { - mcp::variant v1(42); - mcp::variant v2(v1); - - EXPECT_EQ(v1.index(), v2.index()); - EXPECT_EQ(v1.get(), v2.get()); - } - - { - mcp::variant v1("test"); - mcp::variant v2(v1); - - EXPECT_EQ(v1.index(), v2.index()); - EXPECT_EQ(v1.get(), v2.get()); - } + { + mcp::variant v1(42); + mcp::variant v2(v1); + + EXPECT_EQ(v1.index(), v2.index()); + EXPECT_EQ(v1.get(), v2.get()); + } + + { + mcp::variant v1("test"); + mcp::variant v2(v1); + + EXPECT_EQ(v1.index(), v2.index()); + EXPECT_EQ(v1.get(), v2.get()); + } } TEST_F(VariantExtensiveTest, MoveConstructionTransfersOwnership) { - { - std::vector data = {1, 2, 3, 4, 5}; - mcp::variant> v1(data); - mcp::variant> v2(std::move(v1)); - - EXPECT_EQ(v2.index(), 1u); - EXPECT_EQ(v2.get>(), data); - } - - { - auto ptr = mcp::make_unique(42); - mcp::variant> v1(std::move(ptr)); - mcp::variant> v2(std::move(v1)); - - EXPECT_EQ(v2.index(), 1u); - ASSERT_NE(v2.get>().get(), nullptr); - EXPECT_EQ(*v2.get>(), 42); - } + { + std::vector data = {1, 2, 3, 4, 5}; + mcp::variant> v1(data); + mcp::variant> v2(std::move(v1)); + + EXPECT_EQ(v2.index(), 1u); + EXPECT_EQ(v2.get>(), data); + } + + { + auto ptr = mcp::make_unique(42); + mcp::variant> v1(std::move(ptr)); + mcp::variant> v2(std::move(v1)); + + EXPECT_EQ(v2.index(), 1u); + ASSERT_NE(v2.get>().get(), nullptr); + EXPECT_EQ(*v2.get>(), 42); + } } // Assignment tests TEST_F(VariantExtensiveTest, CopyAssignmentSameIndex) { - mcp::variant v1(42); - mcp::variant v2(99); - - v2 = v1; - EXPECT_EQ(v2.index(), 0u); - EXPECT_EQ(v2.get(), 42); + mcp::variant v1(42); + mcp::variant v2(99); + + v2 = v1; + EXPECT_EQ(v2.index(), 0u); + EXPECT_EQ(v2.get(), 42); } TEST_F(VariantExtensiveTest, CopyAssignmentDifferentIndex) { - mcp::variant v1(42); - mcp::variant v2("old"); - - v2 = v1; - EXPECT_EQ(v2.index(), 0u); - EXPECT_EQ(v2.get(), 42); + mcp::variant v1(42); + mcp::variant v2("old"); + + v2 = v1; + EXPECT_EQ(v2.index(), 0u); + EXPECT_EQ(v2.get(), 42); } TEST_F(VariantExtensiveTest, MoveAssignment) { - mcp::variant> v1(std::vector{1, 2, 3}); - mcp::variant> v2(42); - - v2 = std::move(v1); - EXPECT_EQ(v2.index(), 1u); - EXPECT_EQ(v2.get>().size(), 3u); + mcp::variant> v1(std::vector{1, 2, 3}); + mcp::variant> v2(42); + + v2 = std::move(v1); + EXPECT_EQ(v2.index(), 1u); + EXPECT_EQ(v2.get>().size(), 3u); } TEST_F(VariantExtensiveTest, ConvertingAssignment) { - mcp::variant v(42); - - v = "hello"; // const char* -> std::string - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get(), "hello"); - - v = 99; - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get(), 99); + mcp::variant v(42); + + v = "hello"; // const char* -> std::string + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get(), "hello"); + + v = 99; + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get(), 99); } // Visitor tests with various return types TEST_F(VariantExtensiveTest, VisitorVoidReturn) { - mcp::variant v(3.14); - - bool visited = false; - struct VoidVisitor { - bool& flag; - void operator()(int) { flag = false; } - void operator()(double) { flag = true; } - void operator()(const std::string&) { flag = false; } - }; - - mcp::visit(VoidVisitor{visited}, v); - EXPECT_TRUE(visited); + mcp::variant v(3.14); + + bool visited = false; + struct VoidVisitor { + bool& flag; + void operator()(int) { flag = false; } + void operator()(double) { flag = true; } + void operator()(const std::string&) { flag = false; } + }; + + mcp::visit(VoidVisitor{visited}, v); + EXPECT_TRUE(visited); } TEST_F(VariantExtensiveTest, VisitorNonVoidReturn) { - mcp::variant v("hello"); - - struct SizeVisitor { - std::size_t operator()(int) const { return sizeof(int); } - std::size_t operator()(double) const { return sizeof(double); } - std::size_t operator()(const std::string& s) const { return s.size(); } - }; - - auto size = mcp::visit(SizeVisitor{}, v); - EXPECT_EQ(size, 5u); + mcp::variant v("hello"); + + struct SizeVisitor { + std::size_t operator()(int) const { return sizeof(int); } + std::size_t operator()(double) const { return sizeof(double); } + std::size_t operator()(const std::string& s) const { return s.size(); } + }; + + auto size = mcp::visit(SizeVisitor{}, v); + EXPECT_EQ(size, 5u); } TEST_F(VariantExtensiveTest, VisitorWithLambdas) { - mcp::variant v(42); - - auto visitor = mcp::make_overload( - [](int i) { return i * 2; }, - [](double d) { return static_cast(d); } - ); - - EXPECT_EQ(mcp::visit(visitor, v), 84); + mcp::variant v(42); + + auto visitor = + mcp::make_overload([](int i) { return i * 2; }, + [](double d) { return static_cast(d); }); + + EXPECT_EQ(mcp::visit(visitor, v), 84); } TEST_F(VariantExtensiveTest, VisitorModifyingValue) { - mcp::variant v(42); - - struct ModifyingVisitor { - void operator()(int& i) { i *= 2; } - void operator()(std::string& s) { s += s; } - }; - - mcp::visit(ModifyingVisitor{}, v); - EXPECT_EQ(v.get(), 84); - - v = std::string("test"); - mcp::visit(ModifyingVisitor{}, v); - EXPECT_EQ(v.get(), "testtest"); + mcp::variant v(42); + + struct ModifyingVisitor { + void operator()(int& i) { i *= 2; } + void operator()(std::string& s) { s += s; } + }; + + mcp::visit(ModifyingVisitor{}, v); + EXPECT_EQ(v.get(), 84); + + v = std::string("test"); + mcp::visit(ModifyingVisitor{}, v); + EXPECT_EQ(v.get(), "testtest"); } // Type traits tests TEST_F(VariantExtensiveTest, TypeIndexing) { - EXPECT_EQ((mcp::type_index::value), 0u); - EXPECT_EQ((mcp::type_index::value), 1u); - EXPECT_EQ((mcp::type_index::value), 2u); + EXPECT_EQ((mcp::type_index::value), 0u); + EXPECT_EQ((mcp::type_index::value), 1u); + EXPECT_EQ((mcp::type_index::value), + 2u); } TEST_F(VariantExtensiveTest, ContainsType) { - EXPECT_TRUE((mcp::contains_type::value)); - EXPECT_TRUE((mcp::contains_type::value)); - EXPECT_TRUE((mcp::contains_type::value)); - EXPECT_FALSE((mcp::contains_type::value)); - EXPECT_FALSE((mcp::contains_type::value)); + EXPECT_TRUE((mcp::contains_type::value)); + EXPECT_TRUE((mcp::contains_type::value)); + EXPECT_TRUE( + (mcp::contains_type::value)); + EXPECT_FALSE((mcp::contains_type::value)); + EXPECT_FALSE((mcp::contains_type::value)); } // Edge case tests TEST_F(VariantExtensiveTest, EmptyVariantBehavior) { - // Single alternative variant - mcp::variant v; - EXPECT_EQ(v.index(), 0u); - v = 42; - EXPECT_EQ(v.get(), 42); + // Single alternative variant + mcp::variant v; + EXPECT_EQ(v.index(), 0u); + v = 42; + EXPECT_EQ(v.get(), 42); } TEST_F(VariantExtensiveTest, LargeVariant) { - // Test with many alternatives - using LargeVariant = mcp::variant< - char, short, int, long, long long, - unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long, - float, double, long double, - std::string, std::vector, std::unique_ptr - >; - - LargeVariant v1('x'); - EXPECT_EQ(v1.index(), 0u); - EXPECT_EQ(v1.get(), 'x'); - - v1 = 3.14; - EXPECT_EQ(v1.index(), 11u); - EXPECT_DOUBLE_EQ(v1.get(), 3.14); - - v1 = std::string("test"); - EXPECT_EQ(v1.index(), 13u); - EXPECT_EQ(v1.get(), "test"); + // Test with many alternatives + using LargeVariant = + mcp::variant, std::unique_ptr>; + + LargeVariant v1('x'); + EXPECT_EQ(v1.index(), 0u); + EXPECT_EQ(v1.get(), 'x'); + + v1 = 3.14; + EXPECT_EQ(v1.index(), 11u); + EXPECT_DOUBLE_EQ(v1.get(), 3.14); + + v1 = std::string("test"); + EXPECT_EQ(v1.index(), 13u); + EXPECT_EQ(v1.get(), "test"); } TEST_F(VariantExtensiveTest, RecursiveVariantSupport) { - // Test recursive variant through pointer/reference - struct Tree { - int value; - std::vector> children; - - Tree(int v) : value(v) {} - }; - - Tree root(1); - Tree child1(2); - Tree child2(3); - - root.children.push_back(&child1); - root.children.push_back(&child2); - root.children.push_back(42); - - EXPECT_EQ(root.children.size(), 3u); - EXPECT_TRUE(root.children[0].holds_alternative()); - EXPECT_TRUE(root.children[1].holds_alternative()); - EXPECT_TRUE(root.children[2].holds_alternative()); + // Test recursive variant through pointer/reference + struct Tree { + int value; + std::vector> children; + + Tree(int v) : value(v) {} + }; + + Tree root(1); + Tree child1(2); + Tree child2(3); + + root.children.push_back(&child1); + root.children.push_back(&child2); + root.children.push_back(42); + + EXPECT_EQ(root.children.size(), 3u); + EXPECT_TRUE(root.children[0].holds_alternative()); + EXPECT_TRUE(root.children[1].holds_alternative()); + EXPECT_TRUE(root.children[2].holds_alternative()); } // Exception safety tests TEST_F(VariantExtensiveTest, ExceptionNeutralConstruction) { - struct ThrowOnCopy { - ThrowOnCopy() = default; - ThrowOnCopy(const ThrowOnCopy&) { throw std::runtime_error("copy"); } - ThrowOnCopy(ThrowOnCopy&&) noexcept = default; - }; - - // Move construction should not throw - ThrowOnCopy toc; - bool threw = false; - try { - mcp::variant v(std::move(toc)); - } catch (...) { - threw = true; - } - EXPECT_FALSE(threw); + struct ThrowOnCopy { + ThrowOnCopy() = default; + ThrowOnCopy(const ThrowOnCopy&) { throw std::runtime_error("copy"); } + ThrowOnCopy(ThrowOnCopy&&) noexcept = default; + }; + + // Move construction should not throw + ThrowOnCopy toc; + bool threw = false; + try { + mcp::variant v(std::move(toc)); + } catch (...) { + threw = true; + } + EXPECT_FALSE(threw); } TEST_F(VariantExtensiveTest, StrongExceptionGuarantee) { - // Define static member outside of local struct - static int construct_count = 0; - - struct ThrowOnConstruct { - ThrowOnConstruct() { - if (++construct_count > 1) { - throw std::runtime_error("construct"); - } - } - }; - construct_count = 0; - - mcp::variant v(42); - construct_count = 1; // Next construction will throw - - // Assignment that throws should leave v unchanged - try { - v = ThrowOnConstruct(); - FAIL() << "Expected exception"; - } catch (...) { - EXPECT_EQ(v.index(), 0u); - EXPECT_EQ(v.get(), 42); + // Define static member outside of local struct + static int construct_count = 0; + + struct ThrowOnConstruct { + ThrowOnConstruct() { + if (++construct_count > 1) { + throw std::runtime_error("construct"); + } } + }; + construct_count = 0; + + mcp::variant v(42); + construct_count = 1; // Next construction will throw + + // Assignment that throws should leave v unchanged + try { + v = ThrowOnConstruct(); + FAIL() << "Expected exception"; + } catch (...) { + EXPECT_EQ(v.index(), 0u); + EXPECT_EQ(v.get(), 42); + } } // get_if tests TEST_F(VariantExtensiveTest, GetIfCorrectType) { - mcp::variant v(42); - - auto* pi = v.get_if(); - ASSERT_NE(pi, nullptr); - EXPECT_EQ(*pi, 42); - - auto* pd = v.get_if(); - EXPECT_EQ(pd, nullptr); - - auto* ps = v.get_if(); - EXPECT_EQ(ps, nullptr); + mcp::variant v(42); + + auto* pi = v.get_if(); + ASSERT_NE(pi, nullptr); + EXPECT_EQ(*pi, 42); + + auto* pd = v.get_if(); + EXPECT_EQ(pd, nullptr); + + auto* ps = v.get_if(); + EXPECT_EQ(ps, nullptr); } TEST_F(VariantExtensiveTest, GetIfConst) { - const mcp::variant v(3.14); - - auto* pd = v.get_if(); - ASSERT_NE(pd, nullptr); - EXPECT_DOUBLE_EQ(*pd, 3.14); - - // Ensure const correctness - static_assert(std::is_same::value, - "get_if on const variant should return const pointer"); + const mcp::variant v(3.14); + + auto* pd = v.get_if(); + ASSERT_NE(pd, nullptr); + EXPECT_DOUBLE_EQ(*pd, 3.14); + + // Ensure const correctness + static_assert(std::is_same::value, + "get_if on const variant should return const pointer"); } // Lifetime tests TEST_F(VariantExtensiveTest, ObjectLifetime) { - { - mcp::variant v; - EXPECT_EQ(Counter::alive, 1); // Default constructed Counter - - v = 42; - EXPECT_EQ(Counter::alive, 0); // Counter destroyed - - v = Counter(); - EXPECT_EQ(Counter::alive, 1); // New Counter created - } - EXPECT_EQ(Counter::alive, 0); // All destroyed + { + mcp::variant v; + EXPECT_EQ(Counter::alive, 1); // Default constructed Counter + + v = 42; + EXPECT_EQ(Counter::alive, 0); // Counter destroyed + + v = Counter(); + EXPECT_EQ(Counter::alive, 1); // New Counter created + } + EXPECT_EQ(Counter::alive, 0); // All destroyed } TEST_F(VariantExtensiveTest, MoveOnlyTypes) { - mcp::variant> v; - - v = mcp::make_unique(42); - EXPECT_EQ(v.index(), 1u); - ASSERT_NE(v.get>().get(), nullptr); - EXPECT_EQ(*v.get>(), 42); - - // Move to another variant - mcp::variant> v2(std::move(v)); - EXPECT_EQ(v2.index(), 1u); - ASSERT_NE(v2.get>().get(), nullptr); - EXPECT_EQ(*v2.get>(), 42); + mcp::variant> v; + + v = mcp::make_unique(42); + EXPECT_EQ(v.index(), 1u); + ASSERT_NE(v.get>().get(), nullptr); + EXPECT_EQ(*v.get>(), 42); + + // Move to another variant + mcp::variant> v2(std::move(v)); + EXPECT_EQ(v2.index(), 1u); + ASSERT_NE(v2.get>().get(), nullptr); + EXPECT_EQ(*v2.get>(), 42); } // Constexpr-like tests (compile-time behavior) TEST_F(VariantExtensiveTest, CompileTimeTypeSelection) { - // Test that variant correctly selects types at compile time - static_assert(std::is_same< - typename mcp::type_at_index<0, int, double, std::string>::type, - int - >::value, "type_at_index<0> should be int"); - - static_assert(std::is_same< - typename mcp::type_at_index<1, int, double, std::string>::type, - double - >::value, "type_at_index<1> should be double"); - - static_assert(std::is_same< - typename mcp::type_at_index<2, int, double, std::string>::type, - std::string - >::value, "type_at_index<2> should be std::string"); + // Test that variant correctly selects types at compile time + static_assert( + std::is_same< + typename mcp::type_at_index<0, int, double, std::string>::type, + int>::value, + "type_at_index<0> should be int"); + + static_assert( + std::is_same< + typename mcp::type_at_index<1, int, double, std::string>::type, + double>::value, + "type_at_index<1> should be double"); + + static_assert( + std::is_same< + typename mcp::type_at_index<2, int, double, std::string>::type, + std::string>::value, + "type_at_index<2> should be std::string"); } // Performance and stress tests TEST_F(VariantExtensiveTest, LargeObjectStorage) { - struct LargeObject { - char data[1024]; - LargeObject() { std::fill(data, data + 1024, 'x'); } - }; - - mcp::variant v; - v = LargeObject(); - - EXPECT_EQ(v.index(), 1u); - EXPECT_EQ(v.get().data[0], 'x'); - EXPECT_EQ(v.get().data[1023], 'x'); + struct LargeObject { + char data[1024]; + LargeObject() { std::fill(data, data + 1024, 'x'); } + }; + + mcp::variant v; + v = LargeObject(); + + EXPECT_EQ(v.index(), 1u); + EXPECT_EQ(v.get().data[0], 'x'); + EXPECT_EQ(v.get().data[1023], 'x'); } TEST_F(VariantExtensiveTest, ManyAlternativesPerformance) { - const int iterations = 1000; - - using ManyTypes = mcp::variant< - char, short, int, long, - float, double, - std::string, std::vector - >; - - std::vector variants; - variants.reserve(iterations); - - // Create many variants of different types - for (int i = 0; i < iterations; ++i) { - switch (i % 8) { - case 0: variants.emplace_back(static_cast(i)); break; - case 1: variants.emplace_back(static_cast(i)); break; - case 2: variants.emplace_back(i); break; - case 3: variants.emplace_back(static_cast(i)); break; - case 4: variants.emplace_back(static_cast(i)); break; - case 5: variants.emplace_back(static_cast(i)); break; - case 6: variants.emplace_back(std::to_string(i)); break; - case 7: variants.emplace_back(std::vector{i, i+1}); break; - } + const int iterations = 1000; + + using ManyTypes = mcp::variant>; + + std::vector variants; + variants.reserve(iterations); + + // Create many variants of different types + for (int i = 0; i < iterations; ++i) { + switch (i % 8) { + case 0: + variants.emplace_back(static_cast(i)); + break; + case 1: + variants.emplace_back(static_cast(i)); + break; + case 2: + variants.emplace_back(i); + break; + case 3: + variants.emplace_back(static_cast(i)); + break; + case 4: + variants.emplace_back(static_cast(i)); + break; + case 5: + variants.emplace_back(static_cast(i)); + break; + case 6: + variants.emplace_back(std::to_string(i)); + break; + case 7: + variants.emplace_back(std::vector{i, i + 1}); + break; + } + } + + // Visit all variants + int sum = 0; + struct SumVisitor { + int& sum; + void operator()(char c) { sum += c; } + void operator()(short s) { sum += s; } + void operator()(int i) { sum += i; } + void operator()(long l) { sum += static_cast(l); } + void operator()(float f) { sum += static_cast(f); } + void operator()(double d) { sum += static_cast(d); } + void operator()(const std::string& s) { + sum += static_cast(s.length()); } - - // Visit all variants - int sum = 0; - struct SumVisitor { - int& sum; - void operator()(char c) { sum += c; } - void operator()(short s) { sum += s; } - void operator()(int i) { sum += i; } - void operator()(long l) { sum += static_cast(l); } - void operator()(float f) { sum += static_cast(f); } - void operator()(double d) { sum += static_cast(d); } - void operator()(const std::string& s) { sum += static_cast(s.length()); } - void operator()(const std::vector& v) { sum += static_cast(v.size()); } - }; - - for (const auto& v : variants) { - mcp::visit(SumVisitor{sum}, v); + void operator()(const std::vector& v) { + sum += static_cast(v.size()); } - - EXPECT_GT(sum, 0); + }; + + for (const auto& v : variants) { + mcp::visit(SumVisitor{sum}, v); + } + + EXPECT_GT(sum, 0); } // Special member function tests TEST_F(VariantExtensiveTest, NoCopyConstructibleAlternative) { - // Variant with non-copyable type should still be move-constructible - using V = mcp::variant; - - V v1(42); - V v2(std::move(v1)); - EXPECT_EQ(v2.index(), 0u); - EXPECT_EQ(v2.get(), 42); - - v2 = NonCopyable(); - EXPECT_EQ(v2.index(), 1u); - - V v3(std::move(v2)); - EXPECT_EQ(v3.index(), 1u); + // Variant with non-copyable type should still be move-constructible + using V = mcp::variant; + + V v1(42); + V v2(std::move(v1)); + EXPECT_EQ(v2.index(), 0u); + EXPECT_EQ(v2.get(), 42); + + v2 = NonCopyable(); + EXPECT_EQ(v2.index(), 1u); + + V v3(std::move(v2)); + EXPECT_EQ(v3.index(), 1u); } // Value category tests TEST_F(VariantExtensiveTest, PerfectForwarding) { - struct TrackValueCategory { - enum Category { LValue, RValue }; - Category cat; - - TrackValueCategory(int&) : cat(LValue) {} - TrackValueCategory(int&&) : cat(RValue) {} - }; - - int lvalue = 42; - mcp::variant v1(lvalue); - EXPECT_EQ(v1.index(), 0u); // Should select int - - // Force conversion to TrackValueCategory - mcp::variant v2(lvalue); - EXPECT_EQ(v2.get().cat, TrackValueCategory::LValue); - - mcp::variant v3(42); - EXPECT_EQ(v3.get().cat, TrackValueCategory::RValue); + struct TrackValueCategory { + enum Category { LValue, RValue }; + Category cat; + + TrackValueCategory(int&) : cat(LValue) {} + TrackValueCategory(int&&) : cat(RValue) {} + }; + + int lvalue = 42; + mcp::variant v1(lvalue); + EXPECT_EQ(v1.index(), 0u); // Should select int + + // Force conversion to TrackValueCategory + mcp::variant v2(lvalue); + EXPECT_EQ(v2.get().cat, TrackValueCategory::LValue); + + mcp::variant v3(42); + EXPECT_EQ(v3.get().cat, TrackValueCategory::RValue); } // Test helper for compile-time checks namespace { - template - struct always_false : std::false_type {}; -} +template +struct always_false : std::false_type {}; +} // namespace TEST_F(VariantExtensiveTest, CompileTimeConstraints) { - // These should compile - mcp::variant v1; - mcp::variant v2; - mcp::variant v3; - - // Test that we can store various types - mcp::variant v4(nullptr); - mcp::variant v5(true); - - SUCCEED() << "All compile-time constraints passed"; + // These should compile + mcp::variant v1; + mcp::variant v2; + mcp::variant v3; + + // Test that we can store various types + mcp::variant v4(nullptr); + mcp::variant v5(true); + + SUCCEED() << "All compile-time constraints passed"; } \ No newline at end of file From 1f82fa9557bd57ceee854c3d017bdc561a93caf6 Mon Sep 17 00:00:00 2001 From: caleb2h Date: Sat, 2 Aug 2025 10:39:57 -0700 Subject: [PATCH 2/3] Update .clang-format to remove trailing whitespace Add InsertTrailingCommas: None to ensure empty lines don't contain spaces --- .clang-format | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 711e1e1e..cc304dc7 100644 --- a/.clang-format +++ b/.clang-format @@ -48,4 +48,6 @@ SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: c++11 -UseTab: Never \ No newline at end of file +UseTab: Never +# Remove trailing whitespace +InsertTrailingCommas: None \ No newline at end of file From de949b967d8d341339fe80864afc69cb7b921773 Mon Sep 17 00:00:00 2001 From: caleb2h Date: Sat, 2 Aug 2025 10:43:54 -0700 Subject: [PATCH 3/3] Add formatting targets to build system - Add 'make format' to format all source files - Add 'make check-format' to check formatting without modifying - Add CMake targets 'format' and 'check-format' for same functionality - Integrated with clang-format using Google C++ style --- CMakeLists.txt | 18 +++++++++++++++++- Makefile | 15 ++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 209ea4a4..487f37ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,4 +44,20 @@ FetchContent_MakeAvailable(googletest) enable_testing() # Add test subdirectory -add_subdirectory(tests) \ No newline at end of file +add_subdirectory(tests) + +# Add custom target for formatting +add_custom_target(format + COMMAND find ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/tests + -name "*.h" -o -name "*.cpp" | xargs clang-format -i + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Formatting all source files with clang-format" +) + +# Add custom target to check formatting without modifying files +add_custom_target(check-format + COMMAND find ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/tests + -name "*.h" -o -name "*.cpp" | xargs clang-format --dry-run --Werror + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Checking source file formatting with clang-format" +) \ No newline at end of file diff --git a/Makefile b/Makefile index 7960c855..30eab0b6 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Makefile for MCP C++ SDK -.PHONY: all build test clean release debug help +.PHONY: all build test clean release debug help format check-format # Default target all: build test @@ -32,6 +32,17 @@ rebuild: clean all verbose: @./build.sh --verbose +# Format all source files +format: + @echo "Formatting all source files with clang-format..." + @find include tests -name "*.h" -o -name "*.cpp" | xargs clang-format -i + @echo "Formatting complete." + +# Check formatting without modifying files +check-format: + @echo "Checking source file formatting..." + @find include tests -name "*.h" -o -name "*.cpp" | xargs clang-format --dry-run --Werror + # Help help: @echo "MCP C++ SDK Makefile" @@ -45,6 +56,8 @@ help: @echo " make clean - Clean build directory" @echo " make rebuild - Clean and rebuild everything" @echo " make verbose - Build with verbose output" + @echo " make format - Format all source files with clang-format" + @echo " make check-format - Check formatting without modifying files" @echo " make help - Show this help message" @echo "" @echo "Examples:"