This is an automated email from the ASF dual-hosted git repository.
apitrou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new 99984fd256 GH-46531: [C++] Add type_singleton utility function and
tests. (#47922)
99984fd256 is described below
commit 99984fd256c473cbdec461abdbddcca80b08b941
Author: Harshkumar Thakur <[email protected]>
AuthorDate: Wed Feb 18 22:33:10 2026 +0530
GH-46531: [C++] Add type_singleton utility function and tests. (#47922)
### Rationale for this change
Introduce a `type_singleton(Type::type id)` utility to create
parameter-free DataType instances (such as int32, boolean, utf8, etc.). Returns
an error for parameterized types.
### Are these changes tested?
Yes, by additional unit tests in `type_test.cc`.
### Are there any user-facing changes?
No.
* GitHub Issue: #46531
Authored-by: Harsh <[email protected]>
Signed-off-by: Antoine Pitrou <[email protected]>
---
cpp/src/arrow/type.cc | 13 +++++++++++++
cpp/src/arrow/type.h | 13 +++++++++++++
cpp/src/arrow/type_test.cc | 24 ++++++++++++++++++++++++
cpp/src/arrow/visit_type_inline.h | 32 +++++++++++++++++++++++++++++---
4 files changed, 79 insertions(+), 3 deletions(-)
diff --git a/cpp/src/arrow/type.cc b/cpp/src/arrow/type.cc
index cba4a0ecd3..b9fe6746f9 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<Type::type>& DecimalTypeIds() {
return type_ids;
}
+Result<std::shared_ptr<DataType>> type_singleton(Type::type id) {
+ auto visit = [](auto type) -> Result<std::shared_ptr<DataType>> {
+ using T = std::decay_t<decltype(*type)>;
+ if constexpr (TypeTraits<T>::is_parameter_free) {
+ return TypeTraits<T>::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 e3582056ea..5d41a45b6f 100644
--- a/cpp/src/arrow/type.h
+++ b/cpp/src/arrow/type.h
@@ -2645,4 +2645,17 @@ const std::vector<std::shared_ptr<DataType>>&
PrimitiveTypes();
ARROW_EXPORT
const std::vector<Type::type>& 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<T>::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<std::shared_ptr<DataType>> type_singleton(Type::type id);
+
} // namespace arrow
diff --git a/cpp/src/arrow/type_test.cc b/cpp/src/arrow/type_test.cc
index e9b1d30e6e..6197ad58eb 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<int>(all_ids.size()), Type::MAX_ID);
}
+TEST(TestTypeSingleton, ParameterFreeTypes) {
+ // Test successful cases - parameter-free types (sample a few)
+ std::vector<std::pair<Type::type, std::shared_ptr<DataType>>> 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 <typename ReprFunc>
void CheckTypeIdReprs(ReprFunc&& repr_func, bool expect_uppercase) {
std::unordered_set<std::string> unique_reprs;
diff --git a/cpp/src/arrow/visit_type_inline.h
b/cpp/src/arrow/visit_type_inline.h
index 30f5bb5416..84d162d15c 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>(visitor)(concrete_ptr,
std::forward<ARGS>(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 <typename VISITOR, typename... ARGS>
+inline auto VisitTypeId(Type::type id, VISITOR&& visitor, ARGS&&... args)
+ -> decltype(std::forward<VISITOR>(visitor)(std::declval<DataType*>(),
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