Создание декартова произведения на основе аргумента шаблона целочисленного диапазона

Я работаю со сторонней библиотекой, которая предоставляет класс 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.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Способ преобразования значения времени выполнения в значение времени компиляции — использовать 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 в этом выражении, верно?

Will Ayd 20.08.2024 05:32

Полагаю, название моего поста вводит в заблуждение; Я действительно ищу треугольную матрицу декартова произведения значений

Will Ayd 20.08.2024 17:17

Смещение фиксировано для S <= P, в других случаях будет throw.

Jarod42 20.08.2024 18:08

Мой мозг официально взорван...супер круто! Огромное спасибо @Jarod42!

Will Ayd 20.08.2024 20:03

Другие вопросы по теме