LINUX.ORG.RU

В стандарт C предложено внести лямбды и defer из golang

 , ,


5

6

Привет, ЛОР!

Я тут тебе немного покушать принёс. Как ты, наверное знаешь, не за горами выход нового стандарта языка C – C23. Среди прочих вкусностей, таких как лямбды в стиле C++, в этот стандарт предложено добавить механизм defer, аналогичный существующему в языке Go.

Ссылка на предложение: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2895.htm

В случае, если этот стандарт будет принят, будет возможно написание вот такого кода:

p = malloc(N);
defer { free(p); }

Где аргументом оператора defer является анонимная функция. Так же возможны более сложные варианты использования:

enum { initial = 16, };
double buffer[initial] = { 0 };
...
size_t elements = 0;
double* q = buffer;
defer [orig = q, &q]{ if (orig != q) { free(q); }};
...
// increase elements somehow
...
// adjust the buffer
if (elements > initial) {
    double* pp = (q == buffer) ? malloc(sizeof(double[elements])) : realloc(q, sizeof(double[elements]));
    if (!pp) return EXIT_FAILURE;
    q = pp;
}
...

Учитывая всё это, скоро в C больше не будет нужно использовать goto вообще нигде, даже для очистки ресурсов при ошибке. Так заживём, ЛОР!

👍👍👍👍👍

Так же очень хотелось бы узнать мнение об этих предложениях от таких известных экспертов по языку C как Царь и @Iron_Bug.

hateyoufeel 👍👍👍👍👍
() автор топика
Ответ на: комментарий от fsb4000

Безусловно! Другой вопрос, что это всё давно есть в C++. Зачем нужны два языка, в которые добавляют одни и те же фичи, только с разницей в 10 лет? Я не понимаю.

Ну и всякие русты это тоже умеют.

hateyoufeel 👍👍👍👍👍
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 1)
defer [q]{ free(q); };
defer [&q]{ free(q); };
defer [qp = &q]{ free(*qp); };
defer [=]{ free(q); };
defer [&]{ free(q); };

Ух, я прям чувствую, как упростилось освобождение ресурсов и резко снизилась вероятность ошибок!

thesis 😊😊😊😊😊
()

Ого, прикольно как работает конкуренция, когда растовики напирают с небезопасно, в си предлагают подобный стандарт.

fernandos
()

Ну не. Сишечка должна оставаться говном. Паттерн goto для чистки ресурсов перед выходом был одной из целью моего говномётства в сишку. Не надо фиксить это.

ox55ff 🤡🤡🤡🤡🤡
()

С таким инопланетянским синтаксисом - не нужно. Ну а вообще - тоже сомневаюсь что нужно, это неявные действия, отсутствие которых - одно из главных достоинств Си. В С++ такого полно, из-за чего там проблемы с longjmp например и сильно запутанная механика отправки исключений. Но то С++, оставим это ему.

Учитывая всё это, скоро в C больше не будет нужно использовать goto вообще нигде, даже для очистки ресурсов при ошибке. Так заживём, ЛОР!

Во-первых, goto используется далеко не только для очистки ресурсов при ошибке. Во-вторых, кто боится goto идите в свои js. goto это хороший инструмент, нативно существующий в любом проце, и он обязан быть в любом нормальном языке программирования.

firkax ☕☕☕☕☕
()
Последнее исправление: firkax (всего исправлений: 1)

Какое-то кромешное ненужно. Лучше бы строки в case завезли, например.

Тому, кто неспособен использовать goto грамотно, никакой defer не поможет, да и вообще сишечка противопоказана. А тому кто способен, никакой defer нафиг не нужен.

Фишка сишечки в том, что в ней нет никаких скрытых механизмов. Прямая трансляция program flow в instruction flow. Если начать туда напихивать всякое, то внезапно получится C++, который уже есть.

Ну и «лямбды» - это не лямбды вовсе, а банальные безымянные функции. Настоящие лямбды, AKA генераторы функций, которые создают новый код в момент вызова, я только в лиспе встречал, да и то не во всех реализациях.

Stanson ☕☕☕☕☕
()
Ответ на: комментарий от fsb4000

Надеюсь что комитет одобрит и примет их в С23.

Вобщем-то плевать что там одобрит комитет. Полезные фичи давно бы были в gcc безо всяких комитетов и бумагомарательства.

firkax ☕☕☕☕☕
()
Ответ на: комментарий от firkax

Полезные фичи давно бы были в gcc безо всяких комитетов и бумагомарательства.

Были бы, если бы gcc не перешёл на C++, и потому забил болт на устаревший С.

Зачем им развивать gnu C, если gcc разрабатывается на C++?

Вот поэтому новых фич можно ждать только от комитета C.

fsb4000 👍
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от olelookoe

Если очень нужно, то у gcc|clang есть же __attribute__((cleanup))

anonymous
()
Ответ на: комментарий от Stanson

Лучше бы строки в case завезли, например.

Эм, нет.

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

Сравнение строк (strcmp) - это функция библиотеки, и хотя компилятор может её генерировать сам, но лишь в порядке оптимизации. Неявно вызывать её из синтаксических конструкций ни в коем случае нельзя.

Ну и switch() это не синтаксический сахар над каскадными if-ами, если что, а штука для использования таблиц переходов. Со строками это опять же невыполнимо.

firkax ☕☕☕☕☕
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от fsb4000

Зачем им развивать gnu C, если gcc разрабатывается на C++?

По заказу от авторов другого GNU софта, написанного на Си. В первую очередь glibc.

Вот поэтому новых фич можно ждать только от комитета C.

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

firkax ☕☕☕☕☕
()
Ответ на: комментарий от firkax

Я вот недавно удивлялся, почему в case указатели нельзя использовать.

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

Строковые константы вполне может обрабатывать компилятор на этапе компиляции и соответственно использовать strcmp, например, для сравнения.

Но это не больше чем синтаксический сахар, просто чтобы вместо лесенок с strcmp и скобкотой писать несколько более удобочитаемые case, только и всего.

Ну и switch() это не синтаксический сахар над каскадными if-ами, если что, а штука для использования таблиц переходов. Со строками это опять же невыполнимо.

Да неужели. Таблицы переходов со строками ещё в софте для ZX Spectrum использовались как нефиг делать. Да и case далеко не всегда в таблицы переходов компилируется.

Stanson ☕☕☕☕☕
()
Последнее исправление: Stanson (всего исправлений: 2)
Ответ на: комментарий от Stanson

Прямая трансляция program flow в instruction flow.

Ахахахахах! Особенно, когда целые функции превращаются в nop. Прямо прямее некуда.

hateyoufeel 👍👍👍👍👍
() автор топика
Ответ на: комментарий от Stanson

Фишка сишечки в том, что в ней нет никаких скрытых механизмов

Родной, ты memory barriers в глаза видел вообще?

anonymous
()

В случае, если этот стандарт будет принят, будет возможно написание вот такого кода:

с defer вопрос в том - вычислим ли статически какой именно defer надо выполнять, например

if(a) defer{...}
else defer{...}

не знаю как они его введут, но в данном случае статически неясно, как именно defer пускать потом.

если они такое разрешат, то им придется толкать актуальный defer в рантайме куда-то(типа стека), и потом извлекать по выходу из блока, но это уже как бы не сишечка, а похоже на завершение блока в с++. скорее всего статически невычислимые defer они запретят, ибо сложно.

alysnix ☕☕☕☕☕
()
Ответ на: комментарий от hateyoufeel

Ахахахахах! Особенно, когда целые функции превращаются в nop. Прямо прямее некуда.

Оптимизация - не, не слыхали? a |= 0 только какой-нибудь пистон или жабоскрипт реально выполнять будет.

Stanson ☕☕☕☕☕
()
Ответ на: комментарий от anonymous

Родной, ты memory barriers в глаза видел вообще?

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

Stanson ☕☕☕☕☕
()
Ответ на: комментарий от hateyoufeel

У тебя есть два стула.

Нет, стульев ммиллион. И из всех них я скорее предпочту сишечку, чем какие-нибудь вычурные модные стулья для модных ребят с прикрученными дилдами типа растов, го или пистонов.

Stanson ☕☕☕☕☕
()

Учитывая всё это, скоро в C больше не будет нужно использовать goto вообще нигде, даже для очистки ресурсов при ошибке.

Так он и ныне в алгоритмах редко используется.
Но иногда нужен …

anonymous
()
Ответ на: комментарий от Stanson

Да нет, ты просто сам себе противоречишь. «Прямая трансляция program flow в instruction flow» исключает «оптимизации», которые, например, выкидывают вызовы memset() для зануления криптоключей в памяти.

hateyoufeel 👍👍👍👍👍
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от Stanson

то всё прекрасно пишется, компилируется и работает.

Ты пробовал? Я вот попробовал, мне gcc ошибку выдал, пришлось переделывать на if-ы.

error: switch quantity not an integer
error: pointers are not permitted as case values

Просто в case же придётся писать константы, т.е. конкретные адреса, которые, очевидно, на момент написания программы неизвестны.

Причём тут известны-неизвестны? Указатель это просто некое перечислимое значение размером, зависящим от архитектуры. Там не обязательно даже должен быть валидный адрес, это нужно только для разыменования. У меня вполне себе константа ((char*)1) была в качестве case.

Строковые константы вполне может обрабатывать компилятор на этапе компиляции и соответственно использовать strcmp, например, для сравнения.

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

Но это не больше чем синтаксический сахар, просто чтобы вместо лесенок с strcmp и скобкотой писать несколько более удобочитаемые case, только и всего.

В том и дело, что switch не для этого. switch не вызывает никакие функции сравнения (тем более - неявно) и не проверяет (по возможности) каждый case по-очереди, а ищет нужный оптимальным способом через таблицы. Ну, если бы был какой-нить variable_switch с логикой как описываешь ты (только strcmp ему нужно передавать аргументом, а не подразумевать неявно) - бы наверно не возражал. Например так:

variable_switch(st, strcmp) {
case "a": ...;
}
Разумеется, он годился бы не только для строк, а вообще для любых объектов, к которым ты сделаешь функцию сравнения. Кстати, хорошая идея, надо будет в своём компиляторе реализовать, еслия его доделаю когда-нить.

Да неужели. Таблицы переходов со строками ещё в софте для ZX Spectrum использовались как нефиг делать. Да и case далеко не всегда в таблицы переходов компилируется.

Каким обрабом ты собрался делать таблицу перехода для строк? Для целых чисел это примерно так:

switch(i) {
case 1: ... ;
case 2: ... ;
}
->
void * jmptable[] = { [1] => &case1, [2] => &case2, ... }
goto jmptable[i];

Разумеется, если значения case друг от друга далеко, одной таблицей не обойтись, а иногда и вообще приходится делать фолбек до каскада if-ов. Но со строками таблицу вообще не выйдет сделать никак, потому что для каждого case надо вызывать strcmp. В таблицу можно только записать соответствие строка - адрес, но она тут ничего уже не ускорит.

firkax ☕☕☕☕☕
()
Ответ на: комментарий от hateyoufeel

Если какой-нибудь жёсткий оптимизатор выкидывает memset для статического массива инициализированного при определении нулями, то не вижу в этом никаких проблем. На то оптимизатор и нужен, чтобы в том числе выкидывать бессмысленный код или код который никогда не будет выполняться.

Если оптимизатор выкидывает что-то реально нужное, то это просто плохой, негодный оптимизатор, и его использовать не нужно.

Stanson ☕☕☕☕☕
()
Ответ на: комментарий от hateyoufeel

Ничего он не противоречит. Джаваскриптерам просто не понять.

firkax ☕☕☕☕☕
()

Пусть что хотят делают.
Лишь бы совместимость была …

anonymous
()
Ответ на: комментарий от firkax

error: pointers are not permitted as case values

Разумеется.

Написал бы

case( ptr )
{
    case 0xC0000000: ... break;
    case 0xC0000010: ... break;
}

никаких ошибок не было бы.

Но со строками таблицу вообще не выйдет сделать никак, потому что для каждого case надо вызывать strcmp.

Да-да, конечно-конечно.

case( string )
{
    case "measure": ... break;
    case "reset": ... break;
    case "calibrate": ... break;
}

=>

char *commands[] = { "measure", "reset", "calibrate" };
void *jmptable[] = { &case0, &case1, &case2 };
for( i = 0; i < sizeof(commands); i++ )
    if( !strcmp( string, commands[i]) )
        goto jmptable[i] 

Вообще никаких проблем. Реализовать это в компиляторе совсем не сложно, благо strcmp/memcmp обычно вообще builtin.

Stanson ☕☕☕☕☕
()
Последнее исправление: Stanson (всего исправлений: 2)

Я думаю они не примут из-за консервативности языка.

mxfm
()
Ответ на: комментарий от Heartbreak_Kid

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

Bad_ptr ☕☕
()

ИМХО поддержка векторных операций не помешала бы …

anonymous
()

В стандарт C предложено внести лямбды и defer из golang

Не нужно! …

anonymous
()
Ответ на: комментарий от Stanson

Разумеется.
Написал бы

Во-первых, там если что была вторая ошибка «switch quantity not an integer». Во-вторых, ты хочешь делать свитч по указателю а в кейсах ставить int? В-третьих, можно конечно сделать switch((uintptr_t)p) { 0x1234: ; } но это не свитч по указателю, а его эмуляция. Речь шла как раз про то, почему напрямую указатели в свитч не даёт компилировать - я предположил, что из-за того, что некоторые туда начнут сувать строки (и другие объекты), не понимая, что сравниваться буду адреса, а не содержание по ним.

Вообще никаких проблем. Реализовать это в компиляторе совсем не сложно, благо

Это не та таблица. В нормальном свитче таблица такая, что по ней сразу видно куда надо перейти. А тут надо каждый кейс просматривать брутфорсом, пока не найдёшь нужный, то есть это полный аналог каскада if-ов. Это кардинальная разница и обобщать оба случая на один и от же оператор switch - плохо. Если бы там был не switch а variable_switch или ещё какое другое слово - повторюсь, не возражаю.

strcmp/memcmp обычно вообще builtin.

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

firkax ☕☕☕☕☕
()
Ответ на: комментарий от Bad_ptr

Еще бы полноценные макросы как в лиспе.

Чем тебе define не полноценный?

Ну а вообще, напиши свой препроцессор, это не сложно. А публиковать для остальных можно будет уже прогнанный через него код, таким образом он будет компилироваться у всех.

firkax ☕☕☕☕☕
()
Ответ на: комментарий от Shadow

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

firkax ☕☕☕☕☕
()

Лучше бы ranges в switch завезли. И нормальные циклы, чтобы можно было написать цикл от a до b, и он просто работал, даже если a и b экстремальные значения для типа.

unC0Rr
()
Ответ на: комментарий от firkax

Чем тебе define не полноценный?

Оно как шаблоны «Иммутабельно», поэтому отпадает куча возможностей.

напиши свой препроцессор, это не сложно

А если делать на анафорических лямбдах, то вообще задачка на 5 минут. Если для тебя это так легко, то напиши, плиз.

Bad_ptr ☕☕
()
Последнее исправление: Bad_ptr (всего исправлений: 1)
Ответ на: комментарий от Bad_ptr

Не знаю что такое анафорические лямбды, но я не очень понял что именно ты хочешь (привёл бы пример на си), и совсем не уверен что я разделю твоё желание иметь такие макросы, когда таки пойму.

firkax ☕☕☕☕☕
()

Возможно я видел исключительно говнокод на си, но там куда ни плюнь было goto, а то и вовсе long jump. С такой культурой программирования, вводить в язык defer на скоупах,.. ну такое.

filosofia 👍
()
Ответ на: комментарий от Stanson

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

char *key = fetch_key();
chat *data = decrypt_data(key);
memset(key, 0, SIZEOF_KEY);
free(key);
return data;

Вот здесь вызов memset() будет почти наверняка выкинут, потому что «нет побочных эффектов». Ну, кроме потенциальной утечки ключа, разве что. Но с каких пор господ волнует безопасность?

Если оптимизатор выкидывает что-то реально нужное, то это просто плохой, негодный оптимизатор, и его использовать не нужно.

Это gcc. Я тоже думаю, что его надо выкинуть, но многие со мной не согласны.

hateyoufeel 👍👍👍👍👍
() автор топика
Ответ на: комментарий от Stanson

Написал бы

case( ptr ) { case 0xC0000000: … break; case 0xC0000010: … break; } никаких ошибок не было бы.

Это UB. Поздравляю, ты себе яйца отстрелил.

hateyoufeel 👍👍👍👍👍
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.