LINUX.ORG.RU

Что нужно добавить в C?

 


0

5

Расскажите, что бы вы хотели добавить в си? Только то, что реально можно добавить, не делая при этом новый язык. Просто фичи, которых не хватает.

Или покритикуйте мой список.

  • Константы. #define - препроцессор, const не работает полноценно в compile-time, enum только для целых и вообще для другого .
  • Лямбды (анонимные функции) - для удобства коллбеков. Можно без замыканий, т. к. они много скрывают.
  • Модули, если возможно. Для изоляции единиц трансляции.
  • Интроспекция (typeof, хотя бы) - для обобщенного программирования.
  • Более развитая макросистема - для того же. Например, возможность макросы раскрывать в директивы препроцессора.
  • Пространства имен, чистые функции, switch по составным типам, case с диапазоном - для сокращения кода.
  • Аналоги volatile и restrict с более точным контролем - для микрооптимизации.
  • Доступ к стеку вызовов, goto между функциями - для трюков типа трамплинов.
  • В стандартной библиотеке - строки, контейнеры, foreach, большие числа. Возможно, сокеты.
★★★★
Ответ на: комментарий от ozkriff

А зачем несколько значений из функции возвращать? Что бы ошибку вернуть?

В основном да, но еще и для того, чтобы не делать структуру с 2-3 полями просто для возврата результатов.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Мне больше нравится подход ржавчины (ну и хаскеля и т.п.), когда результат оборачивается в Option или Result

http://static.rust-lang.org/doc/master/std/option/enum.Option.html http://static.rust-lang.org/doc/master/std/result/enum.Result.html

А «структура с 2-3 полями просто для возврата результатов» обычно плохой звоночек о том, что ты делаешь чего-то странное, как мне кажется.

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

Мне больше нравится подход ржавчины (ну и хаскеля и т.п.), когда результат оборачивается в Option или Result

А вот это будет и правда другой язык.

А «структура с 2-3 полями просто для возврата результатов» обычно плохой звоночек о том, что ты делаешь чего-то странное, как мне кажется.

ЕМНИП, в том Rust есть такое:

fn foo() -> (bar, bazz);

А еще Python, Go, и даже Си++ (tuple и tie). Так что... плохим или странным это кажется только тем, кто этим не пользовался. Тем, кто пользовался, плохим кажется наличие более 3-х полей в кортеже %)

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

А вот это будет и правда другой язык.

Ну я и не считаю, что надо «допиливать» Си. Надо оставить его в виде С89 и не писать на нем ничего нового, только сопровождать старое. А для нового кода брать новые языки, не испорченные обратной совместимостью с Си.

fn foo() -> (bar, bazz);

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

Функция, возвращающая 2 сущности, это по определению должно настораживать. Может я и не прав, можешь показать пример близкий к реальности?

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

В большинстве мест, где лично я это встречал, оно было не к месту и было быстрохаком.

Что плохого или неуместного в быстрохаке?

можешь показать пример близкий к реальности?

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

tailgunner ★★★★★
()
Ответ на: комментарий от ozkriff

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

Где это есть в адекватном виде кроме CL?

P.S. Структура с полями как возвращаемое значение - не то

yoghurt ★★★★★
()
Ответ на: комментарий от tailgunner

Что плохого или неуместного в быстрохаке?

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

в Go возврат двух значений вообще сделали соглашением

Go странно приводить как пример. Были бы у них обобщения и ADT, соглашением было бы использовать тот же Maybe, Option или что подобное.

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

быстрохак потом обычно не заменяется нормальным решением

Быстрохак сам по себе может быть нормальным решением.

Отчаяние, смерть, тлен, забвение. Я это как-то так вижу.

Я в своем коде этого не вижу %)

Go странно приводить как пример. Были бы у них обобщения и ADT

Если в Си добавить ADT, это будет тупо другой язык - хедпост это отметает. И я не только Go привел в пример.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Ну почему же, нужен. Я вот как-то купил книжку на Амазоне наобум, просто по рекомендациям, а там внутри бац! CL.

yoghurt ★★★★★
()
Ответ на: комментарий от LongLiveUbuntu

Кортежи в помощь.

В Си уже есть кортежи? Если нет, то как именно они помогут?

Исключения же.

Усложнение рантайма.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

В Си кортежей нет, а вот в Скале есть. Очень удобная штука.

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

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от LongLiveUbuntu

Кортежи в помощь.

В Си уже есть кортежи?

В Си кортежей нет

Тогда я не понял, к чему твое «в помощь».

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

Сдается мне, что такой подход быстро устаревает. Функция возвращает объект алгебраического типа Data | Error, и он разбирается паттерн матчингом или монадическими конструкциями.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Функция возвращает объект алгебраического типа Data | Error, и он разбирается паттерн матчингом или монадическими конструкциями.

Это же уныло. Результат каждого вызова паттернматчингом проверять чтоли? Исключения гораздо удобнее, очевидно.

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Результат каждого вызова паттернматчингом проверять чтоли?

Фразу «монадическими конструкциями» ты пропустил?

do_!(a bind foo(),
     b bind bar(),
     c bind a + b)

%)

Исключения гораздо удобнее, очевидно.

Лет 10 назад (и даже 5) я тоже так думал...

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

Использование Option[T] дает предсказуемый константный оверхед — то есть, критерии реалтайма удовлетворены. С реализацией же исключений на конкретной платформе придется гадать и молиться.

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

Фразу «монадическими конструкциями» ты пропустил?

Видимо да. Но ведь это уже совсем другая история!

yoghurt ★★★★★
()
Ответ на: комментарий от tailgunner

В ржавчине сейчас try! макрос, вроде, в таких местах используется. Тоже жить вполне можно:

fn readlines(filename: &str) -> Result<Vec<~str>, IoError> {
  let file = try!(files::open(filename));
  let body = try!(file.read());
  body.split('\n').collect()
}

http://static.rust-lang.org/doc/master/std/macros/macro.try.html

ozkriff
()
Ответ на: комментарий от yoghurt

Эм, а под «адекватным видом» ты что подразумеваешь?

Про «настораживаюсь, когда такое вижу» - это, в моем случае, в питонячих скриптах всяких.

ozkriff
()
Ответ на: комментарий от tailgunner

это будет тупо другой язык

Ну так мы, вроде, не только про идеи unsigned`а, а как вообще в языках.

И я не только Go привел в пример.

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

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

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

Ну так мы, вроде, не только про идеи unsigned`а, а как вообще в языках.

Не, я конкретно про доработки Си, при которых он остается Си. Если «вообще» - Rust выглядит неплохо.

http://static.rust-lang.org/doc/master/std/macros/macro.try.html

Мне категорически не нравятся макросы, у которых внутри return.

значений запихнуто не много.

Да. 3 значения уже может быть слишком много.

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

Если бы к элементам структуры можно было обращаться по индексам

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

tailgunner ★★★★★
()
Ответ на: комментарий от Dark_SavanT

А поясни для знающих питон сильно поверхностно?

def foo():
  return 1, 2

one, two = foo() # это распаковка

one = foo()[0] # доступ по индексу
tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Понял, хоть никогда и не пользовался. Такой вариант реально даёт преимущества перед возвращением tuple?

Dark_SavanT ★★★★★
()
Ответ на: комментарий от Dark_SavanT

Такой вариант реально даёт преимущества перед возвращением tuple?

Ммм... в Python это и есть возвращение кортежа. Вопрос в том, что возвращенное значение можно распаковать или доступаться к полям по индексу.

tailgunner ★★★★★
()
Ответ на: комментарий от yoghurt

Где это есть в адекватном виде кроме CL?

А там в адекватном? А то вызвал такую функцию — оно само отвалилось если не подобрал костылём (multiple-value-bind), чтобы передать все возвращённые values аргументами в другую функцию — опять прочие костыли (*-call, *-list и что там ещё). tuple с operator MainType() в C++ и то лучше себя вести будет :)

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

А то вызвал такую функцию — оно само отвалилось если не подобрал костылём (multiple-value-bind)

Это с какой стороны посмотреть. Можно брать значение из функции, как всегда, если нам нужно только первое, или юзать multiple-values-bind, если нам надо больше. Не вижу проблемы, на мой взгляд очень удобно

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Это с какой стороны посмотреть.

Наверно в динамическом языке с множеством неявных преобразований это может быть нормально.

В си тоже хватает implicit преобразований, но это тоже не есть хорошо, по крайней мере, в C++ их частью отменили и в остальном не рекомендуют злоупотреблять.

template <typename T, typename /*...*/ Ts>
struct Values {
    T main;
    Ts rest;
    operator T() { return main; } // implicit!
};
quasimoto ★★★★
()
Ответ на: комментарий от yoghurt

Ну и вопрос производительности — нам всё кидать (может там multiple-value-bind) или только часть (нет)? С явными (+ с неявными преобразованиями ?) кортежами это регламентируется типами.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

+ с неявными преобразованиями?

Всё не пойму о каких неявных преобразованиях речь? Это ж не JS/PHP.

korvin_ ★★★★★
()
Ответ на: комментарий от tailgunner

чтобы не делать структуру с 2-3 полями просто для возврата результатов.

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

DeVliegendeHollander ★★
()
Ответ на: комментарий от korvin_

Это было сказано про C++. Что такое implicit type conversions в C++? http://www.cplusplus.com/doc/tutorial/typecasting, http://en.cppreference.com/w/cpp/language/implicit_cast.

Ещё было сказано

в динамическом языке с множеством неявных преобразований

Это все неявные преобразования на решётке типов, очевидно. В данном случае хотя бы (values main-type ...) -> main-type (возвращаем первое, принимаем второе, всё преобразовывается). А вообще — преобразования в numerical tower, поднятия в t и т.п.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

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

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

в рабочем коде мне нельзя использовать С++11 :(

Эта фишка C++11 реализована в GNU C++ уж лет 5 назад; думаю, что в MSVS (или что там у тебя) примерно тогда же.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Мне категорически не нравятся макросы, у которых внутри return.

Разве данный конкретный макрос на практике чем-то сильно хуже do-синтаксиса?

ozkriff
()
Ответ на: комментарий от tailgunner

эта фишка C++11 реализована в ... уж лет 5 назад

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

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

Мне категорически не нравятся макросы, у которых внутри return.

Разве данный конкретный макрос на практике чем-то сильно хуже do-синтаксиса?

Вопрос «хужести» философский. do_! вырабатывает значение, try! - либо вырабатывает значение, либо выполняет нелокальный переход. Что проще понять?

tailgunner ★★★★★
()
Ответ на: комментарий от ozkriff

А какая альтернатива?

Вообще, то что все эти вещи возвращают итераторы обусловлено тем, что иначе не получится продолжить работу с некоторого места без повторного обхода (Степанов критиковал подобные функции в некоторых других библиотеках коллекций — мол, возвращают тупо какую-нибудь bool или value), в случае std::map возвращается пара — итератор _и_ индикатор «вставили новый» / «есть старый», это позволяет коду использующему такой API делать разные хитрые вещи (с меньшим количеством работы / обходов), которые иначе сделать бы не получилось. operator* итератора тоже возвращает value_type, который пара (опять же — как иначе? могут быть нужны ключ _и_ значение).

quasimoto ★★★★
()
Ответ на: комментарий от ozkriff

Ещё из <algorithm>:

http://www.cplusplus.com/reference/algorithm/equal_range/

http://www.cplusplus.com/reference/algorithm/minmax/

http://www.cplusplus.com/reference/algorithm/minmax_element/

(поиск minmax эффективнее поиска min с последующим поиском max)

В Haskell:

http://www.haskell.org/hoogle/?hoogle=(a, b)&start=1000

И т.п. что он не нашёл:

http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.6.0.1/Data-List....

http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.6.0.1/Control-Ar...

Функции с общим доменом объединяются в функцию из общего домена в кортеж:

newtype Lens a b = Lens { _lens :: a -> (b, b -> a) }
newtype Iterator c i e = Iterator { _iter :: c -> (i, i, i -> (Maybe e, i)) }

И даже

newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))

Так что, можно сказать, в хаскеле только и делаешь, что возвращаешь кортежи из функций, явно или нет :)

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Вот ведь ты разошелся. Ок, ок, я не против разумного использования кортежей-пар)

ozkriff
()
Ответ на: комментарий от tailgunner

Фразу «монадическими конструкциями» ты пропустил?

И чем принципиально функция, возвращающая Data|Error, отличается от функции, возвращающей Data или кидающей Error? Ничем не отличается. Семантически это тот же самый Data|Error.

А вот в реализации монадический подход скорее всего всосёт по скорости работы и объёму кода, при чем всосёт сильно. (Если конечно твой компилятор не обладает естественным интеллектом и способностями ванги, чтобы цепочку монадических вызовов оптимизировать код с стиле крестов.)

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

И чем принципиально функция, возвращающая Data|Error, отличается от функции, возвращающей Data или кидающей Error?

Зависит от того, что именно является принципиальным для тебя.

Семантически это тот же самый Data|Error.

Data | Error - это тип, Data и исключение Error - это НЕХ.

А вот в реализации монадический подход скорее всего всосёт по скорости работы

Для этого нет никаких причин. А вот исключения да, всасывают по скорости.

и объёму кода

При наличии удобных конструкций - нет, и даже примитивный макрос do_ это показывает.

при чем всосёт сильно

Какая уверенность. На чем она основана?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Data | Error - это тип, Data и исключение Error - это НЕХ.

Внезапно, это тоже тип. Дебилом перестаём прикидываться, ага?

Для этого нет никаких причин. А вот исключения да, всасывают по скорости.

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

При наличии удобных конструкций - нет, и даже примитивный макрос do_ это показывает.

Ага, «примитивный макрос do_» обильно обмазывает всё синтаксическим сахаром, и внезапно tailgunner начинает считать, что код, который коротко записывается, будет такой же короткий и быстрый после компиляции.

Какая уверенность. На чем она основана?

На понимании того, в какие машкоды скомпилируется код на исключениях, и в какие - код на монадах. Слушай, меня тут страшная догадка осенила. Может ты не прикидываешься дебилом? Может ЛОР всё-таки необработимо разрушил твой мозг?

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

Data | Error - это тип, Data и исключение Error - это НЕХ.

Внезапно, это тоже тип

Это, разумеется, не так.

«примитивный макрос do_» обильно обмазывает всё синтаксическим сахаром, и внезапно tailgunner начинает считать, что код, который коротко записывается, будет такой же короткий и быстрый после компиляции.

Он будет коротким, да. И хорошо оптимизируемым.

Дебилом перестаём прикидываться, ага?
Дебилом перестаём прикидываться, ага?

«Ты за рулем - ты и поворачивай» (ц)

Слушай, меня тут страшная догадка осенила.

Да ты вечно на нервах весь.

Может ты не прикидываешься дебилом?

Не прикидываюсь.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Он будет коротким, да. И хорошо оптимизируемым.

Ыгы. Верю как родному.

Да ты вечно на нервах весь.

Не вечно, только когда приходится писать из-под *** семерки, где нельзя настроить нормальное переключение *** раскладки. :D

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

Он будет коротким, да. И хорошо оптимизируемым.

Ыгы.

Сравнение и переход.

Верю как родному.

И это правильно.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Сравнение и переход.

В количестве овердохера.

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

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

Сравнение и переход.

В количестве овердохера.

Одно сравнение и один переход на вызов функции.

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

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

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

А вот исключения да, всасывают по скорости.

И это даст офигенный выигрыш в пару процентов производительности

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

Попробуй читать текст до конца:

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

если исключения вообще не возникают

А если возникают - то всё.

tailgunner ★★★★★
()
Ответ на: комментарий от SDSWanderer

нафига ошибку возвращать?

Предложи другое решение для языка, в котором нет исключений.

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