Я работаю со сторонней библиотекой, которая предоставляет класс Numeric
, который должен быть шаблонизирован с аргументами точности и масштабирования. К сожалению, в контексте создаваемой мной библиотеки точность и масштаб неизвестны до момента выполнения, поэтому мне нужно создать сопоставление этих значений во время выполнения с соответствующими шаблонными функциями.
Библиотека Numeric поддерживает до 38 десятичных цифр, поэтому теоретически мне нужно создать отображение формы:
mapping = {
<38, 38>: []() { // do something with Numeric<38, 38> ... },
<38, 37>: []() { // do something with Numeric<38, 37> ... },
...
<37, 37>: []() { // do something with Numeric<38, 37> ... },
<37, 36>: []() { // do something with Numeric<38, 37> ... },
...
}
Для всех аргументов точности и масштаба в диапазоне (0, 38], где scale <= precision
Это код, который я создал, игнорируя реализацию значений карты std::function и используя на данный момент меньший пакет параметров:
using funcMapType = std::map<std::pair<int, int>, std::function<void(void)>>;
template <int P, int S, int... Rest> struct NumericCreatorInserter {
static void insert(funcMapType &func_map) {
NumericCreatorInserter<P, P>::insert(func_map);
NumericCreatorInserter<P, S>::insert(func_map);
NumericCreatorInserter<P, Rest...>::insert(func_map);
NumericCreatorInserter<S, Rest...>::insert(func_map);
}
};
template <int Precision, int Scale>
struct NumericCreatorInserter<Precision, Scale> {
static void insert(funcMapType &func_map) {
std::cout << "Precision is: " << Precision << " and scale is: " << Scale
<< std::endl;
func_map.emplace(std::make_pair(Precision, Scale), [](void) { return; });
}
};
static funcMapType initializeNumericCreatorMap() {
funcMapType numeric_creators;
NumericCreatorInserter<3, 2, 1, 0>::insert(numeric_creators);
return numeric_creators;
};
Отлаживая это во время выполнения, я получаю следующее:
Precision is: 3 and scale is: 3
Precision is: 3 and scale is: 2
Precision is: 3 and scale is: 3
Precision is: 3 and scale is: 1
Precision is: 3 and scale is: 0
Precision is: 1 and scale is: 0
Precision is: 2 and scale is: 2
Precision is: 2 and scale is: 1
Precision is: 2 and scale is: 0
Precision is: 1 and scale is: 0
Я ожидаю увидеть пары (3, 3), (3, 2), (3, 1), (3, 0), (2, 2), (2, 1) и т. д.
Думаю, я понимаю проблему с моим кодом: последовательность, начинающаяся с (3, 3), (3, 1), (3, 0) и (1, 0), происходит, когда P = 3, S = 1 и Rest. ...=0, что происходит при первом рекурсивном вызове функции
С учетом вышесказанного я изо всех сил пытаюсь выразить то, чего пытаюсь достичь, на C++. Есть ли еще одна специализация шаблона, которую мне следует ввести, чтобы это заработало?
В идеале я ищу решение, работающее с С++ 17.
Способ преобразования значения времени выполнения в значение времени компиляции — использовать std::variant:
template <std::size_t N>
constexpr auto to_integral_variant(std::size_t n)
{
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
using ResType = std::variant<std::integral_constant<std::size_t, Is>...>;
ResType all[] = {ResType{std::integral_constant<std::size_t, Is>{}}...};
return all[n];
}(std::make_index_sequence<N>());
}
затем отправьте команду std::visit:
auto foo(std::size_t prec, std::size_t scale)
{
return std::visit([](auto P, auto S) {
return func<P(), S()>();
}, to_integral_variant<MAX>(prec), to_integral_variant<MAX>(scale));
}
Чтобы «гарантировать» наличие P() <= S()
в вашем коде, вы можете использовать if constexpr
:
return std::visit(
[](auto P, auto S) {
if constexpr (S() <= P()) {
return func<P(), S()>();
} else {
throw "unreachable";
}
}, to_integral_variant<MAX>(prec), to_integral_variant<MAX>(scale));
или даже сгруппировать пару
template <std::size_t N>
struct Helper
{
template <std::size_t P>
struct Inner {
using type = decltype(
[]<std::size_t... Is>(std::index_sequence<Is...>){
return std::tuple<
std::integral_constant<std::pair<std::size_t, std::size_t>,
std::pair(P, Is)>...>();
}(std::make_index_sequence<P + 1>())
);
};
using type = typename tuple_to_variant<
decltype(
[]<std::size_t... Is>(std::index_sequence<Is...>){
return std::tuple_cat(typename Inner<Is>::type{}...);
}(std::make_index_sequence<N>())
)
>::type;
};
template <std::size_t N>
constexpr auto to_integral_pair_variant(std::size_t prec, std::size_t scale)
{
return std::visit(
[](auto P, auto S) -> Helper<N>::type
{
if constexpr (S() <= P()) {
return std::integral_constant<std::pair<std::size_t, std::size_t>,
std::pair(P(), S())>{};
} else {
throw "unreachable";
}
}, to_integral_variant<N>(prec), to_integral_variant<N>(scale)
);
}
а потом
auto foo(std::size_t prec, std::size_t scale)
{
return std::visit(
[](auto P) {
return func<P().first, P().second>();
}, to_integral_pair_variant<MAX>(prec, scale));
}
Полагаю, название моего поста вводит в заблуждение; Я действительно ищу треугольную матрицу декартова произведения значений
Смещение фиксировано для S <= P
, в других случаях будет throw
.
Мой мозг официально взорван...супер круто! Огромное спасибо @Jarod42!
Это... волшебно. Спасибо за это! Но при попытке это я все еще получаю сообщение об ошибке, что точность должна быть больше, чем масштаб. Я пока не могу утверждать, что полностью все это понимаю, но нет ничего, что мешает S быть больше P в этом выражении, верно?