Благодаря усилиями команды разработчиков языка у D теперь появилось совместимое с чистым Си подмножество обладающее не только привычным для любого программиста Си синтаксисом, но и значительно расширяющее функционал языка. Новое подмножество называется «betterC». Это подмножество позволяет перевести написание Си приложение на новый уровень.
Вот краткий список того, что позволяет режим betterC программисту:
1. Отсутствие препроцессора
2. Более высокая скорость компиляции
3. Полноценная модульность
3.1 Поддержка различных видов импорта (статический импорт, частичный импорт, ренейминг при импорте и тд)
4. В момент компиляции возможна: генерация кода, интроспекция, проверка различных условий
4.1 Ветвление на этапе компиляции через
static if
и static foreach
4.2 возможны работа блоков вида
version(linux) { ... }
5. Шаблоны
6. Аналог borrow checking из Rust через
scope pointers (scope T*)
, scope slices (scope T[])
и scope references (scope ref T)
7. Поддержка модификаторов доступа
const
и immutable
подробнее8. TLS по умолчанию
9. Поддержка контрактного программирования
10. Удобные массивы с поддержкой слайсинга
11. Ускорение работы с массивами за счет SIMD
12. Удобные unit-тесты
13. Встроенный профайлер
14. User-defined атрибуты
15. Встроенный и очень удобный генератор документации
16. Привычный каждому программисту Си синтаксис
17. Поддержка Unicode
18. Лучшая чем у C memory safety
19. Замыкания
20. Многое другое.
Давайте теперь рассмотрим примеры.
Следующий код на C:
#include <stdio.h>
int main(int argc, char** argv) {
printf("hello world\n");
return 0;
}
Будет скомпилирован в:
_main:
push EAX
mov [ESP],offset FLAT:_DATA
call near ptr _printf
xor EAX,EAX
pop ECX
ret
Размер на выходе: 23,068 bytes.
Теперь тоже самое на D в режиме betterC:
import core.stdc.stdio;
extern (C) int main(int argc, char** argv) {
printf("hello world\n");
return 0;
}
Размер на выходе: 23,068 bytes.
Не плохо правда ли? Тот же самый код без режиме betterC будет весить 194Kb.
Более сложный пример:
C версия:
#include <stdio.h>
/* Eratosthenes Sieve prime number calculation. */
#define true 1
#define false 0
#define size 8190
#define sizepl 8191
char flags[sizepl];
int main() {
int i, prime, k, count, iter;
printf ("10 iterations\n");
for (iter = 1; iter <= 10; iter++) {
count = 0;
for (i = 0; i <= size; i++)
flags[i] = true;
for (i = 0; i <= size; i++) {
if (flags[i]) {
prime = i + i + 3;
k = i + prime;
while (k <= size) {
flags[k] = false;
k += prime;
}
count += 1;
}
}
}
printf ("\n%d primes", count);
return 0;
}
BetterC версия на D:
import core.stdc.stdio;
extern (C): // указываем что надо использовать соглашение о вызове в стиле C
__gshared bool[8191] flags;
int main() {
int count;
printf("10 iterations\n");
foreach (iter; 1 .. 11) {
count = 0;
flags[] = true;
foreach (i; 0 .. flags.length) {
if (flags[i]) {
const prime = i + i + 3; // const - значение никогда не будет меняться после инициализации
auto k = i + prime;
while (k < flags.length) {
flags[k] = false;
k += prime;
}
count += 1;
}
}
}
printf("%d primes\n", count);
return 0;
}
Получилось куда более читабельно и коротко.
Для инициализации пустого проекта вызовите:
dub init
Для включения данного режима в dub.sdl необходимо добавить строку:
dflags "-betterC"
Для dub.json строка будет:
"dflags" : ["betterC"],
Требуется версия компилятора dmd 2.076 и старше.
Для начинающих программистов опубликована первая версия русского учебника по языку D.
Так же вышло новое расширение с поддержкой языка для Visual Code.
Комментарии (140)
lpwaterhouse
25.08.2017 15:04+19Уже 16 лет мучаются, а он так никому и не нужен.
guai
29.08.2017 10:18-1за «никого» не надо говорить.
мне нужен, отличный язык, а альтернативы — или древние сишка с плюсами, в которых чуть ли не каждая изначальная фича успела стать deprecated, или наркоманский руст.0xd34df00d
29.08.2017 22:32Альтернативы по каким критериям? Близость к железу? Императивный язык с сиподобным синтаксисом? Скорость разработки?
kez
25.08.2017 15:41+6Эта вольный перевод статьи D is a Better C, но нужно было хотя бы перевести её полностью, там описаны некоторые важные тонкости.
egordeev
25.08.2017 15:47+5По хорошему, все статьи по поводу языка программирования D напоминают пропаганду! В который раз в этом убеждаюсь!)))) О том, что D — это убийца C++, D — это убийца Rust, D — это убийца C, D — это убийца Go, и т.д. и т.п. Постоянно… сплошное навязывание мнения, что D — лучший язык программирования, но что-то я в этом сомневаюсь.
geekmetwice
25.08.2017 18:58Вы читаете готовые выводы, даже не пытаясь пройти по их логической цепочке. Возьмите ВВЕДЕНИЕ в язык и прочитайте, там обязательно будут отсылки «чем это лучше Си». Тогда все выкрики с задних рядов «ясамниваюсь» уйдут в никуда.
И да, Ди — это лучшее продолжение Си-подобных языков.egordeev
25.08.2017 19:21+2Вы читаете готовые выводы
откуда у вас такие выводы?
Возьмите ВВЕДЕНИЕ в язык и прочитайте
почему вы мне указываете, что я должен прочитать?
Тогда все выкрики с задних рядов «ясамниваюсь» уйдут в никуда.
что-то я сомневаюсь.
И да, Ди — это лучшее продолжение Си-подобных языков.
в этом я тоже сомневаюсь.beduin01 Автор
25.08.2017 21:11-1>почему вы мне указываете, что я должен прочитать?
http://dlang.ru/faq
yarric
25.08.2017 19:34+2Замена C со сборкой мусора? Не думаю!
Wedmer
25.08.2017 19:56Most obviously, the garbage collector is removed, along with the features that depend on the garbage collector. Memory can still be allocated the same way as in C – using malloc() or some custom allocator.
Как то такgrossws
25.08.2017 21:58+2Ага, и минус exceptions, strings и большая часть стандартной библиотеки. Хотя libc останется доступна, да.
yarric
25.08.2017 22:06А в C++ при этом есть RAII и все библиотеки работают и доступны.
Wedmer
25.08.2017 22:32Вообще то тут разговор про C и D в режиме betterC.
В чистом C тоже нет RAII.
В будущем ничто не мешает адаптировать часть библиотек под этот режим.DmitryMry
26.08.2017 17:05+1Комментарий Уолтера Брайта:
RAII in D currently has a soft dependency on the GC. A fix for this is in the works, and then RAII will be available for D as better C.
monah_tuk
28.08.2017 10:17В чистом C тоже нет RAII.
Согласен. Но оно вполне реализуемо, даже в рамках современных компиляторов. Взять для примера GCC:__attribute__((cleanup (...)))
. Думаю, что в VS, clang что-то подобное тоже может быть. Это не призыв использовать, просто констатация: было бы желание, а реализовать, не ломая язык, вполне возможно.Wedmer
28.08.2017 19:46Нельзя привязывать возможности конкретных компиляторов к стандарту. Выше отписали, что как отвяжут RAII от GC, так он появится для нового режима.
monah_tuk
28.08.2017 23:52Не не, я немного не про то. Я про то, что даже для C, если комитет вдруг захочет стандартизовать атрибуты, то вполне себе реализуется аналог RAII.
По поводу привязки конкретных возможностей компиляторов. По сути, это поведение может выступать как Proof of Concept при рассмотрении следующего стандарта.
Wedmer
29.08.2017 00:39IAR тоже многое не по стандарту дает делать. Я не думаю, что это многое войдет в стандарт.
GCC позволяет такое впихивать за счет своего бэкенда, как и clang.
TargetSan
25.08.2017 22:40+1Меня смущает, что сам по себе язык очень сильно завязан на сборку мусора. Многие вещи могли бы без проблем работать, сидя на стеке или кастомных аренах. Те же классы с наследованием, интерфейсы и т.п. Однако — увы.
Кстати, вопрос к знающим Ди. А как там теперь с обработкой ошибок, после того как выкинули на мороз исключения? Потому что C-style коды ошибок и libc в голом виде низводят бОльшую часть преимуществ в ноль.
vintage
25.08.2017 22:44TargetSan
25.08.2017 23:45+1Тут более каверзный вопрос — а как у stdlib с error handling через него?
И есть ли сахар "unpack-or-return"?vintage
26.08.2017 00:09Да никак, нужно обёртки писать.
TargetSan
26.08.2017 21:15Это крайне печально.
К тому же, я подозреваю, Phobos завязан на исключения. А значит, перевести его на variant-совместимый подход — переписать хорошим куском. Либо руками обёртки над libc. Пока напрашивается вывод — в betterC фактически нет стандартной библиотеки.vintage
27.08.2017 00:53Ну, эти обёртки написать — довольно тривиальная задача. Я тут не вижу особой проблемы. Особенно в сравнении с собственно С.
Daniil1979
25.08.2017 16:08Хм… Хренею я с обоих языков. На одном языке вывод одной единственной строчки текста — 23Кб, на другом — 194 Кб. Да Вы зажрались!!!
Надо будет qBasic с компилятором найти на старых дисках и посмотреть, сколько у него готовая прога займёт.
А потом вспомнить, как это делается на ассемблере. :-)lorc
25.08.2017 17:07+1Интересно чем они собирали. У меня hello world собранный GCC5.4.0 занимает 8 килобайт. Стрипнутый — 6 килобайт. В основном — секции с метаданными и информацией для динамической линковки.
В секциях кода — в основном инициализация/деинициализация libc.
Короче, ничего такого страшного. Можно выкинуть всё и дернуть write() руками. Но зачем?
objdump -x для тех, кому интересноSections:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 0000000000400238 0000000000400238 00000238 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 0000000000400254 0000000000400254 00000254 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 0000000000400274 0000000000400274 00000274 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 0000001c 0000000000400298 0000000000400298 00000298 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000060 00000000004002b8 00000000004002b8 000002b8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 0000003d 0000000000400318 0000000000400318 00000318 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 00000008 0000000000400356 0000000000400356 00000356 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000020 0000000000400360 0000000000400360 00000360 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rela.dyn 00000018 0000000000400380 0000000000400380 00000380 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rela.plt 00000030 0000000000400398 0000000000400398 00000398 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 0000001a 00000000004003c8 00000000004003c8 000003c8 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000030 00000000004003f0 00000000004003f0 000003f0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt.got 00000008 0000000000400420 0000000000400420 00000420 2**3
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 00000192 0000000000400430 0000000000400430 00000430 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 00000009 00000000004005c4 00000000004005c4 000005c4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .rodata 00000010 00000000004005d0 00000000004005d0 000005d0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame_hdr 00000034 00000000004005e0 00000000004005e0 000005e0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .eh_frame 000000f4 0000000000400618 0000000000400618 00000618 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .init_array 00000008 0000000000600e10 0000000000600e10 00000e10 2**3
CONTENTS, ALLOC, LOAD, DATA
19 .fini_array 00000008 0000000000600e18 0000000000600e18 00000e18 2**3
CONTENTS, ALLOC, LOAD, DATA
20 .jcr 00000008 0000000000600e20 0000000000600e20 00000e20 2**3
CONTENTS, ALLOC, LOAD, DATA
21 .dynamic 000001d0 0000000000600e28 0000000000600e28 00000e28 2**3
CONTENTS, ALLOC, LOAD, DATA
22 .got 00000008 0000000000600ff8 0000000000600ff8 00000ff8 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .got.plt 00000028 0000000000601000 0000000000601000 00001000 2**3
CONTENTS, ALLOC, LOAD, DATA
24 .data 00000010 0000000000601028 0000000000601028 00001028 2**3
CONTENTS, ALLOC, LOAD, DATA
25 .bss 00000008 0000000000601038 0000000000601038 00001038 2**0
ALLOC
26 .comment 00000034 0000000000000000 0000000000000000 00001038 2**0
CONTENTS, READONLY
SYMBOL TABLE:
no symbols
Wedmer
25.08.2017 21:47В статье пишется, что оба варианта привета транслируются в одинаковый бинарный код.
Но если отключить новую фичу — получается 194кб. Да, и дело то не в языках, а компиляторах.
TargetSan
25.08.2017 16:14+1В оригинальной статье был ещё небольшой раздельчик, что выкинули. В частности:
- RAII
- Полиморфные классы (а значит, весь полиморфизм будет ручками на указателях)
Вопрос — зачем он теперь такой нужен?
vintage
25.08.2017 21:20Некоторый полиморфизм в D есть и у структур:
import std.stdio; void sayA( A a ) { writeln( "a = " , a ); } void sayB( B b ) { writeln( "b = " , b ); } struct A { int a = 1; } struct B { A a = A(2); alias a this; int b = 3; } void main(){ A().sayA; // a = A(1) B().sayA; // a = A(2) B().sayB; // b = B(A(2), 3) }
TargetSan
25.08.2017 22:20Я вас огорчу, это не полиморфизм. Это, по сути:
struct A { int a = 1; }; struct B: public A { B() { a = 2; } b = 3; }; void sayA(A const& a) { std::cout << "a = " << a.a << "\n"; } void sayB(B const& b) { std::cout << "a = " << b.a << "\nb = " << b.b << "\n"; } int main(int, char**) { sayA(A()); // a = 1 sayA(B()); // a = 2 sayB(B()); // a = 2 b = 3 }
vintage
25.08.2017 22:39Интересно, что же вы такое вкладываете в понятие полиморфизма?
TargetSan
25.08.2017 22:55+2Эмм… оперирование разнородными типами через некий единый интерфейс?
trait WhoAmI { fn say(&self); } struct Cat; struct Dog; impl WhoAmI for Cat { fn say(&self) { println!("I'm a cat! I say Meow!"); } } impl WhoAmI for Dog { fn say(&self) { println!("I'm a dog! I say Woof!"); } } fn identify_one<T: WhoAmI>(someone: &T) { someone.say(); } fn main() { identify_one(Cat); // cat's phrase identify_one(Dog); // dog's phrase }
То, что вы показали — по сути использование объекта-потомка вместо объекта-предка. Только в Ди реализованное через алиасинг. Вот если бы у вас
B().sayA; // a = B(A(2), 3)
это был бы полиморфизм
Wedmer
25.08.2017 22:57-3Тогда вы должны просто обожать PHP.
TargetSan
25.08.2017 23:51+2Я как раз к языкам со слабой типизацией стараюсь пореже подходить.
По поводу же конца моего комментария — суть в том, что функция не меняет поведения в зависимости от полного входного типа. Поэтому полиморфизмом там как бы и не пахнет.Wedmer
26.08.2017 00:23+2Да ладно?
Освежите ваши знания для начала)
- Ad hoc polymorphism: when a function denotes different and potentially heterogeneous implementations depending on a limited range of individually specified types and combinations. Ad hoc polymorphism is supported in many languages using function overloading.
- Parametric polymorphism: when code is written without mention of any specific type and thus can be used transparently with any number of new types. In the object-oriented programming community, this is often known as generics or generic programming. In the functional programming community, this is often shortened to polymorphism.
- Subtyping (also called subtype polymorphism or inclusion polymorphism): when a name denotes instances of many different classes related by some common superclass.[3] In the object-oriented programming community, this is often referred to as simply Inheritance.
Также прошу не забывать, что в PHP есть функционал позволяющий делать нечто вроде перегрузки функций в классах.
vintage
26.08.2017 00:17+1Вы, пожалуйста, не выдавайте ваше личное видение полиморфизма, за общепринятое.
Вот если бы у вас
Да пожалуйста:
import std.stdio; void say( A )( A a ) { writeln( a ); } struct A { int a = 1; } struct B { int b = 2; } void main(){ A().say; // A(1) B().say; // B(2) }
dlinyj
25.08.2017 17:06Результаты голосования лучше говорят, чем сама статья.
beduin01 Автор
25.08.2017 17:20Просто 90% проголосовавших на JavaScript пишет..))
Wedmer
25.08.2017 22:16+1К примеру я пишу на плюсах.
Переходить на D не планирую. Может быть буду использовать там, где он будет лучшим инструментом.
Синтаксис Rust меня внешне не радует — слишком непривычно. Но при наличии свободного времени надо будет поковырять поближе, может и пойдет.grossws
25.08.2017 22:25Непривычность синтаксиса — минорщина, не стоящая большого внимания. Существенные отличия в семантике и системе типов ломают мозг и производят куда больше wtf/s.
Тот же borrow checker и lifetimes для многих является эффективно непреодолимым препятствием, хотя в си и плюсах про lifetimes тоже надо думать и держать/проверять их в голове, что куда более трудоёмко, чем поручить это компилятору.
Wedmer
25.08.2017 22:42У меня пока не получилось выделить какое либо время, чтобы углубиться в процесс.
Был момент, когда что то пробовал, но с тех пор Rust уже изменился. После того как он стабилизировался, вроде как легче должно быть… но времени нет. Как будет разгрузка, так и займусь, ибо в столе лежат некоторые концепции, под которые Rust может оказаться самым подходящим инструментом.TargetSan
25.08.2017 23:00+1Не ради саморекламы (там 300 строчек кода, плюнуть и растереть)
https://github.com/target-san/httpdl/tree/step-by-step
Простейший загрузчик файлов по HTTP, конкретно в той ветке что я указал он строится пошагово, в т.ч. с переходом на "идиоматичную" обработку ошибок. Пример не ахти какой, без асинхронки — но маленький, а значит понять должно быть легко.
TargetSan
25.08.2017 22:26+1Если не секрет, что именно непривычно? Я, например, в основном тоже на плюсах пишу — и Rust мне вполне даже зашёл. Очень многие вещи выглядят как раз знакомо.
Wedmer
25.08.2017 22:37Как все выглядит. Как я сказал, мне надо погрузиться, все пощупать, но времени пока нет. За языком слежу с момента анонса.
0xd34df00d
29.08.2017 22:39+1Пишу на плюсах и на хаскеле.
На плюсах пишу то, что должно работать очень быстро. С плюсов особо некуда переходить, потому что для них есть инструменты (vTune), навыки отладки и написания эффективного кода, а также более-менее знающие люди вокруг. Ну, разве что, на Rust интересно посмотреть, у него система типов хорошая и вкусная, и к железу тоже вполне себе близко.
На хаскеле пишу то, где производительность важна чуть меньше, чем скорость разработки, корректность и стоимость внесения изменений. С него вот вообще некуда уходить, только на какие-нибудь там идрисы, но их знает ещё меньше людей.
Szer
25.08.2017 17:13+3Получилось куда более читабельно и коротко.
По-моему, почти ничем не отличается.
kez
25.08.2017 18:20+5Потому что в оригинальной статье речь идёт про похожесть кода («It looks much the same...»), а не о том, о чем пишет автор перевода.
Cryvage
25.08.2017 19:54+2В оригинале, автор говорит что код похож, а затем отмечает важные нюансы.
ОригиналIt looks much the same, but some things are worthy of note:
- extern ( C ): means use the C calling convention.
- D normally puts static data into thread local storage. C sticks them in global storage. __gshared accomplishes that.
- foreach is a simpler way of doing for loops over known endpoints.
- flags[] = true; sets all the elements in flags to true in one go.
- Using const tells the reader that prime never changes once it is initialized.
- The types of iter, i, prime and k are inferred, preventing inadvertent type coercion errors.
- The number of elements in flags is given by flags.length, not some independent variable.
And the last item leads to a very important hidden advantage: accesses to the flags array are bounds checked. No more overflow errors! We didn’t have to do anything
in particular to get that, either.
degs
25.08.2017 18:01iBolit# dmd -h
Все таки хотелось бы в посте побольше информации, хотя бы столько сколько приведено в оригинальной статье.
…
-betterC omit generating some runtime information and helper functions
…
С книгой тоже неясно — это перевод или кто-то сам пишет? Чем она лучше чем существующие 2.5 книги?
McAaron
25.08.2017 21:12-2Этот язык в нише системного программрования. не взлетит никогда. Просто потому, что он не содержит ничего низкоуровневого из того, что есть в C. Системное программирование опирается на абстрактную ISA-архитектуру с одной стороны и конкретную с другой. Все остальное, включая семантику и управление ресурсами — лишнее. Именно поэтому в язык си не вошло ничего из того, что можно безболезненно для производительности и разумности реализовать в виде библиотек. Единственно, чего не хватает в си, так это битовых операций, типа разных сдвигов и прямого управления из языка регистрами — все вычсистемы имеют регистры и кеши. Все это сегодня работает одинаково везде, но отсутствовало тогда, когда создавался язык. Поэтому в язык не вошло. Жаль, что комитет занимается херней, типа немых индексов, вместо того, чтобы как положено распердолить сдвиги во все четыре стороны и обеспечить широкий и полный доступ к состоянию процессора на уровне языка.
Я не прочь поиемть в языке полнофункциональные аналоги всех мыслимых с точки зрения теории jx/jnxneit_kas
26.08.2017 10:32+1C — это первая (или не первая?) попытка реализации кроссплатформенности. Почитайте его историю. Завязка на регистры убьёт эту его особенность окончательно. И да, платформы могут очень сильно отличаться. Тот же переход не всегда делается по регистрам.
mirypoko
26.08.2017 11:05+1Простите, но разве если вам чего-то не хватает в C, разве не лучше сделать ассемблерную вставку? Можно и регистрами процессора управлять, и сдвиги какие хотите делать. Имхо на более высоком уровне абстракции (на уровне C) управление регистрами ненужно.
vintage
26.08.2017 11:19И тут мы приходим к довольно неплохой интеграции низкоуровневого ассемблера и выскоуровнего D: https://dlang.org/spec/iasm.html
struct Foo // or class { int a,b,c; int bar() { asm { mov EBX, this ; mov EAX, b[EBX] ; } } } void main() { Foo f = Foo(0, 2, 0); assert(f.bar() == 2); }
Gorthauer87
26.08.2017 13:26А чем gcc или clang хуже? А немного макромагии и будет работать и с visual studio.
Kobalt_x
26.08.2017 14:32в visualstudio только в x86, в x64 никакого inline asm.
Gorthauer87
26.08.2017 17:05А как там драйвера пишут тогда? Оо
CodeRush
26.08.2017 17:51+1Вынимают весь ассемблер в отдельные функции и (с учетом того, что соглашение о вызове известно) вызывают из потом из кода на С.
Такой подход, кстати, лучше вставок, т.к. и целевые машины, и ассемблеры бывают разные, и потому можно иметь несколько реализаций одной и той же функции в проекте и переключать их через систему сборки, а не через препроцессор, как это пришлось бы делать со вставками.vintage
26.08.2017 19:05В D это делают так:
version(A) { // ... } version(B) { // ... }
Переключать версии можно при сборке, можно в коде.
ziv
25.08.2017 21:24+2#define true 1
#define false 0
char flags[sizepl];
Язык C с версии C99 поддерживает тип bool: en.cppreference.com/w/c/types/boolean. const в C тоже есть.
Код на C и на D практически не отличается, что должен показать этот пример — непонятно.
kmu1990
26.08.2017 00:20Мне немного непонятно, в статье упоминается про системное программирование, но, например, нет ни слова про рантайм. Всякие SIMD и TLS требуют какой-то поддержки, как это запускать на голом железе? Какие функции нужно подложить компилятору, чтобы это взлетело? Особенно, если железо отличается от чего-то очень широко распротранненого? Я не думаю, что причина, по которой некоторые системные программисты не редко работают с C, заключается в его синтаксисе.
Wedmer
26.08.2017 00:28Когда эта фича перетечет в GDC, вопрос сам по себе отпадет.
kmu1990
26.08.2017 00:31С чего бы это? Что gcc для таких вещей вдруг не требует поддержки рантайма? Или магическим образом умеет TLS на всех платформах, для которых он может генерировать код?
Wedmer
26.08.2017 00:36Это значит, что все поддерживаемые архитектуры получат возможность разработки на этом сабсете. В том числе и кросскомпиляцию. И никто не помешает туда включить все расширения для создания кода под конечную платформу.
kmu1990
26.08.2017 00:46+1Под рантаймом имеются ввиду библиотеки, которые линкуются с вашей программой (и прочие вспомогательные средства), чтобы помогать компиялтору реализовывать фичи языка (такие как TLS или например код, который вызывает main с нужными argc и argv, или код которые вызывает конструкторы и деструкторы глобальных объектов и тд и тп), для которых компилятор не может просто сгенерировать код.
Если для распространненых сочетаний архитектура/ОС вы получаете рантайм вместе с компилятором, то для менее распространенных комбинаций это не так. Никто не помешает вам реализовать эти библиотеки, но если ABI не стабилен, то никто не помешает создателям компилятора сломать ваши библиотеки и весь ваш проект вместе с ним.vintage
26.08.2017 09:58Так D генерирует ABI совместимый с C при использовании extern©.
kmu1990
26.08.2017 12:35+1extern вероятно задает конвенцию вызова, а речь не только о ней, а о поддержке рантайма. Например, в статье упоминается лучшая чем у C memory_safety, я полагаю, что это включает среди прочего проверку границ массивов. Что D делает, когда проверка не проходит? Какие функции нужно ему подложить, чтобы это все собралось? Как D представляет массивы в памяти, чтобы можно было настроить передачу данных между D и каким-нибудь firmware?
Чем больше фич язык включает тем больше таких вопросов возникает. В этом смысле сравнительно небогатый на фичи C просто не создавал таких проблем, и поэтому использовать его было довольно просто.vintage
26.08.2017 14:59В D по умолчанию структуры выравниваются так же как в C. Массивы хранятся так же единым куском. После строк помещается нулевой байт на случай работы с ними из сишных функций. Выход за границы массива приводит к исключению.
kmu1990
26.08.2017 15:07+1Если массивы хранятся так же как в C, то откуда берется размер массива?
Исключения могут требовать поддержки рантайма и полезно указать в чем она заключается, а не про синтаксис близкий к C рассказывать. И просто конвенция вызова «как в C» мало что дает.
Элементарно, если исключение не поймать, то программа должна по идее завершиться, но как завершать программу зависит от ОС. А что если у вас нет ОС?vintage
26.08.2017 15:19Если массивы хранятся так же как в C, то откуда берется размер массива?
Статические массивы имеют фиксированный размер. Динамический массив — это фактически структура из указателя и длинны.
Элементарно, если исключение не поймать, то программа должна по идее завершиться, но как завершать программу зависит от ОС. А что если у вас нет ОС?
Как минимум можно всю программу завернуть в try-catch и завершать как считаете нужным. Я не спец в системном программировании, но подозреваю, что в D так же как и в C дёргается abort: https://dlang.org/library/core/stdc/stdlib/abort.html
kmu1990
26.08.2017 15:39+1Как минимум можно всю программу завернуть в try-catch и завершать как считаете нужным.
И если я так сделаю, компилятор перестанет втыкать вызовы рантайма в код? Или отсутсвие их реализации перестанет приводить к ошибке линковки?
Я не спец в системном программировании, но подозреваю, что в D так же как и в C дёргается abort: dlang.org/library/core/stdc/stdlib/abort.htmll
В C нет исключений, так что не также как в C. В C если вы не вызовете abort явно, то компилятор его за вас вызывать не будет только потому, что вы решили работать с массивами, что полезно, если никакой реализации abort у вас нет.
Мой поинт был в том, что если разговор зашел о системном программировании, то есть гораздо более актуальные вещи для обсуждения чем синтаксис похожий на C. И выбор языка C в этой области вовсе не продиктован его синтаксисом и не синтаксис оставнавливает от перехода на другие языки. Что вы хотите сказать своими комментариями?Kobalt_x
26.08.2017 15:59seh на некоторых платформах есть и в C, это не стандарт, но это реализуемо.
kmu1990
26.08.2017 16:06+1И? То что это реализуемо как-то гарантирует мне стабильность рантайма? И что создатели компилятора не сломают его в очередном релизе? Это как-то меняет то обстоятельство, что статья ни слова об всем этом не говорит и с чего собственно обсуждение и началось?
vintage
26.08.2017 17:40А что в C происходит при попытке прочитать данные из нулевого указателя?
kmu1990
26.08.2017 17:43Зависит от архитектуры, ОС, отображения памяти и много чего другого. Но строго говоря с точки зрения стандарта, то это undefined behavior.
Но вот чего там не происходит, так это если компилятор не попросить, то он не будет вставлять в код проверки и если эти проверки не проходят вызывать какие-то функции, которые никто не определял. Как это по вашему мнению происходит в D.
grossws
26.08.2017 01:25Всякие SIMD и TLS требуют какой-то поддержки
А simd-то с какого перепугу требует рантайм? Если, конечно, нет реализации соответствующего intrinsic'а, то будут циклы. Ну так simd-инструкции не везде есть. К сишным
libgcc
/libimf
это относится в той же мере. А если есть sse4/avx/avx2/neon/whatever, то соответствующие simd-инструкции не требуют специфичного рантайма, но только кодогенерации и оптимизаций.kmu1990
26.08.2017 01:30SIMD инструкции могут требовать инициализации соответсвующих расширений процессора. Если вы опираетесь на ОС, то она возможно сделает это за вас (если умеет), а если вы с голым железом работаете, то придется это делать как часть инициализации.
Если компилятор для core фичи генерирует код использующий SIMD (прямо или через intrinsic-и), то это проблема, если это вынесено в библиотеку, использование которой опционально, то проблемы нет.grossws
26.08.2017 01:36Так никто и не заставляет компилятор генерировать simd инструкции, если не указаны
-march
/-mtune
и подобные вещи. Еслиdmd
целится в системное программирование и ведёт себя не так — то это странно.
Вспомнить тот же x86_64, до нормальной работы нужно переключиться в защищенный режим, а потом и в long mode. Если компилятор не позволяет сгенерировать бинарник для всех этих режимов, то на мой взгляд он не очень пригоден для системного программирования.
Меня какое-то время назад пытались убедить, что go и js — языки программирования системного уровня.
P. S. Почти каждый раз, когда собираюсь поставить плюс в word-must-not-be-named, оказывается, что уже ставил. Что ж это такое xD
kmu1990
26.08.2017 01:56Так никто и не заставляет компилятор генерировать simd инструкции, если не указаны -march/-mtune и подобные вещи. Если dmd целится в системное программирование и ведёт себя не так — то это странно.
Я не знаю, как себя ведет D, то что я знаю это то, что статья начинается с упоминания системного программирования (и про какое-то мифическое отсутствие выбора), а потом говорит про синтаксис и фичи ни слова не сказав про ABI и рантайм нужный для поддержки этих фич, я молчу об их стабильности.
Вспомнить тот же x86_64, до нормальной работы нужно переключиться в защищенный режим, а потом и в long mode.
Если вам хватает Real Mode, то в защищенный режим переключать не надо (но тут у вас могут появиться проблемы при использовании, например, gcc), если вам хватает защищенного режима, то в long mode переключаться необходимости нет. Если компилятор поддерживает нужный вам режим, то он вполне пригоден, даже если он не поддерживает все на свете.
Меня какое-то время назад пытались убедить, что go и js — языки программирования системного уровня.
В зависимости от того, что они подразумевали под системным программированием оба языка могут являться таковыми. Мне не известно про какое-то оффициальное стандартизованное законно зафиксированное понятие системного языка программирования. Даже если в контексте данной статьи ограничиться расплывчатым смыслом «близости к железу», то оба языка можно с определенной долей усилий сделать системными создав минимальный рантайм.grossws
26.08.2017 03:27Если вам хватает Real Mode, то в защищенный режим переключать не надо (но тут у вас могут появиться проблемы при использовании, например, gcc), если вам хватает защищенного режима, то в long mode переключаться необходимости нет.
Только если вам не надо на bare metal запуститься и потом использовать long mode) Тогда нужны и все предыдущие. Или, например, на старых arm для использования thumb mode нужно было сначала сделать
bx
для переключения из arm mode в thumb mode, что требует понимания компилятором обоих режимов.
то оба языка можно с определенной долей усилий сделать системными создав минимальный рантайм.
Для некоторых портирование рантайма может оказаться сравнимо с написанием ядра ОС (менеджер памяти, процессов, реализация атомиков, api для файлов, сокетов и т. д. и т. п.).
Если системным называть язык на котором может писаться системное ПО (т. е. не прикладное, не предназначенное для использование пользователем системы), то любой язык, на котором может быть реализован компилятор/транспайлер/вспомогательное ПО для системы, т. е. любой тьюринг-полный окажется системным, что приводит к полной бессмысленности такого определения. Поэтому я склоняюсь к определению системного языка, как такого, на котором может быть реализовано, например, ядро ОС.
kmu1990
26.08.2017 12:49+1Для некоторых портирование рантайма может оказаться сравнимо с написанием ядра ОС (менеджер памяти, процессов, реализация атомиков, api для файлов, сокетов и т. д. и т. п.).
Это очевидно зависит от того, как рантайм определен и от того кто его реализует. Если вы для JS VM определите рантайм из пары функций (например, аллоцировать память и освободить память), то его портирование не должно составить проблем. Отсюда и появился мой вопрос про рантайм.
Поэтому я склоняюсь к определению системного языка, как такого, на котором может быть реализовано, например, ядро ОС.
Я уверен, что на JS и Go можно реализовать ядро ОС. Ваше определение не добавляет ясности, отчасти потому что оно не определяет, что может называться ядром ОС, а что нет. Даже приведенный вами список компонентов ядра вызывает вопросы: известны примеры ядер ОС, которые прекрасно жили/живут без потоков и/или без процессов и/или файловых систем и/или сетевого стека в ядре.
0xd34df00d
29.08.2017 22:50Если, конечно, нет реализации соответствующего intrinsic'а, то будут циклы.
Почему это? По крайней мере, с gcc/clang и x86/x86_64 в моём опыте, если вы дёргаете
_mm_add_blah
какой-нибудь, то код просто не соберётся, если вы не указали соответствующий-march
при компиляции всего файла или если не пометили функцию как__attribute__ ((target ("avx")))
, скажем. Если указали, а потом запускаете на процессоре без поддержки этой фичи, получите какой-нибудь SIGILL.grossws
29.08.2017 23:04Тогда, вероятно, я не прав. Мне казалось, что
icc
вполне умеет обращаться с такими вещами.
Как минимум, оптимизация для простых циклов и при использовании cilkplus активно использовала avx/avx2 при их наличии и сваливалась до sse4 при отсутствии. Но сильно подробно не разглядывал.
0xd34df00d
30.08.2017 02:16Ну, так то ж автовекторизация, по большому счёту, а не ручное написание интринсиков. В упомянутом вами случае оно именно так и происходит.
Laney1
26.08.2017 09:326. Аналог borrow checking из Rust через scope pointers (scope T*), scope slices (scope T[]) и scope references (scope ref T)
в Rust уже активно пилят новую реализацию чекера над графом потока управления — он станет позволять намного больше без потери строгости
11. Ускорение работы с массивами за счет SIMD
а вот это в Rust пока не завезли, и непонятно когда завезут
newpavlov
26.08.2017 15:45>а вот это в Rust пока не завезли, и непонятно когда завезут
SIMD как и остальные LLVM интринзики можно использовать на найтли через llvmint, плюс есть настойчивое желание получить SIMD на стабильном Расте, так что, думаю, в 2018 мы увидим работу в данном направлении.Laney1
26.08.2017 17:11SIMD как и остальные LLVM интринзики можно использовать на найтли через llvmint
проблема в том, что использовать nightly — далеко не всегда вариант. Вот например, разработчик Firefox (основного проекта, ради которого Rust собственно и пилится) еще год назад столкнулся с этим. А воз и ныне там.
Судя по комментам, SIMD в Rust занимается один человек (BurntSushi), причем в свободное время. Так что я бы не был уверен насчет 2018-го.
Temtaime
26.08.2017 12:47+1Как много хейтеров D в комментах, которые даже не разобрались в самой теме вопроса, а посмотрели, что D уже 16 лет, и решили ещё раз его похоронить.
За всё это время D проделал очень большой путь и уже не похож на то, что было в начале. Даже за последние несколько лет изменений в лучшую сторону очень много, идёт потихоньку отказ от сборщика мусора.
После недели кодинга на D вы не захотите возвращаться к C++, потому что у D ОЧЕНЬ богатая стандартная библиотека, прекрасные шеблоны, много фишек самого языка(такие как генерация кода в процессе компиляции; constexpr и рядом не стоял), отсутствует UB, размеры всех типов стандартизированы, есть менеджер пакетов и другие удобные вещи, при этом он ближе к C++, чем Rust и Go и вызывает меньший разрыв шаблона при переходе.
Я говорю это вам как тот, кто написал 3D движок и игру полностью на D.
Если у кого-то есть вопросы по самому языку — могу развёрнуто ответить.rogoz
26.08.2017 13:48+1отсутствует UB
dlang.org/dlangspec.pdf «undefined behavior» найдено неоднократно.
размеры всех типов стандартизированы
В C++ есть int32_t и остальное из cstdint.Temtaime
26.08.2017 14:18Все найденные способы связаны с cast'ом immutable в mutable, а также сборщиком мусора. Ни первого, ни второго в C++ нет.
Оно-то есть, но не «из коробки», а в виде костылей. Как и большинство остального, унаследованного из C.
dataman
26.08.2017 19:08прекрасные шеблоны, много фишек самого языка(такие как генерация кода в процессе компиляции; constexpr и рядом не стоял)
Nim имеет столько фишек, что D и рядом не стоял. :)beduin01 Автор
26.08.2017 19:08Можно хоть пару примеров того что в Nim реализовано лучше чем в D?
dataman
27.08.2017 11:02+1Я некоторое время экспериментировал с D, сделал пару PR, но затем эти nogc, pure, system, etc. стали просто бесить.
пару примеров того что в Nim реализовано лучше чем в D?
1. Макросы. Вы можете создать свой DSL средствами самого языка. Так, например, в Nim реализованы юниттесты. Или макрос scanf, который на этапе компиляции преобразуется в вызовы соответсвующих функций и поэтому парсинг работает очень быстро.
2. Конвертеры
3. Концепты
Для разработчиков игр/графики, думаю, будет интересно взглянуть на opengl-sandbox. Демо.rraderio
28.08.2017 09:23Конвертеры
Т.е. если у меня функция принимает bool а я передаю int, то это все будет работать?
pav5000
29.08.2017 10:39Всякие scanf, printf и даже регулярные выражения в D тоже парсятся на этапе компиляции и преобразуются в вызовы. Это как раз сделано за счёт удобной генерации кода в процессе компиляции.
vintage
29.08.2017 11:20Я бы не сказал, что прям удобной. Всё из перечисленного есть как в Nim, так и в D.
Макросы — mixin() и mixin template, трудно сказать где лучше, они просто разные.
Конвертеры — opCast(), но в D есть ограничение — перегрузки возможны только "изнутри" типа, но нельзя добавить их "снаружи", как в Nim. Хорошо это или плохо — сложно сказать.
Концепты — конструкция if в объявлении шаблонов. Тут D куда мощнее, так как позволяет реализовывать самые дикие условия. Например: "скомпилируется ли код, если использовать такой-то тип в таком-то выражении".
Ну а юниттесты в D являются частью языка, что избавляет от проблем вида "как протестировать приватный метод" и тому подобных.
mkpankov
26.08.2017 13:37+2Аналог borrow checking из Rust через scope pointers (scope T*), scope slices (scope T[]) и scope references (scope ref T)
А что с mutable aliasing? Гонки данных ловит?
lega
26.08.2017 14:38Я считаю что D не плох, просто он попал в цикл непопулярности — «люди туда не идут потому что не популярный, не популярный потому что люди туда не идут», со всеми вытекающими — нет работы, нет развитых тулзов (относительно), нет многих либо от комьюнити.
Для таких вещей нужен большой хайп на старте.
safinaskar
26.08.2017 18:16+3«Нравится ли вам синтаксис Rust?» — не согласен с формулировкой опроса. Синтаксис — одна из последних вещей, которая важна в языке, во всяком случае, если говорить о языках системного программирования. Лучше бы просто спросили «Годится ли Rust как замена C?»
begemot_sun
Какае-то хрень, говорить что он похож синтаксисом на C. Какая разница на что он похож синтаксисом?
Либо ты поддерживаешь синтаксис С, и таким образом тонны С-кода компилируются и исполняются как надо. Либо ты говоришь что я убрал эти С-родовые травмы, и теперь мой язык D-верх совершенства.
А то что, что-то на что-то другое похоже. Да какая вообще разница? эти синтаксисы учаться за 2-4 недели, а дальше ты лобаешь код с той же скоростью. Или ты нанимаешь 100500 девелоперов betterC-режима, и они тебе лабают код с той же скоростью.
Лучше описали бы, плюсы и минусы этого синтаксиса.
И да, я не увидел паттерн-матчинг. Так сразу вопрос, доколе? :)
beduin01 Автор
Чем проще синтаксис, тем меньше шансов сделать ошибку. Да и вообще код проще понимается.
begemot_sun
Ответ неверен. Синтаксис асемблера проще некуда, вот только ошибки там не отлавливаются в прицнипе.
Язык это такая клетка для программиста.
С одной стороны он должен позволять выразить максимум.
С другой стороны он должег позволять выразить это одним единственным способом.
Требования противоречивы.
Про паттерн матчинг мой вопрос был в том, что это очень полезная штука которая из коробки должна быть в языке. Когда её нет, то твой код вместо линейного становится разветвленным, а это очень такая хорошая причина для всяких ошибок.
beduin01 Автор
Вы сами себе противоречите.
begemot_sun
ЧТо? ГдЕ?
johnfound
Отлавливаются и еще как! Посмотрите например как исчезают ошибки в FASM, когда их находят. Буквально за 2..3 часа. В моих проектов на ассемблере, тоже никогда не были проблемы с отловом ошибок.
johnfound
Вот пример: https://board.flatassembler.net/topic.php?t=19985
Баг репорт: 20:41
Автор прочитал репорт: 22:32
Исправленная версия: 22:37
Optik
И где тут время отлова ошибки?
johnfound
В 22:32 Томаш написал "попробуйте то то, чтобы мне легче было", то есть он в тот момент не знает как исправить бага, а в 22:37 пишет:
Тоесть он в эти 5 минут, открыл сорс, исправил ошибку, перекомпилировал все и загрузил на сайт новую исправленную версию. Чем не отлов ошибки?
Optik
Это исправление известного бага. Сколько времени прошло с момента зарождения ошибки до её обнаружения неизвестно, и уж сильно вряд ли 5 минут. Исходная реплика была о том, что отсутствуют инструменты для превентивного поиска ошибок.
johnfound
И какие такие "превентивные инструменты" есть в других языках. Дайте пример, а то совершенно непонятно. По моему баги всегда впервые проявляются, а потом исправляются. И никак иначе.
Кстати, перечитал, о "превентивного поиска" не нашел:
apro
По сравнению с ассемблером в языках типа C/Rust/D есть типизация,
которая на этапе компиляции позволяет предотвратить запись скажем целого числа на место числа с плавающей запятой. Я думаю имелось это ввиду.
johnfound
Там эти ограничения нужны, потому что, уровень абстракции таков, что все делается одинаково. Например то же присвоение, всегда через оператор "=".
В ассемблере, такую ошибку, кроме как намеренно, совершить нереально. По крайней мере, мне не встречалась никогда.
rraderio
А у D точно проще синтаксис?
AnutaU
Самый простой синтаксис у Лиспа. И его же постоянно обвиняют в плохой читаемости.
danfe
Это потому, что у лиспов, по сути, нет синтаксиса. Достаточно простой и при этом читаемый синтаксис, например, у ML-языков.
AnutaU
Он тривиальный, но я бы не сказала, что его вообще нет.
danfe
Он даже не тривиальный, он как бы вырожденный (я поэтому сказал, что «по сути») — это практически готовое AST-дерево, в более-менее человеко-читаемом виде.
grossws
При этом, судя по опросу выше не все любят ML-подобный синтаксис Rust'а (правда, ещё с ощутимой примесью угловых скобок в шаблонах).
danfe
Справедливости ради, когда-то он был намного более подобным (и более приятным на мой вкус); я как-то уже писал об этом:
TargetSan
Назвать синтаксис Rust ML-подобным это довольно оригинально. Если сравнивать с хаскеллем и С++, он больше всего похож именно на плюсы — двойные двоеточия, фигурные скобочки, угловые скобки шаблонов и т.п. Наличие же let и указания типа после имени отнюдь не делает язык ML-подобным, как по мне.
danfe
Сперва он действительно был таким (вообще, до того, как раст стал self-hosted, его компилятор был написан на окамле), но с течением времени стал куда больше похож на C++ чем на ML (собственно, чуть выше я об этом и пишу).
Wedmer
Просто не все наблюдали Rust с момента рождения.
Vjatcheslav3345
Вот как то не очевидно сравнить поклонников языка с консилиумом педиатров, наблюдающими за языком с роддома и искренне интерсующимися его анализами "на кровь, кал, мочу" и прививками...:)
Интересно — фанаты обидятся или обрадуются?
grossws
Как уже написали выше, до 1.0 он был более ml-подобным по синтаксису и erlang-подобным в смысле семантики. Сейчас от вещей, роднящих с ml'ами всякие
let
, вывод типов,match
.Но меня получившийся результат вполне радует. Правда, я на синтаксис смотрю во вторую очередь. Разве что экстремальные варианты типа pascal/ada/modula-подобности не очень приемлемы: он бы стал слишком громоздким ради нужной семантики. Или, наоборот, apl/j/k-подобность сильно повышает плотность информации до уровня неудобоваримости.