diff --git a/cpp/src/arrow/type.cc b/cpp/src/arrow/type.cc index cba4a0ecd3a..b9fe6746f93 100644 --- a/cpp/src/arrow/type.cc +++ b/cpp/src/arrow/type.cc @@ -38,6 +38,7 @@ #include "arrow/result.h" #include "arrow/status.h" #include "arrow/table.h" +#include "arrow/type_traits.h" #include "arrow/util/checked_cast.h" #include "arrow/util/decimal.h" #include "arrow/util/hash_util.h" @@ -3552,4 +3553,16 @@ const std::vector& DecimalTypeIds() { return type_ids; } +Result> type_singleton(Type::type id) { + auto visit = [](auto type) -> Result> { + using T = std::decay_t; + if constexpr (TypeTraits::is_parameter_free) { + return TypeTraits::type_singleton(); + } + return Status::TypeError("Type ", internal::ToString(T::type_id), + " is not a parameter-free type"); + }; + return VisitTypeId(id, visit); +} + } // namespace arrow diff --git a/cpp/src/arrow/type.h b/cpp/src/arrow/type.h index e3582056ead..5d41a45b6fe 100644 --- a/cpp/src/arrow/type.h +++ b/cpp/src/arrow/type.h @@ -2645,4 +2645,17 @@ const std::vector>& PrimitiveTypes(); ARROW_EXPORT const std::vector& DecimalTypeIds(); +/// \brief Create a data type instance from a type ID for parameter-free types +/// +/// This function creates a data type instance for types that don't require +/// additional parameters (where TypeTraits::is_parameter_free is true). +/// For types that require parameters (like TimestampType or ListType), +/// this function will return an error. +/// +/// \param[in] id The type ID to create a type instance for +/// \return The type instance for the given type ID, +/// or a TypeError if the type requires parameters +ARROW_EXPORT +Result> type_singleton(Type::type id); + } // namespace arrow diff --git a/cpp/src/arrow/type_test.cc b/cpp/src/arrow/type_test.cc index e9b1d30e6e7..6197ad58eb4 100644 --- a/cpp/src/arrow/type_test.cc +++ b/cpp/src/arrow/type_test.cc @@ -33,6 +33,7 @@ #include "arrow/memory_pool.h" #include "arrow/table.h" #include "arrow/testing/gtest_util.h" +#include "arrow/testing/matchers.h" #include "arrow/testing/random.h" #include "arrow/testing/util.h" #include "arrow/type.h" @@ -50,6 +51,29 @@ TEST(TestTypeId, AllTypeIds) { ASSERT_EQ(static_cast(all_ids.size()), Type::MAX_ID); } +TEST(TestTypeSingleton, ParameterFreeTypes) { + // Test successful cases - parameter-free types (sample a few) + std::vector>> cases = { + {Type::NA, null()}, {Type::BOOL, boolean()}, {Type::INT32, int32()}, + {Type::STRING, utf8()}, {Type::DATE32, date32()}, + }; + + for (const auto& test_case : cases) { + ARROW_SCOPED_TRACE("Testing type: ", internal::ToString(test_case.first)); + auto result = type_singleton(test_case.first); + ASSERT_OK_AND_ASSIGN(auto type, result); + AssertTypeEqual(*type, *test_case.second); + } +} + +TEST(TestTypeSingleton, ParameterizedTypes) { + // Test error cases - parameterized types (test one representative) + auto result = type_singleton(Type::TIMESTAMP); + ASSERT_RAISES(TypeError, result); + EXPECT_THAT(result.status().message(), + testing::HasSubstr("is not a parameter-free type")); +} + template void CheckTypeIdReprs(ReprFunc&& repr_func, bool expect_uppercase) { std::unordered_set unique_reprs; diff --git a/cpp/src/arrow/visit_type_inline.h b/cpp/src/arrow/visit_type_inline.h index 30f5bb54162..84d162d15c7 100644 --- a/cpp/src/arrow/visit_type_inline.h +++ b/cpp/src/arrow/visit_type_inline.h @@ -71,10 +71,8 @@ inline Status VisitTypeInline(const DataType& type, VISITOR* visitor, ARGS&&... /// \tparam ARGS Additional arguments, if any, will be passed to the Visit function after /// the `type` argument /// -/// Unlike VisitTypeInline which calls `visitor.Visit`, here `visitor` +/// Unlike VisitTypeInline which calls `visitor->Visit`, here `visitor` /// itself is called. -/// `visitor` must support a `const DataType&` argument as a fallback, -/// in addition to concrete type classes. /// /// The intent is for this to be called on a generic lambda /// that may internally use `if constexpr` or similar constructs. @@ -114,4 +112,32 @@ inline Status VisitTypeIdInline(Type::type id, VISITOR* visitor, ARGS&&... args) #undef TYPE_ID_VISIT_INLINE +#define TYPE_ID_VISIT_INLINE(TYPE_CLASS) \ + case TYPE_CLASS##Type::type_id: { \ + const TYPE_CLASS##Type* concrete_ptr = NULLPTR; \ + return std::forward(visitor)(concrete_ptr, std::forward(args)...); \ + } + +/// \brief Calls `visitor` with a nullptr of the corresponding concrete type class +/// \tparam ARGS Additional arguments, if any, will be passed to the Visit function after +/// the `type` argument +/// +/// Unlike VisitTypeIdInline which calls `visitor->Visit`, here `visitor` +/// itself is called. +/// +/// The intent is for this to be called on a generic lambda +/// that may internally use `if constexpr` or similar constructs. +template +inline auto VisitTypeId(Type::type id, VISITOR&& visitor, ARGS&&... args) + -> decltype(std::forward(visitor)(std::declval(), args...)) { + switch (id) { + ARROW_GENERATE_FOR_ALL_TYPES(TYPE_ID_VISIT_INLINE); + default: + break; + } + return Status::NotImplemented("Type not implemented"); +} + +#undef TYPE_ID_VISIT_INLINE + } // namespace arrow