This is an automated email from the ASF dual-hosted git repository.

pitrou 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 95237d96b26 GH-55312: [C++][Compute] More flexible kernel exec 
generation (#55291)
95237d96b26 is described below

commit 95237d96b26283e7e45e8cd887fcc40adbe94ab9
Author: Antoine Pitrou <[email protected]>
AuthorDate: Mon Nov 2 14:22:29 2026 +0100

    GH-55312: [C++][Compute] More flexible kernel exec generation (#55291)
    
    ### Rationale for this change
    
    Our kernel generation utilities in 
`arrow/compute/kernels/codegen_internal.h` are a hodge-podge of hand-written 
switch statements with inconsistent template parameter signatures and semantics.
    
    This PR makes those more flexible, more consistent and more reusable.
    
    ### What changes are included in this PR?
    
    * Rewrite kernel generation utilities to make them more generic, allowing 
to pass compile-time arrays of type ids
    * Add compile-time constant arrays of type ids (`kAllTypeIds`, 
`kIntegerTypeIds`...)
    * Add missing `TypeIdTraits` specializations
    * Add missing `type_id` static member definitions on `RunEndEncodedType` 
and `ExtensionType`
    * Reduce code duplication in numeric cast kernel generation
    
    ### Are these changes tested?
    
    Yes, by existing tests and new unit tests around type id traits.
    
    ### Are there any user-facing changes?
    
    No.
    
    ### Was AI used for this PR?
    
    In accordance to the [AI generation 
guidelines](https://arrow.apache.org/docs/dev/developers/overview.html#ai-generated-code),
 please disclose below whether and how AI was used in this PR.
    
    **PR code and description written by:**
    
    - [x] Human
    - [ ] AI
    
    **Reviewed before submission by:**
    
    - [x] Human
    - [ ] AI
    - [ ] Not reviewed
    
    * GitHub Issue: #55312
    
    Authored-by: Antoine Pitrou <[email protected]>
    Signed-off-by: Antoine Pitrou <[email protected]>
---
 cpp/src/arrow/compute/kernels/aggregate_mode.cc    |  27 +-
 .../arrow/compute/kernels/aggregate_quantile.cc    |  13 +-
 cpp/src/arrow/compute/kernels/codegen_internal.h   | 525 ++++++++++-----------
 cpp/src/arrow/compute/kernels/scalar_arithmetic.cc |  38 +-
 .../arrow/compute/kernels/scalar_cast_boolean.cc   |   9 +-
 .../arrow/compute/kernels/scalar_cast_numeric.cc   | 200 ++------
 .../arrow/compute/kernels/scalar_cast_string.cc    |  24 +-
 cpp/src/arrow/compute/kernels/scalar_compare.cc    |  22 +-
 cpp/src/arrow/compute/kernels/scalar_if_else.cc    |   2 +-
 cpp/src/arrow/compute/kernels/scalar_nested.cc     |   3 +-
 .../arrow/compute/kernels/scalar_string_ascii.cc   |  10 +-
 cpp/src/arrow/compute/kernels/vector_array_sort.cc |  12 +-
 cpp/src/arrow/compute/kernels/vector_replace.cc    |   9 +-
 cpp/src/arrow/compute/kernels/vector_statistics.cc |  15 +-
 cpp/src/arrow/compute/kernels/vector_swizzle.cc    |  14 +-
 cpp/src/arrow/type.cc                              |  83 +---
 cpp/src/arrow/type_test.cc                         |  18 +
 cpp/src/arrow/type_traits.h                        | 326 ++++++-------
 18 files changed, 578 insertions(+), 772 deletions(-)

diff --git a/cpp/src/arrow/compute/kernels/aggregate_mode.cc 
b/cpp/src/arrow/compute/kernels/aggregate_mode.cc
index 75786d12b6e..5011e4a0724 100644
--- a/cpp/src/arrow/compute/kernels/aggregate_mode.cc
+++ b/cpp/src/arrow/compute/kernels/aggregate_mode.cc
@@ -428,7 +428,7 @@ Status CheckOptions(KernelContext* ctx) {
   return Status::OK();
 }
 
-template <typename OutTypeUnused, typename InType>
+template <typename InType>
 struct ModeExecutor {
   static Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* 
out) {
     RETURN_NOT_OK(CheckOptions(ctx));
@@ -436,7 +436,7 @@ struct ModeExecutor {
   }
 };
 
-template <typename OutTypeUnused, typename InType>
+template <typename InType>
 struct ModeExecutorChunked {
   static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) {
     RETURN_NOT_OK(CheckOptions(ctx));
@@ -491,21 +491,20 @@ void RegisterScalarAggregateMode(FunctionRegistry* 
registry) {
   static auto default_options = ModeOptions::Defaults();
   auto func = std::make_shared<VectorFunction>("mode", Arity::Unary(), 
mode_doc,
                                                &default_options);
-  DCHECK_OK(func->AddKernel(
-      NewModeKernel(boolean(), ModeExecutor<StructType, BooleanType>::Exec,
-                    ModeExecutorChunked<StructType, BooleanType>::Exec)));
+  DCHECK_OK(func->AddKernel(NewModeKernel(boolean(), 
ModeExecutor<BooleanType>::Exec,
+                                          
ModeExecutorChunked<BooleanType>::Exec)));
   for (const auto& type : NumericTypes()) {
-    DCHECK_OK(func->AddKernel(
-        NewModeKernel(type, GenerateNumeric<ModeExecutor, StructType>(*type),
-                      GenerateNumeric<ModeExecutorChunked, 
StructType>(*type))));
+    DCHECK_OK(
+        func->AddKernel(NewModeKernel(type, 
GenerateNumeric<ModeExecutor>(*type),
+                                      
GenerateNumeric<ModeExecutorChunked>(*type))));
   }
   // Type parameters are ignored
-  DCHECK_OK(func->AddKernel(
-      NewModeKernel(decimal128(1, 0), ModeExecutor<StructType, 
Decimal128Type>::Exec,
-                    ModeExecutorChunked<StructType, Decimal128Type>::Exec)));
-  DCHECK_OK(func->AddKernel(
-      NewModeKernel(decimal256(1, 0), ModeExecutor<StructType, 
Decimal256Type>::Exec,
-                    ModeExecutorChunked<StructType, Decimal256Type>::Exec)));
+  DCHECK_OK(
+      func->AddKernel(NewModeKernel(decimal128(1, 0), 
ModeExecutor<Decimal128Type>::Exec,
+                                    
ModeExecutorChunked<Decimal128Type>::Exec)));
+  DCHECK_OK(
+      func->AddKernel(NewModeKernel(decimal256(1, 0), 
ModeExecutor<Decimal256Type>::Exec,
+                                    
ModeExecutorChunked<Decimal256Type>::Exec)));
   DCHECK_OK(registry->AddFunction(std::move(func)));
 }
 
diff --git a/cpp/src/arrow/compute/kernels/aggregate_quantile.cc 
b/cpp/src/arrow/compute/kernels/aggregate_quantile.cc
index 4e3894d2f20..79b9b415df4 100644
--- a/cpp/src/arrow/compute/kernels/aggregate_quantile.cc
+++ b/cpp/src/arrow/compute/kernels/aggregate_quantile.cc
@@ -496,7 +496,7 @@ Status CheckQuantileOptions(KernelContext* ctx) {
   return Status::OK();
 }
 
-template <typename OutputTypeUnused, typename InType>
+template <typename InType>
 struct QuantileExecutor {
   static Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* 
out) {
     RETURN_NOT_OK(CheckQuantileOptions(ctx));
@@ -504,7 +504,7 @@ struct QuantileExecutor {
   }
 };
 
-template <typename OutputTypeUnused, typename InType>
+template <typename InType>
 struct QuantileExecutorChunked {
   static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) {
     RETURN_NOT_OK(CheckQuantileOptions(ctx));
@@ -530,15 +530,14 @@ void AddQuantileKernels(VectorFunction* func) {
 
   for (const auto& ty : NumericTypes()) {
     base.signature = KernelSignature::Make({InputType(ty)}, 
OutputType(ResolveOutput));
-    // output type is determined at runtime, set template argument to nulltype
-    base.exec = GenerateNumeric<QuantileExecutor, NullType>(*ty);
-    base.exec_chunked = GenerateNumeric<QuantileExecutorChunked, 
NullType>(*ty);
+    base.exec = GenerateNumeric<QuantileExecutor>(*ty);
+    base.exec_chunked = GenerateNumeric<QuantileExecutorChunked>(*ty);
     DCHECK_OK(func->AddKernel(base));
   }
   for (auto type_id : DecimalTypeIds()) {
     base.signature = KernelSignature::Make({type_id}, 
OutputType(ResolveOutput));
-    base.exec = GenerateDecimal<QuantileExecutor, NullType>(type_id);
-    base.exec_chunked = GenerateDecimal<QuantileExecutorChunked, 
NullType>(type_id);
+    base.exec = GenerateDecimal<QuantileExecutor>(type_id);
+    base.exec_chunked = GenerateDecimal<QuantileExecutorChunked>(type_id);
     DCHECK_OK(func->AddKernel(base));
   }
 }
diff --git a/cpp/src/arrow/compute/kernels/codegen_internal.h 
b/cpp/src/arrow/compute/kernels/codegen_internal.h
index 9da440cc65a..405f35a9234 100644
--- a/cpp/src/arrow/compute/kernels/codegen_internal.h
+++ b/cpp/src/arrow/compute/kernels/codegen_internal.h
@@ -17,6 +17,7 @@
 
 #pragma once
 
+#include <concepts>
 #include <cstdint>
 #include <cstring>
 #include <memory>
@@ -49,6 +50,7 @@
 #include "arrow/util/float16.h"
 #include "arrow/util/logging.h"
 #include "arrow/util/macros.h"
+#include "arrow/util/unreachable.h"
 #include "arrow/util/visibility.h"
 #include "arrow/visit_data_inline.h"
 
@@ -1000,26 +1002,43 @@ using ScalarBinaryNotNullStatefulEqualTypes =
 // looping over vectors of data types rather than using macros or other
 // approaches.
 //
-// The kernel functor must be of the form:
+// A typical kernel functor is of the form:
 //
-// template <typename Type0, typename Type1, Args...>
+// template <typename Type1, Args...>
 // struct FUNCTOR {
 //   static void Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* 
out) {
 //     // IMPLEMENTATION
 //   }
 // };
 //
-// When you pass FUNCTOR to a GD function, you must pass at least one static
-// type along with the functor -- this is often the fixed return type of the
-// functor. This Type0 argument is passed as the first argument to the functor
-// during instantiation. The 2nd type passed to the functor is the DataType
-// subclass corresponding to the type passed as argument (not template type) to
-// the function.
-//
-// For example, GenerateNumeric<FUNCTOR, Type0>(int32()) will select a kernel
-// instantiated like FUNCTOR<Type0, Int32Type>. Any additional variadic
+// For example, GenerateNumeric<FUNCTOR>(int32()) will select a kernel
+// instantiated like FUNCTOR<Int32Type>. Any additional variadic
 // template arguments will be passed as additional template arguments to the
 // kernel template.
+//
+// In some instances, the functor needs to be parametered using several types,
+// for example both by input type and return type:
+//
+// template <typename Type0, typename Type1, Args...>
+// struct FUNCTOR {
+//   static void Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* 
out) {
+//     // IMPLEMENTATION
+//   }
+// };
+//
+// If you want the GD function to instantiate the Type1 parameter of FUNCTOR
+// while passing Type0 explicitly *before* Type1 in the template parameter 
list,
+// you can use the WithType0<> template parameter.
+// For example, GenerateNumeric<FUNCTOR, WithType0<StringType>>(int32()) will
+// select a kernel instantiated like FUNCTOR<StringType, Int32Type>.
+//
+// (note that calling GenerateNumeric<FUNCTOR, StringType>(int32()) would 
instead
+//  select a kernel instantiated like FUNCTOR<Int32Type, StringType>).
+//
+// Similarly, the WithFirstTypeTwice template parameter instructs the GD to 
pass
+// its generated template parameter twice.
+// In the example above, calling GenerateNumeric<FUNCTOR, 
WithFirstTypeTwice>(int32())
+// will select a kernel instantiated like FUNCTOR<Int32Type, Int32Type>.
 
 namespace detail {
 
@@ -1028,9 +1047,9 @@ struct GetTypeId {
   Type::type id;
   GetTypeId(const std::shared_ptr<DataType>& type)  // NOLINT implicit 
construction
       : id(type->id()) {}
-  GetTypeId(const DataType& type)  // NOLINT implicit construction
+  constexpr GetTypeId(const DataType& type)  // NOLINT implicit construction
       : id(type.id()) {}
-  GetTypeId(Type::type id)  // NOLINT implicit construction
+  constexpr GetTypeId(Type::type id)  // NOLINT implicit construction
       : id(id) {}
 };
 
@@ -1053,221 +1072,252 @@ struct FailFunctor<VectorKernel::ChunkedExec> {
   }
 };
 
-// GD for numeric types (integer and floating point)
-template <template <typename...> class Generator, typename Type0, typename... 
Args>
-auto GenerateNumeric(detail::GetTypeId get_id) {
-  using KernelType = decltype(&Generator<Type0, Int8Type, Args...>::Exec);
-  switch (get_id.id) {
+constexpr Type::type TypeIdIdentity(Type::type id) { return id; }
+
+constexpr std::optional<Type::type> TypeIdToUnsigned(Type::type id) {
+  switch (id) {
     case Type::INT8:
-      return Generator<Type0, Int8Type, Args...>::Exec;
     case Type::UINT8:
-      return Generator<Type0, UInt8Type, Args...>::Exec;
+      return Type::UINT8;
     case Type::INT16:
-      return Generator<Type0, Int16Type, Args...>::Exec;
     case Type::UINT16:
-      return Generator<Type0, UInt16Type, Args...>::Exec;
+      return Type::UINT16;
     case Type::INT32:
-      return Generator<Type0, Int32Type, Args...>::Exec;
     case Type::UINT32:
-      return Generator<Type0, UInt32Type, Args...>::Exec;
-    case Type::INT64:
-      return Generator<Type0, Int64Type, Args...>::Exec;
-    case Type::UINT64:
-      return Generator<Type0, UInt64Type, Args...>::Exec;
-    case Type::FLOAT:
-      return Generator<Type0, FloatType, Args...>::Exec;
-    case Type::DOUBLE:
-      return Generator<Type0, DoubleType, Args...>::Exec;
-    default:
-      ARROW_DCHECK(false);
-      return FailFunctor<KernelType>::Exec;
-  }
-}
-
-// Generate a kernel given a templated functor for floating point types
-//
-// See "Numeric" above for description of the generator functor
-template <template <typename...> class Generator, typename Type0, typename... 
Args>
-ArrayKernelExec GenerateFloatingPoint(detail::GetTypeId get_id) {
-  switch (get_id.id) {
-    case Type::FLOAT:
-      return Generator<Type0, FloatType, Args...>::Exec;
-    case Type::DOUBLE:
-      return Generator<Type0, DoubleType, Args...>::Exec;
-    default:
-      ARROW_DCHECK(false);
-      return nullptr;
-  }
-}
-
-// Generate a kernel given a templated functor for integer types
-//
-// See "Numeric" above for description of the generator functor
-template <template <typename...> class Generator, typename Type0,
-          typename KernelType = ArrayKernelExec, typename... Args>
-KernelType GenerateInteger(detail::GetTypeId get_id) {
-  switch (get_id.id) {
-    case Type::INT8:
-      return Generator<Type0, Int8Type, Args...>::Exec;
-    case Type::INT16:
-      return Generator<Type0, Int16Type, Args...>::Exec;
-    case Type::INT32:
-      return Generator<Type0, Int32Type, Args...>::Exec;
+      return Type::UINT32;
     case Type::INT64:
-      return Generator<Type0, Int64Type, Args...>::Exec;
-    case Type::UINT8:
-      return Generator<Type0, UInt8Type, Args...>::Exec;
-    case Type::UINT16:
-      return Generator<Type0, UInt16Type, Args...>::Exec;
-    case Type::UINT32:
-      return Generator<Type0, UInt32Type, Args...>::Exec;
     case Type::UINT64:
-      return Generator<Type0, UInt64Type, Args...>::Exec;
+      return Type::UINT64;
     default:
-      ARROW_DCHECK(false);
-      return nullptr;
+      return std::nullopt;
   }
 }
 
-template <template <typename...> class Generator, typename Type0, typename... 
Args>
-ArrayKernelExec GeneratePhysicalInteger(detail::GetTypeId get_id) {
-  switch (get_id.id) {
+constexpr std::optional<Type::type> TypeIdToPhysicalNumeric(Type::type id) {
+  switch (id) {
     case Type::INT8:
-      return Generator<Type0, Int8Type, Args...>::Exec;
+      return Type::INT8;
     case Type::INT16:
-      return Generator<Type0, Int16Type, Args...>::Exec;
+      return Type::INT16;
     case Type::INT32:
     case Type::DATE32:
     case Type::TIME32:
-      return Generator<Type0, Int32Type, Args...>::Exec;
+      return Type::INT32;
     case Type::INT64:
     case Type::DATE64:
     case Type::TIMESTAMP:
     case Type::TIME64:
     case Type::DURATION:
-      return Generator<Type0, Int64Type, Args...>::Exec;
+      return Type::INT64;
     case Type::UINT8:
-      return Generator<Type0, UInt8Type, Args...>::Exec;
+      return Type::UINT8;
     case Type::UINT16:
-      return Generator<Type0, UInt16Type, Args...>::Exec;
+      return Type::UINT16;
     case Type::UINT32:
-      return Generator<Type0, UInt32Type, Args...>::Exec;
+      return Type::UINT32;
     case Type::UINT64:
-      return Generator<Type0, UInt64Type, Args...>::Exec;
+      return Type::UINT64;
+    case Type::HALF_FLOAT:
+      return Type::HALF_FLOAT;
+    case Type::FLOAT:
+      return Type::FLOAT;
+    case Type::DOUBLE:
+      return Type::DOUBLE;
     default:
-      ARROW_DCHECK(false);
-      return nullptr;
+      return std::nullopt;
   }
 }
 
-template <template <typename...> class KernelGenerator, typename Op,
-          typename KernelType = ArrayKernelExec, typename... Args>
-KernelType ArithmeticExecFromOp(detail::GetTypeId get_id) {
-  switch (get_id.id) {
-    case Type::INT8:
-      return KernelGenerator<Int8Type, Int8Type, Op, Args...>::Exec;
-    case Type::UINT8:
-      return KernelGenerator<UInt8Type, UInt8Type, Op, Args...>::Exec;
-    case Type::INT16:
-      return KernelGenerator<Int16Type, Int16Type, Op, Args...>::Exec;
-    case Type::UINT16:
-      return KernelGenerator<UInt16Type, UInt16Type, Op, Args...>::Exec;
-    case Type::INT32:
-      return KernelGenerator<Int32Type, Int32Type, Op, Args...>::Exec;
-    case Type::UINT32:
-      return KernelGenerator<UInt32Type, UInt32Type, Op, Args...>::Exec;
-    case Type::DURATION:
-    case Type::INT64:
-    case Type::TIMESTAMP:
-      return KernelGenerator<Int64Type, Int64Type, Op, Args...>::Exec;
-    case Type::UINT64:
-      return KernelGenerator<UInt64Type, UInt64Type, Op, Args...>::Exec;
-    case Type::FLOAT:
-      return KernelGenerator<FloatType, FloatType, Op, Args...>::Exec;
-    case Type::DOUBLE:
-      return KernelGenerator<DoubleType, DoubleType, Op, Args...>::Exec;
+constexpr std::optional<Type::type> TypeIdToPhysicalBinary(Type::type id) {
+  switch (id) {
+    case Type::BINARY:
+    case Type::STRING:
+      return Type::BINARY;
+    case Type::LARGE_BINARY:
+    case Type::LARGE_STRING:
+      return Type::LARGE_BINARY;
+    case Type::BINARY_VIEW:
+    case Type::STRING_VIEW:
+      return Type::BINARY_VIEW;
     default:
-      ARROW_DCHECK(false);
-      return FailFunctor<KernelType>::Exec;
+      return std::nullopt;
   }
 }
 
-template <typename ReturnType, template <typename... Args> class Generator,
-          typename... Args>
-ReturnType GeneratePhysicalNumericGeneric(detail::GetTypeId get_id) {
-  switch (get_id.id) {
+constexpr std::optional<Type::type> TypeIdToPrimitiveAgnostic(Type::type id) {
+  switch (id) {
+    case Type::NA:
+      return Type::NA;
+    case Type::BOOL:
+      return Type::BOOL;
+    case Type::UINT8:
     case Type::INT8:
-      return Generator<Int8Type, Args...>::Exec;
+      return Type::UINT8;
+    case Type::UINT16:
     case Type::INT16:
-      return Generator<Int16Type, Args...>::Exec;
+    case Type::HALF_FLOAT:
+      return Type::UINT16;
+    case Type::UINT32:
     case Type::INT32:
+    case Type::FLOAT:
     case Type::DATE32:
     case Type::TIME32:
-      return Generator<Int32Type, Args...>::Exec;
+    case Type::INTERVAL_MONTHS:
+      return Type::UINT32;
+    case Type::UINT64:
     case Type::INT64:
+    case Type::DOUBLE:
     case Type::DATE64:
     case Type::TIMESTAMP:
     case Type::TIME64:
     case Type::DURATION:
-      return Generator<Int64Type, Args...>::Exec;
-    case Type::UINT8:
-      return Generator<UInt8Type, Args...>::Exec;
-    case Type::UINT16:
-      return Generator<UInt16Type, Args...>::Exec;
-    case Type::UINT32:
-      return Generator<UInt32Type, Args...>::Exec;
-    case Type::UINT64:
-      return Generator<UInt64Type, Args...>::Exec;
-    case Type::FLOAT:
-      return Generator<FloatType, Args...>::Exec;
-    case Type::DOUBLE:
-      return Generator<DoubleType, Args...>::Exec;
+    case Type::INTERVAL_DAY_TIME:
+      return Type::UINT64;
+    case Type::INTERVAL_MONTH_DAY_NANO:
+      return Type::INTERVAL_MONTH_DAY_NANO;
     default:
-      ARROW_DCHECK(false);
-      return nullptr;
+      return std::nullopt;
   }
 }
-template <template <typename... Args> class Generator, typename... Args>
-ArrayKernelExec GeneratePhysicalNumeric(detail::GetTypeId get_id) {
-  return GeneratePhysicalNumericGeneric<ArrayKernelExec, Generator, 
Args...>(get_id);
+
+// A special marker for GenerateKernelExec that indicates its type parameter
+// should be passed *before* the generated type parameter.
+template <typename T>
+struct WithType0 {
+  using Type = T;
+};
+
+// A special marker for GenerateKernelExec that indicates the generated type
+// parameter should be passed twice.
+struct WithFirstTypeTwice {};
+
+namespace detail {
+
+template <typename Array, std::size_t... I>
+constexpr auto array_to_tuple_impl(const Array& a, std::index_sequence<I...>) {
+  return std::make_tuple(a[I]...);
 }
 
-// Generate a kernel given a templated functor for decimal types
-template <template <typename... Args> class Generator, typename... Args>
-ArrayKernelExec GenerateDecimalToDecimal(detail::GetTypeId get_id) {
-  switch (get_id.id) {
-    case Type::DECIMAL32:
-      return Generator<Decimal32Type, Args...>::Exec;
-    case Type::DECIMAL64:
-      return Generator<Decimal64Type, Args...>::Exec;
-    case Type::DECIMAL128:
-      return Generator<Decimal128Type, Args...>::Exec;
-    case Type::DECIMAL256:
-      return Generator<Decimal256Type, Args...>::Exec;
-    default:
-      ARROW_DCHECK(false);
-      return nullptr;
+template <typename T, std::size_t N>
+constexpr auto array_to_tuple(const std::array<T, N>& a) {
+  return array_to_tuple_impl(a, std::make_index_sequence<N>{});
+}
+
+template <typename Tup>
+constexpr auto tuple_to_array(const Tup& tup) {
+  return std::apply([](auto&&... elements) { return std::array{elements...}; 
}, tup);
+}
+
+}  // namespace detail
+
+// Concatenate a number of std::array's
+template <typename... Args>
+constexpr auto array_cat(Args&&... args) {
+  return 
detail::tuple_to_array(std::tuple_cat(detail::array_to_tuple(args)...));
+}
+
+namespace detail {
+
+template <template <typename...> class Generator, typename... Args>
+struct GeneratorApplicator {
+  using Type = Generator<Args...>;
+};
+
+template <template <typename...> class Generator, typename Arg1, typename 
Type0,
+          typename... Args>
+struct GeneratorApplicator<Generator, Arg1, WithType0<Type0>, Args...> {
+  using Type = GeneratorApplicator<Generator, Type0, Arg1, Args...>::Type;
+};
+
+template <template <typename...> class Generator, typename Arg1, typename... 
Args>
+struct GeneratorApplicator<Generator, Arg1, WithFirstTypeTwice, Args...> {
+  using Type = GeneratorApplicator<Generator, Arg1, Arg1, Args...>::Type;
+};
+
+template <auto Transform, auto TypeIdArray, size_t ArrayIndex,
+          template <typename...> class Generator, typename... Args>
+  requires std::invocable<decltype(Transform), Type::type>
+auto GenerateKernelExecImpl(Type::type id) {
+  constexpr auto kNumTypeIds = std::tuple_size_v<decltype(TypeIdArray)>;
+  static_assert(ArrayIndex < kNumTypeIds);
+
+  constexpr Type::type match_id = std::get<ArrayIndex>(TypeIdArray);
+  // The transformer may return Type::type or std::optional<Type::type>
+  constexpr std::optional<Type::type> transformed_id = 
std::optional{Transform(match_id)};
+  static_assert(transformed_id.has_value(), "Transform function didn't accept 
match_id");
+
+  using TransformedType = typename TypeIdTraits<transformed_id.value()>::Type;
+  constexpr auto match_val =
+      GeneratorApplicator<Generator, TransformedType, Args...>::Type::Exec;
+
+  if constexpr (ArrayIndex == kNumTypeIds - 1) {
+    ARROW_CHECK(id == match_id) << "Unmatched type id in kernel generation";
+    return match_val;
+  } else {
+    auto fallback = [&]() {
+      return GenerateKernelExecImpl<Transform, TypeIdArray, ArrayIndex + 1, 
Generator,
+                                    Args...>(id);
+    };
+    // Help the compiler infer the proper return type for this function
+    using RetType = std::common_type_t<decltype(match_val), 
decltype(fallback())>;
+    if (id == match_id) {
+      return RetType{match_val};
+    } else {
+      return RetType{fallback()};
+    }
   }
 }
 
+}  // namespace detail
+
+template <auto Transform, auto TypeIdArray, template <typename...> class 
Generator,
+          typename... Args>
+  requires std::invocable<decltype(Transform), Type::type>
+auto GenerateKernelExec(detail::GetTypeId get_id) {
+  return detail::GenerateKernelExecImpl<Transform, TypeIdArray, 
/*ArrayIndex=*/0,
+                                        Generator, Args...>(get_id.id);
+}
+
+template <auto TypeIdArray, template <typename...> class Generator, 
typename... Args>
+auto GenerateKernelExec(detail::GetTypeId get_id) {
+  return detail::GenerateKernelExecImpl<TypeIdIdentity, TypeIdArray,
+                                        /*ArrayIndex=*/0, Generator, 
Args...>(get_id.id);
+}
+
+// GD for numeric types (integer and floating point)
+template <template <typename...> class Generator, typename... Args>
+auto GenerateNumeric(detail::GetTypeId get_id) {
+  return GenerateKernelExec<array_cat(kIntegerTypeIds, 
kPhysicalFloatingTypeIds),
+                            Generator, Args...>(get_id);
+}
+
 // Generate a kernel given a templated functor for integer types
 //
-// See "Numeric" above for description of the generator functor
-template <template <typename...> class Generator, typename Type0, typename... 
Args>
-ArrayKernelExec GenerateSignedInteger(detail::GetTypeId get_id) {
-  switch (get_id.id) {
-    case Type::INT8:
-      return Generator<Type0, Int8Type, Args...>::Exec;
-    case Type::INT16:
-      return Generator<Type0, Int16Type, Args...>::Exec;
-    case Type::INT32:
-      return Generator<Type0, Int32Type, Args...>::Exec;
-    case Type::INT64:
-      return Generator<Type0, Int64Type, Args...>::Exec;
-    default:
-      ARROW_DCHECK(false);
-      return nullptr;
-  }
+template <template <typename...> class Generator, typename... Args>
+auto GenerateInteger(detail::GetTypeId get_id) {
+  return GenerateKernelExec<kIntegerTypeIds, Generator, Args...>(get_id);
+}
+
+template <template <typename...> class Generator, typename... Args>
+auto ArithmeticExec(detail::GetTypeId get_id) {
+  return GenerateKernelExec<TypeIdToPhysicalNumeric,
+                            array_cat(kIntegerTypeIds, 
kPhysicalFloatingTypeIds,
+                                      std::array{Type::TIMESTAMP, 
Type::DURATION}),
+                            Generator, WithFirstTypeTwice, Args...>(get_id);
+}
+
+template <template <typename... Args> class Generator, typename... Args>
+auto GeneratePhysicalNumeric(detail::GetTypeId get_id) {
+  return GenerateKernelExec<TypeIdToPhysicalNumeric,
+                            array_cat(kIntegerTypeIds, 
kPhysicalFloatingTypeIds,
+                                      kTemporalTypeIds, 
std::array{Type::DURATION}),
+                            Generator, Args...>(get_id);
+}
+
+// Generate a kernel given a templated functor for decimal types
+template <template <typename... Args> class Generator, typename... Args>
+auto GenerateDecimalToDecimal(detail::GetTypeId get_id) {
+  return GenerateKernelExec<kDecimalTypeIds, Generator, Args...>(get_id);
 }
 
 // Generate a kernel given a templated functor. Only a single template is
@@ -1277,43 +1327,11 @@ ArrayKernelExec GenerateSignedInteger(detail::GetTypeId 
get_id) {
 // bits).
 //
 // See "Numeric" above for description of the generator functor
-template <template <typename...> class Generator, typename KernelType = 
ArrayKernelExec,
-          typename... Args>
-KernelType GenerateTypeAgnosticPrimitive(detail::GetTypeId get_id) {
-  switch (get_id.id) {
-    case Type::NA:
-      return Generator<NullType, Args...>::Exec;
-    case Type::BOOL:
-      return Generator<BooleanType, Args...>::Exec;
-    case Type::UINT8:
-    case Type::INT8:
-      return Generator<UInt8Type, Args...>::Exec;
-    case Type::UINT16:
-    case Type::INT16:
-    case Type::HALF_FLOAT:
-      return Generator<UInt16Type, Args...>::Exec;
-    case Type::UINT32:
-    case Type::INT32:
-    case Type::FLOAT:
-    case Type::DATE32:
-    case Type::TIME32:
-    case Type::INTERVAL_MONTHS:
-      return Generator<UInt32Type, Args...>::Exec;
-    case Type::UINT64:
-    case Type::INT64:
-    case Type::DOUBLE:
-    case Type::DATE64:
-    case Type::TIMESTAMP:
-    case Type::TIME64:
-    case Type::DURATION:
-    case Type::INTERVAL_DAY_TIME:
-      return Generator<UInt64Type, Args...>::Exec;
-    case Type::INTERVAL_MONTH_DAY_NANO:
-      return Generator<MonthDayNanoIntervalType, Args...>::Exec;
-    default:
-      ARROW_DCHECK(false);
-      return FailFunctor<KernelType>::Exec;
-  }
+template <template <typename...> class Generator, typename... Args>
+auto GenerateTypeAgnosticPrimitive(detail::GetTypeId get_id) {
+  return GenerateKernelExec<TypeIdToPrimitiveAgnostic,
+                            array_cat(kPrimitiveTypeIds, 
std::array{Type::NA}), Generator,
+                            Args...>(get_id);
 }
 
 // Similar to GenerateTypeAgnosticPrimitive, but for base variable binary types
@@ -1323,18 +1341,8 @@ KernelType 
GenerateTypeAgnosticPrimitive(detail::GetTypeId get_id) {
 // This helps cut down on code generation (see GH-50615).
 template <template <typename...> class Generator, typename... Args>
 auto GenerateTypeAgnosticVarBinaryBase(detail::GetTypeId get_id) {
-  using KernelType = decltype(&Generator<BinaryType, Args...>::Exec);
-  switch (get_id.id) {
-    case Type::BINARY:
-    case Type::STRING:
-      return Generator<BinaryType, Args...>::Exec;
-    case Type::LARGE_BINARY:
-    case Type::LARGE_STRING:
-      return Generator<LargeBinaryType, Args...>::Exec;
-    default:
-      ARROW_DCHECK(false);
-      return FailFunctor<KernelType>::Exec;
-  }
+  return GenerateKernelExec<TypeIdToPhysicalBinary, kBaseBinaryLikeTypeIds, 
Generator,
+                            Args...>(get_id);
 }
 
 // Generate a kernel given a templated functor for base binary types. Generates
@@ -1343,80 +1351,37 @@ auto 
GenerateTypeAgnosticVarBinaryBase(detail::GetTypeId get_id) {
 // BaseBinarySpecific.
 //
 // See "Numeric" above for description of the generator functor
-template <template <typename...> class Generator, typename Type0, typename... 
Args>
-ArrayKernelExec GenerateVarBinaryBase(detail::GetTypeId get_id) {
-  switch (get_id.id) {
-    case Type::BINARY:
-    case Type::STRING:
-      return Generator<Type0, BinaryType, Args...>::Exec;
-    case Type::LARGE_BINARY:
-    case Type::LARGE_STRING:
-      return Generator<Type0, LargeBinaryType, Args...>::Exec;
-    default:
-      ARROW_DCHECK(false);
-      return nullptr;
-  }
+template <template <typename...> class Generator, typename... Args>
+auto GenerateVarBinaryBase(detail::GetTypeId get_id) {
+  return GenerateKernelExec<TypeIdToPhysicalBinary, kBaseBinaryLikeTypeIds, 
Generator,
+                            Args...>(get_id);
 }
 
 // Generate a kernel given a templated functor for binary-view types. 
Generates a
 // single kernel for binary/string-view.
 //
 // See "Numeric" above for description of the generator functor
-template <template <typename...> class Generator, typename Type0, typename... 
Args>
-ArrayKernelExec GenerateVarBinaryViewBase(detail::GetTypeId get_id) {
-  switch (get_id.id) {
-    case Type::BINARY_VIEW:
-    case Type::STRING_VIEW:
-      return Generator<Type0, BinaryViewType, Args...>::Exec;
-    default:
-      ARROW_DCHECK(false);
-      return nullptr;
-  }
+template <template <typename...> class Generator, typename... Args>
+auto GenerateVarBinaryViewBase(detail::GetTypeId get_id) {
+  return GenerateKernelExec<TypeIdToPhysicalBinary, kBinaryViewLikeTypeIds, 
Generator,
+                            Args...>(get_id);
 }
 
 // Generate a kernel given a templated functor for temporal types
 //
 // See "Numeric" above for description of the generator functor
-template <template <typename...> class Generator, typename Type0, typename... 
Args>
-ArrayKernelExec GenerateTemporal(detail::GetTypeId get_id) {
-  switch (get_id.id) {
-    case Type::DATE32:
-      return Generator<Type0, Date32Type, Args...>::Exec;
-    case Type::DATE64:
-      return Generator<Type0, Date64Type, Args...>::Exec;
-    case Type::DURATION:
-      return Generator<Type0, DurationType, Args...>::Exec;
-    case Type::TIME32:
-      return Generator<Type0, Time32Type, Args...>::Exec;
-    case Type::TIME64:
-      return Generator<Type0, Time64Type, Args...>::Exec;
-    case Type::TIMESTAMP:
-      return Generator<Type0, TimestampType, Args...>::Exec;
-    default:
-      ARROW_DCHECK(false);
-      return nullptr;
-  }
+template <template <typename...> class Generator, typename... Args>
+auto GenerateTemporal(detail::GetTypeId get_id) {
+  return GenerateKernelExec<array_cat(kTemporalTypeIds, 
std::array{Type::DURATION}),
+                            Generator, Args...>(get_id);
 }
 
 // Generate a kernel given a templated functor for decimal types
 //
 // See "Numeric" above for description of the generator functor
-template <template <typename...> class Generator, typename Type0, typename... 
Args>
+template <template <typename...> class Generator, typename... Args>
 auto GenerateDecimal(detail::GetTypeId get_id) {
-  using KernelType = decltype(&Generator<Type0, Decimal256Type, 
Args...>::Exec);
-  switch (get_id.id) {
-    case Type::DECIMAL32:
-      return Generator<Type0, Decimal32Type, Args...>::Exec;
-    case Type::DECIMAL64:
-      return Generator<Type0, Decimal64Type, Args...>::Exec;
-    case Type::DECIMAL128:
-      return Generator<Type0, Decimal128Type, Args...>::Exec;
-    case Type::DECIMAL256:
-      return Generator<Type0, Decimal256Type, Args...>::Exec;
-    default:
-      ARROW_DCHECK(false);
-      return KernelType(nullptr);
-  }
+  return GenerateKernelExec<kDecimalTypeIds, Generator, Args...>(get_id);
 }
 
 // END of kernel generator-dispatchers
diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc 
b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc
index 14dbd5b7e8b..53d4751b8c4 100644
--- a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc
@@ -913,7 +913,7 @@ std::shared_ptr<ScalarFunction> 
MakeArithmeticFunction(std::string name,
                                                        FunctionDoc doc) {
   auto func = std::make_shared<FunctionImpl>(name, Arity::Binary(), 
std::move(doc));
   for (const auto& ty : NumericTypes()) {
-    auto exec = ArithmeticExecFromOp<ScalarBinaryEqualTypes, Op>(ty);
+    auto exec = ArithmeticExec<ScalarBinaryEqualTypes, Op>(ty);
     DCHECK_OK(func->AddKernel({ty, ty}, ty, exec));
   }
   AddNullExec(func.get());
@@ -927,7 +927,7 @@ std::shared_ptr<ScalarFunction> 
MakeArithmeticFunctionNotNull(std::string name,
                                                               FunctionDoc doc) 
{
   auto func = std::make_shared<FunctionImpl>(name, Arity::Binary(), 
std::move(doc));
   for (const auto& ty : NumericTypes()) {
-    auto exec = ArithmeticExecFromOp<ScalarBinaryNotNullEqualTypes, Op>(ty);
+    auto exec = ArithmeticExec<ScalarBinaryNotNullEqualTypes, Op>(ty);
     DCHECK_OK(func->AddKernel({ty, ty}, ty, exec));
   }
   AddNullExec(func.get());
@@ -939,7 +939,7 @@ std::shared_ptr<ScalarFunction> 
MakeUnaryArithmeticFunction(std::string name,
                                                             FunctionDoc doc) {
   auto func = std::make_shared<ArithmeticFunction>(name, Arity::Unary(), 
std::move(doc));
   for (const auto& ty : NumericTypes()) {
-    auto exec = ArithmeticExecFromOp<ScalarUnary, Op>(ty);
+    auto exec = ArithmeticExec<ScalarUnary, Op>(ty);
     DCHECK_OK(func->AddKernel({ty}, ty, exec));
   }
   AddNullExec(func.get());
@@ -975,7 +975,7 @@ std::shared_ptr<ScalarFunction> 
MakeUnaryArithmeticFunctionNotNull(std::string n
                                                                    FunctionDoc 
doc) {
   auto func = std::make_shared<ArithmeticFunction>(name, Arity::Unary(), 
std::move(doc));
   for (const auto& ty : NumericTypes()) {
-    auto exec = ArithmeticExecFromOp<ScalarUnaryNotNull, Op>(ty);
+    auto exec = ArithmeticExec<ScalarUnaryNotNull, Op>(ty);
     DCHECK_OK(func->AddKernel({ty}, ty, exec));
   }
   AddNullExec(func.get());
@@ -990,7 +990,7 @@ std::shared_ptr<ScalarFunction> 
MakeUnarySignedArithmeticFunctionNotNull(
   auto func = std::make_shared<ArithmeticFunction>(name, Arity::Unary(), 
std::move(doc));
   for (const auto& ty : NumericTypes()) {
     if (!arrow::is_unsigned_integer(ty->id())) {
-      auto exec = ArithmeticExecFromOp<ScalarUnaryNotNull, Op>(ty);
+      auto exec = ArithmeticExec<ScalarUnaryNotNull, Op>(ty);
       DCHECK_OK(func->AddKernel({ty}, ty, exec));
     }
   }
@@ -1508,7 +1508,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
 
   // abs(duration)
   for (auto unit : TimeUnit::values()) {
-    auto exec = ArithmeticExecFromOp<ScalarUnary, 
AbsoluteValue>(duration(unit));
+    auto exec = ArithmeticExec<ScalarUnary, AbsoluteValue>(duration(unit));
     DCHECK_OK(
         absolute_value->AddKernel({duration(unit)}, 
OutputType(duration(unit)), exec));
   }
@@ -1521,8 +1521,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
   AddDecimalUnaryKernels<AbsoluteValueChecked>(absolute_value_checked.get());
   // abs_checked(duraton)
   for (auto unit : TimeUnit::values()) {
-    auto exec =
-        ArithmeticExecFromOp<ScalarUnaryNotNull, 
AbsoluteValueChecked>(duration(unit));
+    auto exec = ArithmeticExec<ScalarUnaryNotNull, 
AbsoluteValueChecked>(duration(unit));
     DCHECK_OK(absolute_value_checked->AddKernel({duration(unit)},
                                                 OutputType(duration(unit)), 
exec));
   }
@@ -1543,7 +1542,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
   // Add add(duration, duration) -> duration
   for (auto unit : TimeUnit::values()) {
     InputType in_type(match::DurationTypeUnit(unit));
-    auto exec = ArithmeticExecFromOp<ScalarBinaryEqualTypes, 
Add>(Type::DURATION);
+    auto exec = ArithmeticExec<ScalarBinaryEqualTypes, Add>(Type::DURATION);
     DCHECK_OK(add->AddKernel({in_type, in_type}, duration(unit), 
std::move(exec)));
   }
 
@@ -1570,7 +1569,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
   // Add add(duration, duration) -> duration
   for (auto unit : TimeUnit::values()) {
     InputType in_type(match::DurationTypeUnit(unit));
-    auto exec = ArithmeticExecFromOp<ScalarBinaryEqualTypes, 
AddChecked>(Type::DURATION);
+    auto exec = ArithmeticExec<ScalarBinaryEqualTypes, 
AddChecked>(Type::DURATION);
     DCHECK_OK(
         add_checked->AddKernel({in_type, in_type}, duration(unit), 
std::move(exec)));
   }
@@ -1587,7 +1586,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
   // Add subtract(timestamp, timestamp) -> duration
   for (auto unit : TimeUnit::values()) {
     InputType in_type(match::TimestampTypeUnit(unit));
-    auto exec = ArithmeticExecFromOp<ScalarBinaryEqualTypes, 
Subtract>(Type::TIMESTAMP);
+    auto exec = ArithmeticExec<ScalarBinaryEqualTypes, 
Subtract>(Type::TIMESTAMP);
     DCHECK_OK(subtract->AddKernel({in_type, in_type},
                                   OutputType::Resolver(ResolveTemporalOutput),
                                   std::move(exec)));
@@ -1604,7 +1603,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
   // Add subtract(duration, duration) -> duration
   for (auto unit : TimeUnit::values()) {
     InputType in_type(match::DurationTypeUnit(unit));
-    auto exec = ArithmeticExecFromOp<ScalarBinaryEqualTypes, 
Subtract>(Type::DURATION);
+    auto exec = ArithmeticExec<ScalarBinaryEqualTypes, 
Subtract>(Type::DURATION);
     DCHECK_OK(subtract->AddKernel({in_type, in_type}, duration(unit), 
std::move(exec)));
   }
 
@@ -1646,8 +1645,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
   // Add subtract_checked(timestamp, timestamp) -> duration
   for (auto unit : TimeUnit::values()) {
     InputType in_type(match::TimestampTypeUnit(unit));
-    auto exec =
-        ArithmeticExecFromOp<ScalarBinaryEqualTypes, 
SubtractChecked>(Type::TIMESTAMP);
+    auto exec = ArithmeticExec<ScalarBinaryEqualTypes, 
SubtractChecked>(Type::TIMESTAMP);
     DCHECK_OK(subtract_checked->AddKernel({in_type, in_type},
                                           
OutputType::Resolver(ResolveTemporalOutput),
                                           std::move(exec)));
@@ -1664,8 +1662,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
   // Add subtract_checked(duration, duration) -> duration
   for (auto unit : TimeUnit::values()) {
     InputType in_type(match::DurationTypeUnit(unit));
-    auto exec =
-        ArithmeticExecFromOp<ScalarBinaryEqualTypes, 
SubtractChecked>(Type::DURATION);
+    auto exec = ArithmeticExec<ScalarBinaryEqualTypes, 
SubtractChecked>(Type::DURATION);
     DCHECK_OK(
         subtract_checked->AddKernel({in_type, in_type}, duration(unit), 
std::move(exec)));
   }
@@ -1710,7 +1707,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
 
   // Add multiply(duration, int64) -> duration
   for (auto unit : TimeUnit::values()) {
-    auto exec = ArithmeticExecFromOp<ScalarBinaryEqualTypes, 
Multiply>(Type::DURATION);
+    auto exec = ArithmeticExec<ScalarBinaryEqualTypes, 
Multiply>(Type::DURATION);
     DCHECK_OK(multiply->AddKernel({duration(unit), int64()}, duration(unit), 
exec));
     DCHECK_OK(multiply->AddKernel({int64(), duration(unit)}, duration(unit), 
exec));
   }
@@ -1724,8 +1721,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
 
   // Add multiply_checked(duration, int64) -> duration
   for (auto unit : TimeUnit::values()) {
-    auto exec =
-        ArithmeticExecFromOp<ScalarBinaryEqualTypes, 
MultiplyChecked>(Type::DURATION);
+    auto exec = ArithmeticExec<ScalarBinaryEqualTypes, 
MultiplyChecked>(Type::DURATION);
     DCHECK_OK(
         multiply_checked->AddKernel({duration(unit), int64()}, duration(unit), 
exec));
     DCHECK_OK(
@@ -1805,7 +1801,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
 
   // Add neg(duration) -> duration
   for (auto unit : TimeUnit::values()) {
-    auto exec = ArithmeticExecFromOp<ScalarUnary, Negate>(duration(unit));
+    auto exec = ArithmeticExec<ScalarUnary, Negate>(duration(unit));
     DCHECK_OK(negate->AddKernel({duration(unit)}, OutputType(duration(unit)), 
exec));
   }
 
@@ -1819,7 +1815,7 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) 
{
 
   // Add neg_checked(duration) -> duration
   for (auto unit : TimeUnit::values()) {
-    auto exec = ArithmeticExecFromOp<ScalarUnaryNotNull, 
Negate>(duration(unit));
+    auto exec = ArithmeticExec<ScalarUnaryNotNull, Negate>(duration(unit));
     DCHECK_OK(
         negate_checked->AddKernel({duration(unit)}, 
OutputType(duration(unit)), exec));
   }
diff --git a/cpp/src/arrow/compute/kernels/scalar_cast_boolean.cc 
b/cpp/src/arrow/compute/kernels/scalar_cast_boolean.cc
index 61669e0af0e..c9c3aa3f3f5 100644
--- a/cpp/src/arrow/compute/kernels/scalar_cast_boolean.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_cast_boolean.cc
@@ -55,17 +55,18 @@ std::vector<std::shared_ptr<CastFunction>> 
GetBooleanCasts() {
 
   for (const auto& ty : NumericTypes()) {
     ArrayKernelExec exec =
-        GenerateNumeric<applicator::ScalarUnary, BooleanType, IsNonZero>(*ty);
+        GenerateNumeric<applicator::ScalarUnary, WithType0<BooleanType>, 
IsNonZero>(*ty);
     DCHECK_OK(func->AddKernel(ty->id(), {ty}, boolean(), exec));
   }
   for (const auto& ty : BaseBinaryTypes()) {
-    ArrayKernelExec exec = 
GenerateVarBinaryBase<applicator::ScalarUnaryNotNull,
-                                                 BooleanType, 
ParseBooleanString>(*ty);
+    ArrayKernelExec exec =
+        GenerateVarBinaryBase<applicator::ScalarUnaryNotNull, 
WithType0<BooleanType>,
+                              ParseBooleanString>(*ty);
     DCHECK_OK(func->AddKernel(ty->id(), {ty}, boolean(), exec));
   }
   for (const auto& ty : BinaryViewTypes()) {
     ArrayKernelExec exec =
-        GenerateVarBinaryViewBase<applicator::ScalarUnaryNotNull, BooleanType,
+        GenerateVarBinaryViewBase<applicator::ScalarUnaryNotNull, 
WithType0<BooleanType>,
                                   ParseBooleanString>(*ty);
     DCHECK_OK(func->AddKernel(ty->id(), {ty}, boolean(), exec));
   }
diff --git a/cpp/src/arrow/compute/kernels/scalar_cast_numeric.cc 
b/cpp/src/arrow/compute/kernels/scalar_cast_numeric.cc
index ba2a8c4b563..388bf7b9ee6 100644
--- a/cpp/src/arrow/compute/kernels/scalar_cast_numeric.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_cast_numeric.cc
@@ -779,11 +779,11 @@ void AddCommonNumberCasts(const 
std::shared_ptr<DataType>& out_ty, CastFunction*
 
   // Cast from other strings
   for (const std::shared_ptr<DataType>& in_ty : BaseBinaryTypes()) {
-    auto exec = GenerateVarBinaryBase<CastFunctor, OutType>(*in_ty);
+    auto exec = GenerateVarBinaryBase<CastFunctor, WithType0<OutType>>(*in_ty);
     DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, exec));
   }
   for (const std::shared_ptr<DataType>& in_ty : BinaryViewTypes()) {
-    auto exec = GenerateVarBinaryViewBase<CastFunctor, OutType>(*in_ty);
+    auto exec = GenerateVarBinaryViewBase<CastFunctor, 
WithType0<OutType>>(*in_ty);
     DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, exec));
   }
 }
@@ -856,136 +856,97 @@ std::shared_ptr<CastFunction> 
GetCastToFloating(std::string name) {
   return func;
 }
 
-std::shared_ptr<CastFunction> GetCastToDecimal32() {
-  OutputType sig_out_ty(ResolveOutputFromOptions);
-
-  auto func = std::make_shared<CastFunction>("cast_decimal32", 
Type::DECIMAL32);
-  AddCommonCasts(Type::DECIMAL32, sig_out_ty, func.get());
-
-  // Cast from floating point
-  DCHECK_OK(func->AddKernel(Type::FLOAT, {float32()}, sig_out_ty,
-                            CastFunctor<Decimal32Type, FloatType>::Exec));
-  DCHECK_OK(func->AddKernel(Type::DOUBLE, {float64()}, sig_out_ty,
-                            CastFunctor<Decimal32Type, DoubleType>::Exec));
+std::shared_ptr<CastFunction> GetCastToHalfFloat() {
+  // HalfFloat is a bit brain-damaged for now
+  auto func = std::make_shared<CastFunction>("func", Type::HALF_FLOAT);
+  AddCommonCasts(Type::HALF_FLOAT, float16(), func.get());
 
-  // Cast from integer
+  // Casts from integer to floating point
   for (const std::shared_ptr<DataType>& in_ty : IntTypes()) {
-    auto exec = GenerateInteger<CastFunctor, Decimal32Type>(in_ty->id());
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
+    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty},
+                              TypeTraits<HalfFloatType>::type_singleton(),
+                              CastIntegerToFloating));
   }
 
-  // Cast from other strings
+  // Cast from other strings to half float.
   for (const std::shared_ptr<DataType>& in_ty : BaseBinaryTypes()) {
-    auto exec = GenerateVarBinaryBase<CastFunctor, Decimal32Type>(in_ty->id());
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
-  }
-  for (const std::shared_ptr<DataType>& in_ty : BinaryViewTypes()) {
-    auto exec = GenerateVarBinaryViewBase<CastFunctor, 
Decimal32Type>(in_ty->id());
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
+    auto exec = GenerateVarBinaryBase<CastFunctor, 
WithType0<HalfFloatType>>(*in_ty);
+    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty},
+                              TypeTraits<HalfFloatType>::type_singleton(), 
exec));
   }
 
-  // Cast from other decimal
-  auto exec = CastFunctor<Decimal32Type, Decimal32Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL32, {InputType(Type::DECIMAL32)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal32Type, Decimal64Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL64, {InputType(Type::DECIMAL64)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal32Type, Decimal128Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL128, {InputType(Type::DECIMAL128)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal32Type, Decimal256Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL256, {InputType(Type::DECIMAL256)}, 
sig_out_ty, exec));
+  DCHECK_OK(func.get()->AddKernel(Type::FLOAT, {InputType(Type::FLOAT)}, 
float16(),
+                                  CastFloatingToFloating));
+  DCHECK_OK(func.get()->AddKernel(Type::DOUBLE, {InputType(Type::DOUBLE)}, 
float16(),
+                                  CastFloatingToFloating));
   return func;
 }
 
-std::shared_ptr<CastFunction> GetCastToDecimal64() {
-  OutputType sig_out_ty(ResolveOutputFromOptions);
-
-  auto func = std::make_shared<CastFunction>("cast_decimal64", 
Type::DECIMAL64);
-  AddCommonCasts(Type::DECIMAL64, sig_out_ty, func.get());
-
+template <typename DecimalOutType>
+void AddCommonCastsToDecimal(const OutputType& sig_out_ty, CastFunction* func) 
{
   // Cast from floating point
   DCHECK_OK(func->AddKernel(Type::FLOAT, {float32()}, sig_out_ty,
-                            CastFunctor<Decimal64Type, FloatType>::Exec));
+                            CastFunctor<DecimalOutType, FloatType>::Exec));
   DCHECK_OK(func->AddKernel(Type::DOUBLE, {float64()}, sig_out_ty,
-                            CastFunctor<Decimal64Type, DoubleType>::Exec));
+                            CastFunctor<DecimalOutType, DoubleType>::Exec));
 
   // Cast from integer
   for (const std::shared_ptr<DataType>& in_ty : IntTypes()) {
-    auto exec = GenerateInteger<CastFunctor, Decimal64Type>(in_ty->id());
+    auto exec = GenerateInteger<CastFunctor, 
WithType0<DecimalOutType>>(in_ty->id());
     DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
   }
 
   // Cast from other strings
   for (const std::shared_ptr<DataType>& in_ty : BaseBinaryTypes()) {
-    auto exec = GenerateVarBinaryBase<CastFunctor, Decimal64Type>(in_ty->id());
+    auto exec =
+        GenerateVarBinaryBase<CastFunctor, 
WithType0<DecimalOutType>>(in_ty->id());
     DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
   }
   for (const std::shared_ptr<DataType>& in_ty : BinaryViewTypes()) {
-    auto exec = GenerateVarBinaryViewBase<CastFunctor, 
Decimal64Type>(in_ty->id());
+    auto exec =
+        GenerateVarBinaryViewBase<CastFunctor, 
WithType0<DecimalOutType>>(in_ty->id());
     DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
   }
 
   // Cast from other decimal
-  auto exec = CastFunctor<Decimal64Type, Decimal32Type>::Exec;
+  auto exec = CastFunctor<DecimalOutType, Decimal32Type>::Exec;
   DCHECK_OK(
       func->AddKernel(Type::DECIMAL32, {InputType(Type::DECIMAL32)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal64Type, Decimal64Type>::Exec;
+  exec = CastFunctor<DecimalOutType, Decimal64Type>::Exec;
   DCHECK_OK(
       func->AddKernel(Type::DECIMAL64, {InputType(Type::DECIMAL64)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal64Type, Decimal128Type>::Exec;
+  exec = CastFunctor<DecimalOutType, Decimal128Type>::Exec;
   DCHECK_OK(
       func->AddKernel(Type::DECIMAL128, {InputType(Type::DECIMAL128)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal64Type, Decimal256Type>::Exec;
+  exec = CastFunctor<DecimalOutType, Decimal256Type>::Exec;
   DCHECK_OK(
       func->AddKernel(Type::DECIMAL256, {InputType(Type::DECIMAL256)}, 
sig_out_ty, exec));
-  return func;
 }
 
-std::shared_ptr<CastFunction> GetCastToDecimal128() {
+std::shared_ptr<CastFunction> GetCastToDecimal32() {
   OutputType sig_out_ty(ResolveOutputFromOptions);
 
-  auto func = std::make_shared<CastFunction>("cast_decimal", Type::DECIMAL128);
-  AddCommonCasts(Type::DECIMAL128, sig_out_ty, func.get());
+  auto func = std::make_shared<CastFunction>("cast_decimal32", 
Type::DECIMAL32);
+  AddCommonCasts(Type::DECIMAL32, sig_out_ty, func.get());
+  AddCommonCastsToDecimal<Decimal32Type>(sig_out_ty, func.get());
+  return func;
+}
 
-  // Cast from floating point
-  DCHECK_OK(func->AddKernel(Type::FLOAT, {float32()}, sig_out_ty,
-                            CastFunctor<Decimal128Type, FloatType>::Exec));
-  DCHECK_OK(func->AddKernel(Type::DOUBLE, {float64()}, sig_out_ty,
-                            CastFunctor<Decimal128Type, DoubleType>::Exec));
+std::shared_ptr<CastFunction> GetCastToDecimal64() {
+  OutputType sig_out_ty(ResolveOutputFromOptions);
 
-  // Cast from integer
-  for (const std::shared_ptr<DataType>& in_ty : IntTypes()) {
-    auto exec = GenerateInteger<CastFunctor, Decimal128Type>(in_ty->id());
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
-  }
+  auto func = std::make_shared<CastFunction>("cast_decimal64", 
Type::DECIMAL64);
+  AddCommonCasts(Type::DECIMAL64, sig_out_ty, func.get());
+  AddCommonCastsToDecimal<Decimal64Type>(sig_out_ty, func.get());
+  return func;
+}
 
-  // Cast from other strings
-  for (const std::shared_ptr<DataType>& in_ty : BaseBinaryTypes()) {
-    auto exec = GenerateVarBinaryBase<CastFunctor, 
Decimal128Type>(in_ty->id());
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
-  }
-  for (const std::shared_ptr<DataType>& in_ty : BinaryViewTypes()) {
-    auto exec = GenerateVarBinaryViewBase<CastFunctor, 
Decimal128Type>(in_ty->id());
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
-  }
+std::shared_ptr<CastFunction> GetCastToDecimal128() {
+  OutputType sig_out_ty(ResolveOutputFromOptions);
 
-  // Cast from other decimal
-  auto exec = CastFunctor<Decimal128Type, Decimal32Type>::Exec;
-  // We resolve the output type of this kernel from the CastOptions
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL32, {InputType(Type::DECIMAL32)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal128Type, Decimal64Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL64, {InputType(Type::DECIMAL64)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal128Type, Decimal128Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL128, {InputType(Type::DECIMAL128)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal128Type, Decimal256Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL256, {InputType(Type::DECIMAL256)}, 
sig_out_ty, exec));
+  auto func = std::make_shared<CastFunction>("cast_decimal", Type::DECIMAL128);
+  AddCommonCasts(Type::DECIMAL128, sig_out_ty, func.get());
+  AddCommonCastsToDecimal<Decimal128Type>(sig_out_ty, func.get());
   return func;
 }
 
@@ -994,68 +955,7 @@ std::shared_ptr<CastFunction> GetCastToDecimal256() {
 
   auto func = std::make_shared<CastFunction>("cast_decimal256", 
Type::DECIMAL256);
   AddCommonCasts(Type::DECIMAL256, sig_out_ty, func.get());
-
-  // Cast from floating point
-  DCHECK_OK(func->AddKernel(Type::FLOAT, {float32()}, sig_out_ty,
-                            CastFunctor<Decimal256Type, FloatType>::Exec));
-  DCHECK_OK(func->AddKernel(Type::DOUBLE, {float64()}, sig_out_ty,
-                            CastFunctor<Decimal256Type, DoubleType>::Exec));
-
-  // Cast from integer
-  for (const std::shared_ptr<DataType>& in_ty : IntTypes()) {
-    auto exec = GenerateInteger<CastFunctor, Decimal256Type>(in_ty->id());
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
-  }
-
-  // Cast from other strings
-  for (const std::shared_ptr<DataType>& in_ty : BaseBinaryTypes()) {
-    auto exec = GenerateVarBinaryBase<CastFunctor, 
Decimal256Type>(in_ty->id());
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
-  }
-  for (const std::shared_ptr<DataType>& in_ty : BinaryViewTypes()) {
-    auto exec = GenerateVarBinaryViewBase<CastFunctor, 
Decimal256Type>(in_ty->id());
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, sig_out_ty, 
std::move(exec)));
-  }
-
-  // Cast from other decimal
-  auto exec = CastFunctor<Decimal256Type, Decimal32Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL32, {InputType(Type::DECIMAL32)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal256Type, Decimal64Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL64, {InputType(Type::DECIMAL64)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal256Type, Decimal128Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL128, {InputType(Type::DECIMAL128)}, 
sig_out_ty, exec));
-  exec = CastFunctor<Decimal256Type, Decimal256Type>::Exec;
-  DCHECK_OK(
-      func->AddKernel(Type::DECIMAL256, {InputType(Type::DECIMAL256)}, 
sig_out_ty, exec));
-  return func;
-}
-
-std::shared_ptr<CastFunction> GetCastToHalfFloat() {
-  // HalfFloat is a bit brain-damaged for now
-  auto func = std::make_shared<CastFunction>("func", Type::HALF_FLOAT);
-  AddCommonCasts(Type::HALF_FLOAT, float16(), func.get());
-
-  // Casts from integer to floating point
-  for (const std::shared_ptr<DataType>& in_ty : IntTypes()) {
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty},
-                              TypeTraits<HalfFloatType>::type_singleton(),
-                              CastIntegerToFloating));
-  }
-
-  // Cast from other strings to half float.
-  for (const std::shared_ptr<DataType>& in_ty : BaseBinaryTypes()) {
-    auto exec = GenerateVarBinaryBase<CastFunctor, HalfFloatType>(*in_ty);
-    DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty},
-                              TypeTraits<HalfFloatType>::type_singleton(), 
exec));
-  }
-
-  DCHECK_OK(func.get()->AddKernel(Type::FLOAT, {InputType(Type::FLOAT)}, 
float16(),
-                                  CastFloatingToFloating));
-  DCHECK_OK(func.get()->AddKernel(Type::DOUBLE, {InputType(Type::DOUBLE)}, 
float16(),
-                                  CastFloatingToFloating));
+  AddCommonCastsToDecimal<Decimal256Type>(sig_out_ty, func.get());
   return func;
 }
 
diff --git a/cpp/src/arrow/compute/kernels/scalar_cast_string.cc 
b/cpp/src/arrow/compute/kernels/scalar_cast_string.cc
index 52b79c8d452..43fe3910f57 100644
--- a/cpp/src/arrow/compute/kernels/scalar_cast_string.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_cast_string.cc
@@ -706,10 +706,10 @@ void AddNumberToStringCasts(CastFunction* func) {
                             NullHandling::COMPUTED_NO_PREALLOCATE));
 
   for (const std::shared_ptr<DataType>& in_ty : NumericTypes()) {
-    DCHECK_OK(
-        func->AddKernel(in_ty->id(), {in_ty}, out_ty,
-                        GenerateNumeric<NumericToStringCastFunctor, 
OutType>(*in_ty),
-                        NullHandling::COMPUTED_NO_PREALLOCATE));
+    DCHECK_OK(func->AddKernel(
+        in_ty->id(), {in_ty}, out_ty,
+        GenerateNumeric<NumericToStringCastFunctor, 
WithType0<OutType>>(*in_ty),
+        NullHandling::COMPUTED_NO_PREALLOCATE));
   }
 
   DCHECK_OK(func->AddKernel(Type::HALF_FLOAT, {float16()}, out_ty,
@@ -721,10 +721,10 @@ template <typename OutType>
 void AddDecimalToStringCasts(CastFunction* func) {
   auto out_ty = TypeTraits<OutType>::type_singleton();
   for (const auto& in_tid : DecimalTypeIds()) {
-    DCHECK_OK(
-        func->AddKernel(in_tid, {in_tid}, out_ty,
-                        GenerateDecimal<DecimalToStringCastFunctor, 
OutType>(in_tid),
-                        NullHandling::COMPUTED_NO_PREALLOCATE));
+    DCHECK_OK(func->AddKernel(
+        in_tid, {in_tid}, out_ty,
+        GenerateDecimal<DecimalToStringCastFunctor, 
WithType0<OutType>>(in_tid),
+        NullHandling::COMPUTED_NO_PREALLOCATE));
   }
 }
 
@@ -733,10 +733,10 @@ void AddTemporalToStringCasts(CastFunction* func) {
   auto out_ty = TypeTraits<OutType>::type_singleton();
   for (const auto& types : {TemporalTypes(), DurationTypes()}) {
     for (const std::shared_ptr<DataType>& in_ty : types) {
-      DCHECK_OK(
-          func->AddKernel(in_ty->id(), {InputType(in_ty->id())}, out_ty,
-                          GenerateTemporal<TemporalToStringCastFunctor, 
OutType>(*in_ty),
-                          NullHandling::COMPUTED_NO_PREALLOCATE));
+      DCHECK_OK(func->AddKernel(
+          in_ty->id(), {InputType(in_ty->id())}, out_ty,
+          GenerateTemporal<TemporalToStringCastFunctor, 
WithType0<OutType>>(*in_ty),
+          NullHandling::COMPUTED_NO_PREALLOCATE));
     }
   }
 }
diff --git a/cpp/src/arrow/compute/kernels/scalar_compare.cc 
b/cpp/src/arrow/compute/kernels/scalar_compare.cc
index e7f12838c91..d844597e5c7 100644
--- a/cpp/src/arrow/compute/kernels/scalar_compare.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_compare.cc
@@ -311,14 +311,11 @@ ScalarKernel GetCompareKernel(InputType ty, Type::type 
compare_type,
   ScalarKernel kernel;
   kernel.signature = KernelSignature::Make({ty, ty}, boolean());
   BinaryKernel func_aa =
-      GeneratePhysicalNumericGeneric<BinaryKernel, ComparePrimitiveArrayArray, 
Op>(
-          compare_type);
+      GeneratePhysicalNumeric<ComparePrimitiveArrayArray, Op>(compare_type);
   BinaryKernel func_sa =
-      GeneratePhysicalNumericGeneric<BinaryKernel, 
ComparePrimitiveScalarArray, Op>(
-          compare_type);
+      GeneratePhysicalNumeric<ComparePrimitiveScalarArray, Op>(compare_type);
   BinaryKernel func_as =
-      GeneratePhysicalNumericGeneric<BinaryKernel, 
ComparePrimitiveArrayScalar, Op>(
-          compare_type);
+      GeneratePhysicalNumeric<ComparePrimitiveArrayScalar, Op>(compare_type);
   kernel.data = std::make_shared<CompareData>(func_aa, func_sa, func_as);
   kernel.exec = exec;
   return kernel;
@@ -423,19 +420,20 @@ std::shared_ptr<ScalarFunction> 
MakeCompareFunction(std::string name, FunctionDo
   }
 
   for (const std::shared_ptr<DataType>& ty : BaseBinaryTypes()) {
-    auto exec =
-        GenerateVarBinaryBase<applicator::ScalarBinaryEqualTypes, BooleanType, 
Op>(*ty);
+    auto exec = GenerateVarBinaryBase<applicator::ScalarBinaryEqualTypes,
+                                      WithType0<BooleanType>, Op>(*ty);
     DCHECK_OK(func->AddKernel({ty, ty}, boolean(), std::move(exec)));
   }
   for (const auto& ty : BinaryViewTypes()) {
-    auto exec =
-        GenerateVarBinaryViewBase<applicator::ScalarBinaryEqualTypes, 
BooleanType, Op>(
-            *ty);
+    auto exec = GenerateVarBinaryViewBase<applicator::ScalarBinaryEqualTypes,
+                                          WithType0<BooleanType>, Op>(*ty);
     DCHECK_OK(func->AddKernel({ty, ty}, boolean(), std::move(exec)));
   }
 
   for (const auto id : {Type::DECIMAL128, Type::DECIMAL256}) {
-    auto exec = GenerateDecimal<applicator::ScalarBinaryEqualTypes, 
BooleanType, Op>(id);
+    auto exec =
+        GenerateDecimal<applicator::ScalarBinaryEqualTypes, 
WithType0<BooleanType>, Op>(
+            id);
     DCHECK_OK(func->AddKernel({InputType(id), InputType(id)}, boolean(), 
std::move(exec),
                               /*init=*/nullptr, DecimalsHaveSameScale()));
   }
diff --git a/cpp/src/arrow/compute/kernels/scalar_if_else.cc 
b/cpp/src/arrow/compute/kernels/scalar_if_else.cc
index 1d8e6c1f62e..666c0ae93ab 100644
--- a/cpp/src/arrow/compute/kernels/scalar_if_else.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_if_else.cc
@@ -1303,7 +1303,7 @@ void AddPrimitiveIfElseKernels(const 
std::shared_ptr<ScalarFunction>& scalar_fun
                                const std::vector<std::shared_ptr<DataType>>& 
types) {
   for (auto&& type : types) {
     auto exec =
-        internal::GenerateTypeAgnosticPrimitive<ResolveIfElseExec, 
ArrayKernelExec,
+        internal::GenerateTypeAgnosticPrimitive<ResolveIfElseExec,
                                                 
/*AllocateMem=*/std::false_type>(*type);
     // cond array needs to be boolean always
     std::shared_ptr<KernelSignature> sig;
diff --git a/cpp/src/arrow/compute/kernels/scalar_nested.cc 
b/cpp/src/arrow/compute/kernels/scalar_nested.cc
index 0df3dc738b0..c5d9624bada 100644
--- a/cpp/src/arrow/compute/kernels/scalar_nested.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_nested.cc
@@ -517,7 +517,8 @@ void AddListElementKernels(ScalarFunction* func) {
     auto output = OutputType{ListValuesType};
     auto sig = KernelSignature::Make(std::move(inputs), std::move(output),
                                      /*is_varargs=*/false);
-    auto scalar_exec = GenerateInteger<Functor, 
InListType>({index_type->id()});
+    auto scalar_exec =
+        GenerateInteger<Functor, WithType0<InListType>>({index_type->id()});
     ScalarKernel kernel{std::move(sig), std::move(scalar_exec)};
     kernel.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE;
     kernel.mem_allocation = MemAllocation::NO_PREALLOCATE;
diff --git a/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc 
b/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc
index 8e7b626837f..dd3c507ef53 100644
--- a/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc
@@ -874,15 +874,13 @@ void AddAsciiStringLength(FunctionRegistry* registry) {
   auto func = std::make_shared<ScalarFunction>("binary_length", Arity::Unary(),
                                                binary_length_doc);
   for (const auto& ty : {binary(), utf8()}) {
-    auto exec =
-        GenerateVarBinaryBase<applicator::ScalarUnaryNotNull, Int32Type, 
BinaryLength>(
-            ty);
+    auto exec = GenerateVarBinaryBase<applicator::ScalarUnaryNotNull,
+                                      WithType0<Int32Type>, BinaryLength>(ty);
     DCHECK_OK(func->AddKernel({ty}, int32(), std::move(exec)));
   }
   for (const auto& ty : {large_binary(), large_utf8()}) {
-    auto exec =
-        GenerateVarBinaryBase<applicator::ScalarUnaryNotNull, Int64Type, 
BinaryLength>(
-            ty);
+    auto exec = GenerateVarBinaryBase<applicator::ScalarUnaryNotNull,
+                                      WithType0<Int64Type>, BinaryLength>(ty);
     DCHECK_OK(func->AddKernel({ty}, int64(), std::move(exec)));
   }
   DCHECK_OK(func->AddKernel({InputType(Type::FIXED_SIZE_BINARY)}, int32(),
diff --git a/cpp/src/arrow/compute/kernels/vector_array_sort.cc 
b/cpp/src/arrow/compute/kernels/vector_array_sort.cc
index 1cfb11286e4..497ecd1ca96 100644
--- a/cpp/src/arrow/compute/kernels/vector_array_sort.cc
+++ b/cpp/src/arrow/compute/kernels/vector_array_sort.cc
@@ -523,6 +523,8 @@ using ArraySortIndicesState = 
internal::OptionsWrapper<ArraySortOptions>;
 
 template <typename OutType, typename InType>
 struct ArraySortIndices {
+  static_assert(std::is_same_v<UInt64Type, OutType>,
+                "Invalid output type for ArraySortIndices");
   using ArrayType = typename TypeTraits<InType>::ArrayType;
 
   static Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* 
out) {
@@ -565,29 +567,29 @@ void AddArraySortingKernels(VectorKernel base, 
VectorFunction* func) {
 
   // duration type
   base.signature = KernelSignature::Make({Type::DURATION}, uint64());
-  base.exec = GenerateNumeric<ExecTemplate, UInt64Type>(*int64());
+  base.exec = GenerateNumeric<ExecTemplate, WithType0<UInt64Type>>(*int64());
   DCHECK_OK(func->AddKernel(base));
 
   for (const auto& ty : NumericTypes()) {
     auto physical_type = GetPhysicalType(ty);
     base.signature = KernelSignature::Make({ty}, uint64());
-    base.exec = GenerateNumeric<ExecTemplate, UInt64Type>(*physical_type);
+    base.exec = GenerateNumeric<ExecTemplate, 
WithType0<UInt64Type>>(*physical_type);
     DCHECK_OK(func->AddKernel(base));
   }
   for (const auto& ty : TemporalTypes()) {
     auto physical_type = GetPhysicalType(ty);
     base.signature = KernelSignature::Make({ty->id()}, uint64());
-    base.exec = GenerateNumeric<ExecTemplate, UInt64Type>(*physical_type);
+    base.exec = GenerateNumeric<ExecTemplate, 
WithType0<UInt64Type>>(*physical_type);
     DCHECK_OK(func->AddKernel(base));
   }
   for (const auto id : {Type::DECIMAL128, Type::DECIMAL256}) {
     base.signature = KernelSignature::Make({id}, uint64());
-    base.exec = GenerateDecimal<ExecTemplate, UInt64Type>(id);
+    base.exec = GenerateDecimal<ExecTemplate, WithType0<UInt64Type>>(id);
     DCHECK_OK(func->AddKernel(base));
   }
   for (const auto& ty : BaseBinaryTypes()) {
     base.signature = KernelSignature::Make({ty}, uint64());
-    base.exec = GenerateVarBinaryBase<ExecTemplate, UInt64Type>(ty);
+    base.exec = GenerateVarBinaryBase<ExecTemplate, WithType0<UInt64Type>>(ty);
     DCHECK_OK(func->AddKernel(base));
   }
   base.signature = KernelSignature::Make({Type::FIXED_SIZE_BINARY}, uint64());
diff --git a/cpp/src/arrow/compute/kernels/vector_replace.cc 
b/cpp/src/arrow/compute/kernels/vector_replace.cc
index 313fbd53c8b..9b42e5d6cbb 100644
--- a/cpp/src/arrow/compute/kernels/vector_replace.cc
+++ b/cpp/src/arrow/compute/kernels/vector_replace.cc
@@ -824,11 +824,10 @@ template <template <class> class Functor, template 
<class> class ChunkedFunctor>
 void RegisterVectorFunction(FunctionRegistry* registry,
                             std::shared_ptr<VectorFunction> func) {
   auto add_primitive_kernel = [&](detail::GetTypeId get_id) {
-    AddKernel(
-        get_id.id, Functor<FixedSizeBinaryType>::GetSignature(get_id),
-        GenerateTypeAgnosticPrimitive<Functor, ArrayKernelExec>(get_id),
-        GenerateTypeAgnosticPrimitive<ChunkedFunctor, 
VectorKernel::ChunkedExec>(get_id),
-        registry, func.get());
+    AddKernel(get_id.id, Functor<FixedSizeBinaryType>::GetSignature(get_id),
+              GenerateTypeAgnosticPrimitive<Functor>(get_id),
+              GenerateTypeAgnosticPrimitive<ChunkedFunctor>(get_id), registry,
+              func.get());
   };
   for (const auto& ty : NumericTypes()) {
     add_primitive_kernel(ty);
diff --git a/cpp/src/arrow/compute/kernels/vector_statistics.cc 
b/cpp/src/arrow/compute/kernels/vector_statistics.cc
index cc2fbc40164..b4aa66e4f8f 100644
--- a/cpp/src/arrow/compute/kernels/vector_statistics.cc
+++ b/cpp/src/arrow/compute/kernels/vector_statistics.cc
@@ -56,8 +56,7 @@ Status ValidateOptions(const WinsorizeOptions& options) {
 
 using WinsorizeState = internal::OptionsWrapper<WinsorizeOptions>;
 
-// We have a first unused template parameter for compatibility with 
GenerateNumeric.
-template <typename Unused, typename Type>
+template <typename Type>
 struct Winsorize {
   using ArrayType = typename TypeTraits<Type>::ArrayType;
   using CType = typename TypeTraits<Type>::CType;
@@ -157,10 +156,10 @@ struct Winsorize {
   }
 };
 
-template <typename Unused, typename Type>
+template <typename Type>
 struct WinsorizeChunked {
   static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) {
-    return Winsorize<Unused, Type>::ExecChunked(ctx, batch, out);
+    return Winsorize<Type>::ExecChunked(ctx, batch, out);
   }
 };
 
@@ -198,14 +197,14 @@ void RegisterVectorStatistics(FunctionRegistry* registry) 
{
 
   for (const auto& ty : NumericTypes()) {
     base.signature = KernelSignature::Make({ty->id()}, 
&ResolveWinsorizeOutput);
-    base.exec = GenerateNumeric<Winsorize, /*Unused*/ void>(ty->id());
-    base.exec_chunked = GenerateNumeric<WinsorizeChunked, /*Unused*/ 
void>(ty->id());
+    base.exec = GenerateNumeric<Winsorize>(ty->id());
+    base.exec_chunked = GenerateNumeric<WinsorizeChunked>(ty->id());
     DCHECK_OK(winsorize->AddKernel(base));
   }
   for (auto type_id : DecimalTypeIds()) {
     base.signature = KernelSignature::Make({type_id}, &ResolveWinsorizeOutput);
-    base.exec = GenerateDecimal<Winsorize, /*Unused*/ void>(type_id);
-    base.exec_chunked = GenerateDecimal<WinsorizeChunked, /*Unused*/ 
void>(type_id);
+    base.exec = GenerateDecimal<Winsorize>(type_id);
+    base.exec_chunked = GenerateDecimal<WinsorizeChunked>(type_id);
     DCHECK_OK(winsorize->AddKernel(base));
   }
   DCHECK_OK(registry->AddFunction(std::move(winsorize)));
diff --git a/cpp/src/arrow/compute/kernels/vector_swizzle.cc 
b/cpp/src/arrow/compute/kernels/vector_swizzle.cc
index cf9f5379a69..4fae37c0fc5 100644
--- a/cpp/src/arrow/compute/kernels/vector_swizzle.cc
+++ b/cpp/src/arrow/compute/kernels/vector_swizzle.cc
@@ -242,9 +242,9 @@ struct InversePermutationImpl {
   }
 };
 
-template <typename Ignored, typename Type>
+template <typename Type>
 struct InversePermutation {
-  using ThisType = InversePermutation<Ignored, Type>;
+  using ThisType = InversePermutation<Type>;
   using IndexType = Type;
   using ShapeType = ArraySpan;
 
@@ -266,9 +266,9 @@ struct InversePermutation {
   }
 };
 
-template <typename Ignored, typename Type>
+template <typename Type>
 struct InversePermutationChunked {
-  using ThisType = InversePermutationChunked<Ignored, Type>;
+  using ThisType = InversePermutationChunked<Type>;
   using IndexType = Type;
   using ShapeType = std::shared_ptr<ChunkedArray>;
 
@@ -307,10 +307,8 @@ void RegisterVectorInversePermutation(FunctionRegistry* 
registry) {
         KernelSignature::Make({InputType(match::SameTypeId(type_id))},
                               OutputType(ResolveInversePermutationOutputType));
     kernel.init = InversePermutationState::Init;
-    kernel.exec = GenerateInteger<InversePermutation, void, 
ArrayKernelExec>(type_id);
-    kernel.exec_chunked =
-        GenerateInteger<InversePermutationChunked, void, 
VectorKernel::ChunkedExec>(
-            type_id);
+    kernel.exec = GenerateInteger<InversePermutation>(type_id);
+    kernel.exec_chunked = GenerateInteger<InversePermutationChunked>(type_id);
     kernel.can_execute_chunkwise = false;
     kernel.output_chunked = false;
     DCHECK_OK(function->AddKernel(std::move(kernel)));
diff --git a/cpp/src/arrow/type.cc b/cpp/src/arrow/type.cc
index e841f091809..019c52aee99 100644
--- a/cpp/src/arrow/type.cc
+++ b/cpp/src/arrow/type.cc
@@ -55,110 +55,45 @@ namespace arrow {
 
 using internal::checked_cast;
 
+// XXX Ideally we'd declare those fields `inline` instead, but that doesn't 
work
+// with MinGW's gcc.
+// (see 
https://github.com/apache/arrow/actions/runs/40679598086/job/122809014205)
 constexpr Type::type NullType::type_id;
 constexpr Type::type ListType::type_id;
 constexpr Type::type LargeListType::type_id;
 constexpr Type::type ListViewType::type_id;
 constexpr Type::type LargeListViewType::type_id;
-
 constexpr Type::type MapType::type_id;
-
 constexpr Type::type FixedSizeListType::type_id;
-
 constexpr Type::type BinaryType::type_id;
-
 constexpr Type::type BinaryViewType::type_id;
-
 constexpr Type::type LargeBinaryType::type_id;
-
 constexpr Type::type StringType::type_id;
-
 constexpr Type::type StringViewType::type_id;
-
 constexpr Type::type LargeStringType::type_id;
-
 constexpr Type::type FixedSizeBinaryType::type_id;
-
 constexpr Type::type StructType::type_id;
-
 constexpr Type::type Decimal32Type::type_id;
-
 constexpr Type::type Decimal64Type::type_id;
-
 constexpr Type::type Decimal128Type::type_id;
-
 constexpr Type::type Decimal256Type::type_id;
-
 constexpr Type::type SparseUnionType::type_id;
-
 constexpr Type::type DenseUnionType::type_id;
-
 constexpr Type::type Date32Type::type_id;
-
 constexpr Type::type Date64Type::type_id;
-
 constexpr Type::type Time32Type::type_id;
-
 constexpr Type::type Time64Type::type_id;
-
 constexpr Type::type TimestampType::type_id;
-
 constexpr Type::type MonthIntervalType::type_id;
-
 constexpr Type::type DayTimeIntervalType::type_id;
-
 constexpr Type::type MonthDayNanoIntervalType::type_id;
-
 constexpr Type::type DurationType::type_id;
-
 constexpr Type::type DictionaryType::type_id;
+constexpr Type::type ExtensionType::type_id;
+constexpr Type::type RunEndEncodedType::type_id;
 
 std::vector<Type::type> AllTypeIds() {
-  return {Type::NA,
-          Type::BOOL,
-          Type::INT8,
-          Type::INT16,
-          Type::INT32,
-          Type::INT64,
-          Type::UINT8,
-          Type::UINT16,
-          Type::UINT32,
-          Type::UINT64,
-          Type::HALF_FLOAT,
-          Type::FLOAT,
-          Type::DOUBLE,
-          Type::DECIMAL32,
-          Type::DECIMAL64,
-          Type::DECIMAL128,
-          Type::DECIMAL256,
-          Type::DATE32,
-          Type::DATE64,
-          Type::TIME32,
-          Type::TIME64,
-          Type::TIMESTAMP,
-          Type::INTERVAL_DAY_TIME,
-          Type::INTERVAL_MONTHS,
-          Type::DURATION,
-          Type::STRING,
-          Type::BINARY,
-          Type::LARGE_STRING,
-          Type::LARGE_BINARY,
-          Type::STRING_VIEW,
-          Type::BINARY_VIEW,
-          Type::FIXED_SIZE_BINARY,
-          Type::STRUCT,
-          Type::LIST,
-          Type::LARGE_LIST,
-          Type::LIST_VIEW,
-          Type::LARGE_LIST_VIEW,
-          Type::FIXED_SIZE_LIST,
-          Type::MAP,
-          Type::DENSE_UNION,
-          Type::SPARSE_UNION,
-          Type::DICTIONARY,
-          Type::EXTENSION,
-          Type::INTERVAL_MONTH_DAY_NANO,
-          Type::RUN_END_ENCODED};
+  return std::vector<Type::type>(kAllTypeIds.begin(), kAllTypeIds.end());
 }
 
 namespace internal {
@@ -3028,8 +2963,7 @@ std::string FixedSizeListType::ComputeFingerprint() const 
{
     } else {
       ss << 'N';
     }
-    ss << "[" << list_size_ << "]"
-       << "{" << child_fingerprint << "}";
+    ss << "[" << list_size_ << "]" << "{" << child_fingerprint << "}";
     return ss.str();
   }
   return "";
@@ -3548,8 +3482,7 @@ const std::vector<TimeUnit::type>& TimeUnit::values() {
 }
 
 const std::vector<Type::type>& DecimalTypeIds() {
-  static std::vector<Type::type> type_ids = {Type::DECIMAL32, Type::DECIMAL64,
-                                             Type::DECIMAL128, 
Type::DECIMAL256};
+  static std::vector<Type::type> type_ids{kDecimalTypeIds.begin(), 
kDecimalTypeIds.end()};
   return type_ids;
 }
 
diff --git a/cpp/src/arrow/type_test.cc b/cpp/src/arrow/type_test.cc
index 133afbfa0c5..b46807a74cf 100644
--- a/cpp/src/arrow/type_test.cc
+++ b/cpp/src/arrow/type_test.cc
@@ -49,6 +49,24 @@ using internal::checked_pointer_cast;
 TEST(TestTypeId, AllTypeIds) {
   const auto all_ids = AllTypeIds();
   ASSERT_EQ(static_cast<int>(all_ids.size()), Type::MAX_ID);
+  std::unordered_set<Type::type> distinct_ids(all_ids.begin(), all_ids.end());
+  ASSERT_EQ(distinct_ids.size(), all_ids.size());
+
+  for (const auto id : all_ids) {
+    // Check that all known type ids:
+    // - are supported by VisitTypeId
+    // - have a corresponding `TypeIdTraits` specialization
+    ASSERT_OK(VisitTypeId(id, [id]<typename T>(const T*) {
+      if constexpr (!std::is_same_v<T, DataType>) {
+        EXPECT_EQ(T::type_id, id);
+        using TypeInferredFromId = typename TypeIdTraits<T::type_id>::Type;
+        EXPECT_TRUE((std::is_same_v<T, TypeInferredFromId>));
+        return Status::OK();
+      } else {
+        return Status::Invalid("Unexpectedly got DataType* for id ", id);
+      }
+    }));
+  }
 }
 
 TEST(TestTypeSingleton, ParameterFreeTypes) {
diff --git a/cpp/src/arrow/type_traits.h b/cpp/src/arrow/type_traits.h
index fda3e3be12b..d99befd7de6 100644
--- a/cpp/src/arrow/type_traits.h
+++ b/cpp/src/arrow/type_traits.h
@@ -17,9 +17,11 @@
 
 #pragma once
 
+#include <array>
 #include <memory>
 #include <string>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "arrow/type.h"
@@ -57,6 +59,8 @@ TYPE_ID_TRAIT(STRING, StringType)
 TYPE_ID_TRAIT(BINARY, BinaryType)
 TYPE_ID_TRAIT(LARGE_STRING, LargeStringType)
 TYPE_ID_TRAIT(LARGE_BINARY, LargeBinaryType)
+TYPE_ID_TRAIT(STRING_VIEW, StringViewType)
+TYPE_ID_TRAIT(BINARY_VIEW, BinaryViewType)
 TYPE_ID_TRAIT(FIXED_SIZE_BINARY, FixedSizeBinaryType)
 TYPE_ID_TRAIT(DATE32, Date32Type)
 TYPE_ID_TRAIT(DATE64, Date64Type)
@@ -74,10 +78,13 @@ TYPE_ID_TRAIT(DECIMAL256, Decimal256Type)
 TYPE_ID_TRAIT(STRUCT, StructType)
 TYPE_ID_TRAIT(LIST, ListType)
 TYPE_ID_TRAIT(LARGE_LIST, LargeListType)
+TYPE_ID_TRAIT(LIST_VIEW, ListViewType)
+TYPE_ID_TRAIT(LARGE_LIST_VIEW, LargeListViewType)
 TYPE_ID_TRAIT(FIXED_SIZE_LIST, FixedSizeListType)
 TYPE_ID_TRAIT(MAP, MapType)
 TYPE_ID_TRAIT(DENSE_UNION, DenseUnionType)
 TYPE_ID_TRAIT(SPARSE_UNION, SparseUnionType)
+TYPE_ID_TRAIT(RUN_END_ENCODED, RunEndEncodedType)
 TYPE_ID_TRAIT(DICTIONARY, DictionaryType)
 TYPE_ID_TRAIT(EXTENSION, ExtensionType)
 
@@ -1018,6 +1025,146 @@ template <typename T, typename R = void>
 using enable_if_physical_floating_point =
     enable_if_t<is_physical_floating_type<T>::value, R>;
 
+///
+/// Compile-time collections of type ids, for metaprogramming and other 
purposes.
+///
+
+/// \addtogroup type-id-collections
+/// @{
+
+/// A std::array<> of all valid Type::type ids.
+constexpr auto kAllTypeIds = std::array{Type::NA,
+                                        Type::BOOL,
+                                        Type::INT8,
+                                        Type::INT16,
+                                        Type::INT32,
+                                        Type::INT64,
+                                        Type::UINT8,
+                                        Type::UINT16,
+                                        Type::UINT32,
+                                        Type::UINT64,
+                                        Type::HALF_FLOAT,
+                                        Type::FLOAT,
+                                        Type::DOUBLE,
+                                        Type::DECIMAL32,
+                                        Type::DECIMAL64,
+                                        Type::DECIMAL128,
+                                        Type::DECIMAL256,
+                                        Type::DATE32,
+                                        Type::DATE64,
+                                        Type::TIME32,
+                                        Type::TIME64,
+                                        Type::TIMESTAMP,
+                                        Type::INTERVAL_DAY_TIME,
+                                        Type::INTERVAL_MONTHS,
+                                        Type::DURATION,
+                                        Type::STRING,
+                                        Type::BINARY,
+                                        Type::LARGE_STRING,
+                                        Type::LARGE_BINARY,
+                                        Type::STRING_VIEW,
+                                        Type::BINARY_VIEW,
+                                        Type::FIXED_SIZE_BINARY,
+                                        Type::STRUCT,
+                                        Type::LIST,
+                                        Type::LARGE_LIST,
+                                        Type::LIST_VIEW,
+                                        Type::LARGE_LIST_VIEW,
+                                        Type::FIXED_SIZE_LIST,
+                                        Type::MAP,
+                                        Type::DENSE_UNION,
+                                        Type::SPARSE_UNION,
+                                        Type::DICTIONARY,
+                                        Type::EXTENSION,
+                                        Type::INTERVAL_MONTH_DAY_NANO,
+                                        Type::RUN_END_ENCODED};
+
+/// A std::array of all integer type ids
+constexpr auto kIntegerTypeIds =
+    std::array{Type::INT8,  Type::UINT8,  Type::INT16, Type::UINT16,
+               Type::INT32, Type::UINT32, Type::INT64, Type::UINT64};
+
+/// A std::array of all signed integer type ids
+constexpr auto kSignedIntegerTypeIds =
+    std::array{Type::INT8, Type::INT16, Type::INT32, Type::INT64};
+
+/// A std::array of all unsigned integer type ids
+constexpr auto kUnsignedIntegerTypeIds =
+    std::array{Type::UINT8, Type::UINT16, Type::UINT32, Type::UINT64};
+
+/// A std::array of all floating-point type ids
+constexpr auto kFloatingTypeIds = std::array{Type::HALF_FLOAT, Type::FLOAT, 
Type::DOUBLE};
+
+/// A std::array of all floating-point type ids, except HALF_FLOAT
+constexpr auto kPhysicalFloatingTypeIds = std::array{Type::FLOAT, 
Type::DOUBLE};
+
+/// A std::array of all numeric type ids (except decimals)
+constexpr auto kNumericTypeIds = std::array{
+    Type::INT8,  Type::UINT8,  Type::INT16,      Type::UINT16, Type::INT32, 
Type::UINT32,
+    Type::INT64, Type::UINT64, Type::HALF_FLOAT, Type::FLOAT,  Type::DOUBLE};
+
+/// A std::array of all decimal type ids
+constexpr auto kDecimalTypeIds =
+    std::array{Type::DECIMAL32, Type::DECIMAL64, Type::DECIMAL128, 
Type::DECIMAL256};
+
+/// A std::array of all temporal type ids (dates, times, timestamps)
+constexpr auto kTemporalTypeIds =
+    std::array{Type::DATE32, Type::DATE64, Type::TIME32, Type::TIME64, 
Type::TIMESTAMP};
+
+/// A std::array of all date type ids
+constexpr auto kDateTypeIds = std::array{Type::DATE32, Type::DATE64};
+
+/// A std::array of all time type ids
+constexpr auto kTimeTypeIds = std::array{Type::TIME32, Type::TIME64};
+
+/// A std::array of all interval type ids
+constexpr auto kIntervalTypeIds = std::array{
+    Type::INTERVAL_DAY_TIME, Type::INTERVAL_MONTHS, 
Type::INTERVAL_MONTH_DAY_NANO};
+
+/// A std::array of all primitive type ids (excluding null, decimals, 
binary-like)
+constexpr auto kPrimitiveTypeIds = std::array{Type::BOOL,
+                                              Type::INT8,
+                                              Type::UINT8,
+                                              Type::INT16,
+                                              Type::UINT16,
+                                              Type::INT32,
+                                              Type::UINT32,
+                                              Type::INT64,
+                                              Type::UINT64,
+                                              Type::HALF_FLOAT,
+                                              Type::FLOAT,
+                                              Type::DOUBLE,
+                                              Type::DATE32,
+                                              Type::DATE64,
+                                              Type::TIME32,
+                                              Type::TIME64,
+                                              Type::TIMESTAMP,
+                                              Type::DURATION,
+                                              Type::INTERVAL_DAY_TIME,
+                                              Type::INTERVAL_MONTHS,
+                                              Type::INTERVAL_MONTH_DAY_NANO};
+
+/// A std::array of all base-binary-like type ids
+constexpr auto kBaseBinaryLikeTypeIds =
+    std::array{Type::BINARY, Type::LARGE_BINARY, Type::STRING, 
Type::LARGE_STRING};
+
+/// A std::array of all binary-view-like type ids
+constexpr auto kBinaryViewLikeTypeIds = std::array{Type::BINARY_VIEW, 
Type::STRING_VIEW};
+
+/// @}
+
+namespace detail {
+
+template <typename T, size_t N>
+constexpr bool array_contains(const std::array<T, N>& array, T value) {
+  auto impl = [&]<std::size_t... I>(std::index_sequence<I...>) {
+    return ((std::get<I>(array) == value) || ...);
+  };  // NOLINT(readability/braces)
+  return impl(std::make_index_sequence<N>{});
+}
+
+}  // namespace detail
+
 /// @}
 
 /// \addtogroup runtime-type-predicates
@@ -1028,20 +1175,7 @@ using enable_if_physical_floating_point =
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is an integer type one
 constexpr bool is_integer(Type::type type_id) {
-  switch (type_id) {
-    case Type::UINT8:
-    case Type::INT8:
-    case Type::UINT16:
-    case Type::INT16:
-    case Type::UINT32:
-    case Type::INT32:
-    case Type::UINT64:
-    case Type::INT64:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kIntegerTypeIds, type_id);
 }
 
 /// \brief Check for a signed integer type
@@ -1049,16 +1183,7 @@ constexpr bool is_integer(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a signed integer type one
 constexpr bool is_signed_integer(Type::type type_id) {
-  switch (type_id) {
-    case Type::INT8:
-    case Type::INT16:
-    case Type::INT32:
-    case Type::INT64:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kSignedIntegerTypeIds, type_id);
 }
 
 /// \brief Check for an unsigned integer type
@@ -1066,16 +1191,7 @@ constexpr bool is_signed_integer(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is an unsigned integer type one
 constexpr bool is_unsigned_integer(Type::type type_id) {
-  switch (type_id) {
-    case Type::UINT8:
-    case Type::UINT16:
-    case Type::UINT32:
-    case Type::UINT64:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kUnsignedIntegerTypeIds, type_id);
 }
 
 /// \brief Check for a floating point type
@@ -1083,22 +1199,14 @@ constexpr bool is_unsigned_integer(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a floating point type one
 constexpr bool is_floating(Type::type type_id) {
-  switch (type_id) {
-    case Type::HALF_FLOAT:
-    case Type::FLOAT:
-    case Type::DOUBLE:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kFloatingTypeIds, type_id);
 }
 
 /// \brief Check for a physical floating point type
 ///
 /// This predicate matches floating-point types, except half-float.
 constexpr bool is_physical_floating(Type::type type_id) {
-  return is_floating(type_id) && type_id != Type::HALF_FLOAT;
+  return detail::array_contains(kPhysicalFloatingTypeIds, type_id);
 }
 
 /// \brief Check for a numeric type
@@ -1108,23 +1216,7 @@ constexpr bool is_physical_floating(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a numeric type one
 constexpr bool is_numeric(Type::type type_id) {
-  switch (type_id) {
-    case Type::UINT8:
-    case Type::INT8:
-    case Type::UINT16:
-    case Type::INT16:
-    case Type::UINT32:
-    case Type::INT32:
-    case Type::UINT64:
-    case Type::INT64:
-    case Type::HALF_FLOAT:
-    case Type::FLOAT:
-    case Type::DOUBLE:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kNumericTypeIds, type_id);
 }
 
 /// \brief Check for a decimal type
@@ -1132,16 +1224,7 @@ constexpr bool is_numeric(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a decimal type one
 constexpr bool is_decimal(Type::type type_id) {
-  switch (type_id) {
-    case Type::DECIMAL32:
-    case Type::DECIMAL64:
-    case Type::DECIMAL128:
-    case Type::DECIMAL256:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kDecimalTypeIds, type_id);
 }
 
 /// \brief Check for a type that can be used as a run-end in Run-End Encoded
@@ -1168,33 +1251,7 @@ constexpr bool is_run_end_type(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a primitive type one
 constexpr bool is_primitive(Type::type type_id) {
-  switch (type_id) {
-    case Type::BOOL:
-    case Type::UINT8:
-    case Type::INT8:
-    case Type::UINT16:
-    case Type::INT16:
-    case Type::UINT32:
-    case Type::INT32:
-    case Type::UINT64:
-    case Type::INT64:
-    case Type::HALF_FLOAT:
-    case Type::FLOAT:
-    case Type::DOUBLE:
-    case Type::DATE32:
-    case Type::DATE64:
-    case Type::TIME32:
-    case Type::TIME64:
-    case Type::TIMESTAMP:
-    case Type::DURATION:
-    case Type::INTERVAL_MONTHS:
-    case Type::INTERVAL_MONTH_DAY_NANO:
-    case Type::INTERVAL_DAY_TIME:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kPrimitiveTypeIds, type_id);
 }
 
 /// \brief Check for a base-binary-like type
@@ -1205,16 +1262,7 @@ constexpr bool is_primitive(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a base-binary-like type one
 constexpr bool is_base_binary_like(Type::type type_id) {
-  switch (type_id) {
-    case Type::BINARY:
-    case Type::LARGE_BINARY:
-    case Type::STRING:
-    case Type::LARGE_STRING:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kBaseBinaryLikeTypeIds, type_id);
 }
 
 /// \brief Check for a binary-like type (i.e. with 32-bit offsets)
@@ -1314,14 +1362,7 @@ constexpr bool is_string_or_string_view(Type::type 
type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a binary-view-like type one
 constexpr bool is_binary_view_like(Type::type type_id) {
-  switch (type_id) {
-    case Type::STRING_VIEW:
-    case Type::BINARY_VIEW:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kBinaryViewLikeTypeIds, type_id);
 }
 
 /// \brief Check for a temporal type
@@ -1329,17 +1370,7 @@ constexpr bool is_binary_view_like(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a temporal type one
 constexpr bool is_temporal(Type::type type_id) {
-  switch (type_id) {
-    case Type::DATE32:
-    case Type::DATE64:
-    case Type::TIME32:
-    case Type::TIME64:
-    case Type::TIMESTAMP:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kTemporalTypeIds, type_id);
 }
 
 /// \brief Check for a time type
@@ -1347,14 +1378,7 @@ constexpr bool is_temporal(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a primitive type one
 constexpr bool is_time(Type::type type_id) {
-  switch (type_id) {
-    case Type::TIME32:
-    case Type::TIME64:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kTimeTypeIds, type_id);
 }
 
 /// \brief Check for a date type
@@ -1362,14 +1386,7 @@ constexpr bool is_time(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a primitive type one
 constexpr bool is_date(Type::type type_id) {
-  switch (type_id) {
-    case Type::DATE32:
-    case Type::DATE64:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kDateTypeIds, type_id);
 }
 
 /// \brief Check for an interval type
@@ -1377,15 +1394,7 @@ constexpr bool is_date(Type::type type_id) {
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is an interval type one
 constexpr bool is_interval(Type::type type_id) {
-  switch (type_id) {
-    case Type::INTERVAL_MONTHS:
-    case Type::INTERVAL_DAY_TIME:
-    case Type::INTERVAL_MONTH_DAY_NANO:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kIntervalTypeIds, type_id);
 }
 
 /// \brief Check for a dictionary type
@@ -1400,17 +1409,8 @@ constexpr bool is_dictionary(Type::type type_id) { 
return type_id == Type::DICTI
 /// \param[in] type_id the type-id to check
 /// \return whether type-id is a fixed-size-binary type one
 constexpr bool is_fixed_size_binary(Type::type type_id) {
-  switch (type_id) {
-    case Type::DECIMAL32:
-    case Type::DECIMAL64:
-    case Type::DECIMAL128:
-    case Type::DECIMAL256:
-    case Type::FIXED_SIZE_BINARY:
-      return true;
-    default:
-      break;
-  }
-  return false;
+  return detail::array_contains(kDecimalTypeIds, type_id) ||
+         type_id == Type::FIXED_SIZE_BINARY;
 }
 
 /// \brief Check for a fixed-width type

Reply via email to