diff --git a/src/pantab/numeric_gen.hpp b/src/pantab/numeric_gen.hpp index 81e35623..e57488c5 100644 --- a/src/pantab/numeric_gen.hpp +++ b/src/pantab/numeric_gen.hpp @@ -3,16 +3,22 @@ #include #include #include -#include // The Tableau Hyper API requires Numeric to be templated at compile time -// but the values are only known at runtime. This solution is adopted from -// https://stackoverflow.com/questions/78888913/creating-cartesian-product-from-integer-range-template-argument/78889229?noredirect=1#comment139097273_78889229 -template constexpr auto to_integral_variant(std::size_t n) { - return [&](std::index_sequence) { - using ResType = std::variant...>; - std::array all{ - ResType{std::integral_constant{}}...}; - return all[n]; - }(std::make_index_sequence()); +// but the values are only known at runtime. This dispatches a runtime index +// n ∈ [0, N) to a compile-time integral_constant, calling f with it. +// +// Uses a function pointer table for O(1) dispatch. This replaces the previous +// std::variant + std::visit approach which was extremely slow to compile due +// to the cartesian product of two N-element variant visit dispatch tables. +template +constexpr void integral_dispatch(std::size_t n, F &&f) { + [&](std::index_sequence) { + using FnPtr = void (*)(F &); + const std::array table{ + {static_cast([](F &fn) { + fn(std::integral_constant{}); + })...}}; + table[n](f); + }(std::make_index_sequence{}); } diff --git a/src/pantab/reader.cpp b/src/pantab/reader.cpp index f2c31c5b..9a79f056 100644 --- a/src/pantab/reader.cpp +++ b/src/pantab/reader.cpp @@ -2,7 +2,6 @@ #include "numeric_gen.hpp" #include -#include #include #include @@ -296,18 +295,19 @@ class DecimalReadHelper : public ReadHelper { throw nb::value_error("Numeric scale may not exceed 38!"); } - const auto decimal_string = std::visit( - [&value](auto P, auto S) -> std::string { + const auto decimal_string = [&]() -> std::string { + std::string result; + integral_dispatch(precision_, [&](auto P) { + integral_dispatch(scale_, [&](auto S) { if constexpr (S() <= P()) { const auto decimal_value = value.get>(); - auto value_string = decimal_value.toString(); - std::erase(value_string, '.'); - return value_string; + result = decimal_value.toString(); + std::erase(result, '.'); } - throw "unreachable"; - }, - to_integral_variant(precision_), - to_integral_variant(scale_)); + }); + }); + return result; + }(); const struct ArrowStringView sv { decimal_string.data(), static_cast(decimal_string.size()) diff --git a/src/pantab/writer.cpp b/src/pantab/writer.cpp index 5df13906..85531553 100644 --- a/src/pantab/writer.cpp +++ b/src/pantab/writer.cpp @@ -8,7 +8,6 @@ #include #include #include -#include static auto GetHyperTypeFromArrowSchema(struct ArrowSchema *schema, ArrowError *error) @@ -429,17 +428,13 @@ class DecimalInsertHelper : public InsertHelper { } if (CheckNull(idx)) { - std::visit( - [&](auto P, auto S) { - if constexpr (S() <= P()) { - InsertNull>(); - return; - } else { - throw "unreachable"; - } - }, - to_integral_variant(precision_), - to_integral_variant(scale_)); + integral_dispatch(precision_, [&](auto P) { + integral_dispatch(scale_, [&](auto S) { + if constexpr (S() <= P()) { + InsertNull>(); + } + }); + }); return; } @@ -475,18 +470,14 @@ class DecimalInsertHelper : public InsertHelper { } } - std::visit( - [&](auto P, auto S) { - if constexpr (S() <= P()) { - const auto value = hyperapi::Numeric{str}; - InsertValue(std::move(value)); - return; - } else { - throw "unreachable"; - } - }, - to_integral_variant(precision_), - to_integral_variant(scale_)); + integral_dispatch(precision_, [&](auto P) { + integral_dispatch(scale_, [&](auto S) { + if constexpr (S() <= P()) { + const auto value = hyperapi::Numeric{str}; + InsertValue(std::move(value)); + } + }); + }); ArrowBufferReset(&buffer); }