CPreprocess и шаблонизация в C++ для нетипового шаблона

У меня есть функция C++, которая выполняет вложенный цикл for с идентичными вычислениями два раза, каждый из вложенных циклов for действует на разные данные и на немного разные индексы массива/цикла, но концептуально делает одно и то же. Это сэкономило бы мне много времени на кодирование/отладку, если бы я просто один раз написал первый вложенный цикл for, а затем заставил компилятор дублировать его, воссоздавая вложенный цикл и заменяя соответствующие переменные и индексы. Вот фиктивный пример.

    void function(myStruct &b){
      // first nested for loop
      for(int i=1; i < 10; i++){
        for(int j=0; j < 9; j++){
          b.iF[i,j] = b.someOtherArray[i-1,j]*2;
        }
      }
      // second nested for loop I do not want to code
      for(int i=0; i < 9; i++){
        for(int j=1; j < 10; j++){
          b.jF[i,j] = b.someOtherArray[i,j-1]*2;
        }
      }
    }

Второй вложенный цикл for аналогичен первому, но «b.iF-->b.jF», «i-1,j-->i,j-1» и так далее. В остальном они делают то же самое. Я никогда не работал с шаблонами, но это не похоже на правильное использование шаблонов, поскольку нет разницы в типах. А определение макросов потребует записи «мяса» вложенных циклов в виде макроса, который будет не очень читабельным. Не уверен, что это правильное прочтение, или если есть что-то, о чем я не знаю, это было бы хорошим решением.

Я мог бы выделить каждый вложенный цикл в отдельную функцию и вызывать их с правильными переменными и индексами в качестве аргументов, но я хочу попробовать что-то другое.

Реальный вариант использования — это переход от одномерной физической задачи к трехмерной задаче, где я повторяю одномерную физику еще два раза в двух других пространственных измерениях. Так что приятно просто написать одномерный случай и попросить компилятор повторить это для меня для двух других пространственных измерений.

Я мог бы выделить каждый вложенный цикл в отдельную функцию и вызывать их с правильными переменными и индексами в качестве аргументов. Это мое предложение. помещение общего кода в какую-либо функцию позволяет повторно использовать код с разными переменными.

NathanOliver 23.05.2024 18:23

В зависимости от того, как вы собираетесь его использовать, могут возникнуть проблемы с локальностью кэша при переходе на 3D. Это не будет проблемой для массивов небольших размеров, которые поместятся в кеш. Кстати, почему вы не можете создать все массивы [10,10] и просто игнорировать ненужные строки/столбцы? В больших массивах ваш код должен поддерживать кэш, иначе он будет работать медленно!

Martin Brown 23.05.2024 18:30

«поскольку нет разницы типов». Шаблон также работает со значением, как «указатель метода».

Jarod42 23.05.2024 18:43

Кажется, у вас есть внешние доступы...

Jarod42 23.05.2024 18:52

... и синтаксические ошибки.

Ted Lyngmo 23.05.2024 19:00
Стоит ли изучать 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
5
78
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Я вижу два способа сделать это, используя абстракции более высокого уровня.

Во-первых, сами циклы можно превратить в диапазоны. Во-вторых, операцию можно превратить в процедуру.

void function(myStruct& b){
  auto body = [&](int i0, int j0, int i1, int j1) {
    b.iF[i0,j0] = b.someOtherArray[i1,j1]*2;
  };

  auto range1 = range(0, 10);
  auto range2 = range(0, 9);
  (range1 * range2)([&](auto i, auto j){
    body(i,j,i-1,j);
  });
  (range2 * range1)([&](auto i, auto j){
    body(i,j,i,j-1);
  });
}

Здесь у меня есть типы диапазонов, которые допускают операции с ними и принимают функциональные объекты. Это попытка удалить структурное кодирование (циклы for, сохранение конечных элементов и т. д.) и превратить его в данные.

Я переписываю тело так, чтобы входные и выходные аргументы были разными. Затем я пишу лямбда-обертку, которая принимает решения. Вместо этого вы можете взять i,j, delta_i и delta_j.

    body(i,j,0,-1);

Конечной целью будет код, который компилируется так же, как ваши рукописные циклы.

Конечно, range можно просто завернуть std::views::iota и operator* можно просто завернуть std::views::cartesian_product.

Weijun Zhou 23.05.2024 19:01

Я не рассматривал лямбды. Хотя на данный момент он фактически просто разбивается на отдельные функции. Спасибо!

Kschau 24.05.2024 15:42

@Kschau Вроде того; обе лямбды имеют захват [&] для неизменяющихся частей: вы помещаете изменяющиеся части в вызов функции, что одновременно документирует вещи и очищает шаблон. Они также имеют локальную область действия, поэтому вы знаете, что они вызываются только локально, что упрощает рассуждения об их работе/правильности. Наконец, их можно позиционировать в логике, которая их использует, а не быть абстрактной внешней вещью, что может облегчить читаемость. Помните «Тьюринговую яму»: если у вас есть if и goto, все программные конструкции становятся просто сахаром, но этот сахар имеет значение.

Yakk - Adam Nevraumont 24.05.2024 16:05

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