GH-48740: [C++] Add missing CTypeTraits for decimal types
Project: apache/arrow | Language: C++ | Concepts: type-systems, refactoring | Difficulty: mid
Link: https://github.com/apache/arrow/pull/50153
The problem
The Arrow C++ codebase was missing CTypeTraits specializations for its decimal types (Decimal32, Decimal64, Decimal128, Decimal256). This omission prevented generic code from mapping C++ decimal types to their corresponding Arrow type representations, breaking or limiting generic programming patterns that rely on these traits.
Why it's non-trivial
A naive fix might just add the missing specializations, but could place them in the wrong order or with inconsistent inheritance, leading to compile errors or subtle mismatches. The specializations must be defined after their corresponding TypeTraits definitions and should follow the project's conventions for consistency and maintainability.
The approach
The PR adds the missing CTypeTraits specializations for all four decimal types, ensuring that each one inherits from the corresponding TypeTraits and explicitly defines the ArrowType alias. The order and inheritance style match the rest of the codebase.
1. Add CTypeTraits specializations for decimal types:
+template <>
+struct CTypeTraits<Decimal32> : public TypeTraits<Decimal32Type> {
+ using ArrowType = Decimal32Type;
+};
+
+template <>
+struct CTypeTraits<Decimal64> : public TypeTraits<Decimal64Type> {
+ using ArrowType = Decimal64Type;
+};
+
+template <>
+struct CTypeTraits<Decimal128> : public TypeTraits<Decimal128Type> {
+ using ArrowType = Decimal128Type;
+};
+
+template <>
+struct CTypeTraits<Decimal256> : public TypeTraits<Decimal256Type> {
+ using ArrowType = Decimal256Type;
+};
What this does:
- Each specialization of
CTypeTraitsfor a decimal type inherits from the correspondingTypeTraitsspecialization, ensuring that all trait information is consistent and available. - The
ArrowTypealias is set to the correct Arrow type, enabling generic code to map from the C++ decimal type to the Arrow type.
2. Add compile-time tests to verify the mappings:
+TEST(TypesTest, DecimalTraits) {
+ static_assert(std::is_same_v<TypeTraits<Decimal32Type>::CType, Decimal32>);
+ static_assert(std::is_same_v<CTypeTraits<Decimal32>::ArrowType, Decimal32Type>);
+
+ static_assert(std::is_same_v<TypeTraits<Decimal64Type>::CType, Decimal64>);
+ static_assert(std::is_same_v<CTypeTraits<Decimal64>::ArrowType, Decimal64Type>);
+
+ static_assert(std::is_same_v<TypeTraits<Decimal128Type>::CType, Decimal128>);
+ static_assert(std::is_same_v<CTypeTraits<Decimal128>::ArrowType, Decimal128Type>);
+
+ static_assert(std::is_same_v<TypeTraits<Decimal256Type>::CType, Decimal256>);
+ static_assert(std::is_same_v<CTypeTraits<Decimal256>::ArrowType, Decimal256Type>);
+}
What this does:
- Uses
static_assertto ensure at compile time that the trait mappings are correct and complete for all decimal types.
From the review thread
[review] HuaHuaY: I think it should inherit
TypeTraits<Decimal128Type>like other specialization.
[review] Naurder: Done. Thank you very much for suggestion.
[review] zanmato1984: For consistency with the other
CTypeTraitsspecializations in this file, could we keep the public inheritance explicit here?
struct CTypeTraits<Decimal32> : public TypeTraits<Decimal32Type> {
using ArrowType = Decimal32Type;
};
...
Takeaways
- When adding type traits or template specializations, always match the ordering and inheritance patterns of existing code to avoid subtle bugs and maintain consistency.
- Compile-time tests using
static_assertare effective for verifying type trait correctness, especially in template-heavy C++ codebases. - Consistency in trait definitions and explicit inheritance make generic code safer and easier to maintain.