Как сконвертировать GNU-C вложенные функции в лямбды С++ и чтоб переносимо
Добрый день,
есть тонна своего кода на GNU-C, который использует вложенные функции (nested functions), которые так и не воткнули в стандарт. Решил таки перетащить это все на стандартный С++. Всегда думал, что ламбды в С++ - это почти то же самое, что и вложенные функции. Уткнулся, что не могу скомпилировать вроде бы тривиальщину:
void Func(int N, int M, double *_X)
{ double (*X)[M] = static_cast<double (*)[M]>((void*)_X);
int i, j;
double (*Tester1)(int, int);
double (*Tester2)(double *);
Tester1 = [&] (int k, int l) -> double
{ return (double)(k*1000+l); };
Tester2 = [&] (double *XX) -> double
{ int i; double r; for(i=1; i<M; i++) r+=XX*XX; return r; };
for(i=0; i<N; i++)
for(j=0; j<M; j++)
X[j]=Tester1(i,j); // i*1000+j;
for(i=0; i<N; i++)
X[0]=Tester2(X+1);
return;
}
причем первая лямбда (Tester1) компилируется, а вторая - нет что типа не может сконвертировать тип.
Подскажите, пожалуйста, что я делаю не так?
Спасибо!
PS: странно как-то текст вставился, в некоторых местях квадратные скобки пропущены, приложил исходник как есть.
PPS: надобна именно лямбда с именем, чтоб ее несколько раз вызывать, и чтоб она пользовала локальные аргументы. В класс засовывать - совсем не вариант, и писанины много, и не оптимально с точки зрения кэша данных будет - у меня почти всегда эти вложенные функции для выжимания производительности и для многоядерного распараллеливания сделаны.
Подскажите, пожалуйста, что я делаю не так?
Так просто, без извращений, насколько я знаю, сделать нельзя, т.к. по стандарту C++11, lambda can only be converted to a function pointer if it does not capture... В первом случае (Tester1) компилируется, поскольку де-факто capture и нет, так что [&] не имеет эффекта, а во втором случае это уже не так. :))
В класс засовывать - совсем не вариант, и писанины много...
double (*Tester1)(int, int);
double (*Tester2)(double *);
auto Tester1 = [&] (int k, int l) -> double {...};
auto Tester2 = [&] (double *XX) -> double {...};
остальное - без изменений
Да уж, писанины и вправду много.:))
Лямбды в плюсах - хорошо, что появились. Но после шарпов как-то корёжит, глядя на синтаксис. С непривычки что ли. Всё никак не могу подружиться с плюсовым синтаксисом лямбд, с самого 11-го стандарта.
> Но после шарпов как-то корёжит, глядя на синтаксис.
В плюсах получается описание через auto достаточно лаконичное и от nested functions мало отличающееся. Скажите, пожалуйста, чем шарповские лямбды более привлекательны? Я в шарпе никогда не писал, просто любопытно.
> не обязательно писать, можно просто почитать. задать в гоголе "c#
lambda examples", и выбрать что попонятнее. и будет у вас собственное
мнение.
правильно, я перед тем как писать так и сделал, но не понял чем функционально, или по громоздкости написания, или еще как лямбда в шарпе существеннее отличается от плюсов, поэтому решил и полюбопытствовать, что именно я такого пропустил.
> А почему вы, сионисты, упорно во всех примерах передаете лямбдам
ссылки на все внешние переменные? Почему [&] а не просто []? На
всякий случай, чтоб было?
ну я-то на лямбды только сейчас перехожу, до этого используя только вложенные функции. Там-то внешние переменные так и так видны всегда были. Другое дело, в моем случае - это ключевая фича. Переменные моих головных фунций лежат в стеке, обычно хорошо закешированном во всех кешах памяти предыдущими операциями, и производительность от такой конструкции не падает. Если разделять что-то надо, что-то не надо, или, еще хуже засовывать в разные классы, оно ляжет в памяти далеко друг от друга, и обычно наблюдается потеря производительности.
В общем случае - не спорю, наличие возможности "видеть" только подкласс переменных - правильнее с точки зрения идеологии.
А почему вы, сионисты, упорно во всех примерах передаете лямбдам ссылки на все внешние переменные?
И чего это сразу - сионисты, наше дело auto и [&] написать, а дальше - компилятор умный, он пусть думает... Он вот в первом примере сам догадался, что лямбда с [&] - это от лукавого, и родил кондовый function pointer, я современных плюсовых компиляторов уже и побаиваться стал. :))
Переменные моих головных фунций лежат в стеке, обычно хорошо закешированном во всех кешах памяти предыдущими операциями, и производительность от такой конструкции не падает.
Ну да, производительность от использования лямбд не должна падать, хоть лямбды по-сути есть конвертация в класс, но при этом они создаются в стеке и конструктор класса - inlined. Но, сравнительно потестировать тайминги вызовов не мешало бы. Вот тут https://web.mst.edu/~nmjxv3/articles/lambdas.html немного об оверхеде лямбд в плюсах.
> Но, сравнительно потестировать тайминги вызовов не мешало бы. Вот тут https://web.mst.edu/~nmjxv3/articles/lambdas.html немного об оверхеде лямбд в плюсах.
спасибо большое за интересную и познавательную ссылку! Тестировать-то буду однозначно. Я сейчас в процессе принятия решения о перелопачивании огромного куска кода с С на С++. Код изначально шел с фортрана, но много раз частично переписывался и улучшался. Единственно - все было завязано на многомерные массивы в аргументах и вложенные функции. Ждал с 2000 года, чтоб вложенные фунции внесли в стандарт, но этого так и не случилось до настоящего времени, вот и планировал перетащить это все на С++. Код весь в нашей команде разрабатывался, с очень сильной стандартизацией как писать и как располагать данные и с сильным использованием мультитрединга. Несколько
раз до этого видел как перекомпиляция с С на С++ ухудшала производительность, поэтому всегда с опаской смотрел на авантюру перетащить этот код с С на С++.
если код с фортрана, то с большой вероятностью это рассчеты. не пробовали переписать на Julia? (https://julialang.org/)
Вставлю свои 5 копеек, хотя вопрос не ко мне. :))
Посмотрел бенчмарки Julia, сравнение с Fortran там, на мой взгляд, некорректное, т.к. gnu-компилятор для фортрана - это ни о чем, там разрыв в производительности с Intel Fortran Compiler(IFC)/Portland Group Compiler (PGI) - громадный... По-моему код, скомпилированный PGI был сильно быстрее, точно не помню
Спасибо за интересные обсуждения и советы!
> gnu-компилятор для фортрана - это ни о чем,
там разрыв в производительности с Intel Fortran Compiler(IFC)/Portland
Group Compiler (PGI) - громадный... По-моему код, скомпилированный PGI
был сильно быстрее, точно не помню
да, сейчас это верно. Был довольно большой промежуток времени (начало 2000) когда гнутый компилер уступал на проценты, но гемор по подцеплянию icc/pgcc на различные кластерные платформы был тот еще, то есть суммарно GNU был и удобнее, и портабельнее, и, при понимании специфики не уступал по производительности платным компиляторам . Тогда мы и завязались на GNU. Завязавшись на него, захотелось пользовать весь функционал, а конкретно передачу многомерных массивов по аргументам функций (чтоб от Фортрана отказаться) и использование вложенных функций. Наличие этих возможностей позволяло писать программы и с большей скоростью отладки и с большой скоростью работы по сравнению с остальными компиллерами, которые этот функционал не поддерживали. Была надежда, что этот функционал рано или поздно появится в стандарте С и его начнут поддерживать остальные компиляторы. Вот долгое время наш софт так в GNU сидел и продолжал развиваться.
Сейчас надо перейти на стандарт языка (хоть С, хоть С++), чтоб можно было с легкостью менять платформы и компиляторы, поэтому переползаю с GNU-C на современный стандарт С++. Так как в пакете пол миллиона строк кода на С и с десяток тысяч строк остатков на фортране, приходится много раз отмерять, прежде чем отрезать.