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 ★★★★★
()

Я надеюсь комитет перестанет пинать и все-таки начнет принимать хорошие предложения.

a1batross ★★★★★
()

С таким инопланетянским синтаксисом - не нужно. Ну а вообще - тоже сомневаюсь что нужно, это неявные действия, отсутствие которых - одно из главных достоинств Си. В С++ такого полно, из-за чего там проблемы с 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 ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.