[C, C++, D] D как улучшенный C (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Уолтер Брайт — «великодушный пожизненный диктатор» языка программирования D и основатель Digital Mars. За его плечами не один десяток лет опыта в разработке компиляторов и интерпретаторов для нескольких языков, в числе которых Zortech C++ — первый нативный компилятор C++. Он также создатель игры Empire, послужившей основным источником вдохновения для Sid Meier’s Civilization. Данная публикация — первая в серии статей о режиме Better C в языке D.
Язык D был с самого начала спроектирован так, чтобы легко и напрямую обращаться к C и, в меньшей степени, C++. Благодаря этому в нём доступны бесчисленные C-библиотеки, стандартная библиотека C и конечно же системные API, которые как правило построены на API языка C.
Но C — это не только библиотеки. На C написаны многие большие и неоценимо полезные программы, такие как операционная система Linux и значительная часть программ для неё. И хотя программы на D могут обращаться к библиотекам на C, обратное неверно. Программы на C не могут обращаться к коду на D. Невозможно (или по крайней мере очень сложно) скомпилировать несколько файлов на D и слинковать их в программу на C. Проблема в том, что скомпированные файлы на D могут обращаться к чему-то, что существует только в рантайме D, а добавлять его в линковку обычно оказывается непрактично (рантайм довольно объёмный).
Также код на языке D не может существовать в программе, если D не контролирует функцию main(), потому что именно так происходит запуск рантайма D. Поэтому библиотеки на D оказываются недоступны для программ на C, а программы-химеры (смесь C и D) становятся непрактичными. Нельзя взять и «просто попробовать» язык D, добавляя модули на D в существующие модули программы на C.
Так было до тех пор, пока не появился Better C.
Это всё уже было, идея не новая. Бьёрн Страуструп в 1988 году написал статью под названием A Better C. Его ранний компилятор C++ мог компилировать код на C почти без изменений, и можно было начать использовать возможности C++ тут и там, где это имело смысл — не жертвуя существующими наработками на C. Это была блестящая стратегия, обеспечившая ранний успех C++.
Более современный пример — Kotlin, в котором использовался другой подход. Синтаксически Kotlin не совместим с Java, однако он обеспечивает двустороннее взаимодействие с существующими Java-библиотеками, что позволяет постепенно мигрировать с Java на Kotlin. Kotlin — действительно «улучшенная Java», и его успех говорит за себя.
D как улучшенный C
D использует кардинально другой подход для создания улучшенного C. Это не надстройка над C, не надмножество С, и он не тащит за собой давние проблемы C (такие как препроцессор, переполнение массивов и т.д.). Решение D — это создание подмножества языка D, из которого убраны возможности, требующие инициализирующего кода и рантайма. И сводится оно к опции компилятору -betterC.
Остаётся ли урезанная версия D языком D? На это не так просто ответить, и на самом деле это лишь вопрос личных предпочтений. Основная часть языка остаётся на месте. Однозначно остаются все свойства, аналогичные C. В результате мы получаем язык, промежуточный между C и D.
Что убрано
Очевидно, что убран сборщик мусора, а вместе с ним — возможности, которые от него зависят. Память всё ещё можно выделять точно так же, как и в C: при помощи malloc или собственного аллокатора.
Классы C++ и COM всё ещё будут работать, но полиморфные классы D — нет, поскольку они полагаются на сборщик мусора.
Исключения, typeid, статические конструкторы и деструкторы модулей, RAII и юниттесты убраны. Но возможно, мы придумаем, как их вернуть.
На сегодняшний день в Better C уже стали доступны RAII и юниттесты. (прим. пер.)
Проверки assert изменены, чтобы использовать библиотеку C вместо рантайма D.
(Это неполный список, см. спецификацию Better C).
Что осталось
Гораздо более важно, что может предложить Better C по сравнению C?
Программистов на C в первую очередь могут заинтересовать безопасность доступа к памяти в виде проверок границ массивов, запрет на утечку указателей из области видимости и гарантированная инициализация локальных переменных. Далее следуют возможности, которые ожидаются от современного языка: модульность, перегрузка функций, конструкторы, методы, Юникод, вложенные функции, замыкания, выполнение функций на стадии компиляции (Compile Time Function Execution, CTFE), генератор документации, продвинутое метапрограммирование и проектирование через интроспекцию (Design by Introspection, DbI).
Генерируемый код
Возьмём следующую программу на С:
#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 байт.
Перенесём её на D:
import core.stdc.stdio;
extern (C) int main(int argc, char** argv) {
printf("hello world\n");
return 0;
}
Размер исполняемого файла тот же самый: 23 068 байт. Это неудивительно, потому что и компилятор C, и компилятор D генерируют один и тот же код, поскольку используют один и тот же генератор кода. (Эквивалентная программа на полноценном D занимала бы 194 Кб). Другими словами, вы ничего не платите за использование D вместо C при аналогичном коде.
Но Hello World — это слишком просто. Возьмём что-то посложнее: пресловутый бенчмарк на основе решета Эратосфена:
#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;
}
Перепишем на Better C:
import core.stdc.stdio;
extern (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;
auto k = i + prime;
while (k < flags.length) {
flags[k] = false;
k += prime;
}
count += 1;
}
}
}
printf("%d primes\n", count);
return 0;
}
Выглядит почти так же, но кое-что следует отметить:
- Приписка extern(C) означает использование соглашения о вызове из языка C.
- D обычно хранит статические данные в локальном хранилище потока (thread-local storage, TLS). C же хранит их в глобальном хранилище. Аналогичного поведения мы достигаем при помощи __gshared.
- Инструкция foreach — более простой способ пройтись циклом по известному промежутку.
- Использование const даёт знать читателю, что prime никогда не изменится после инициализации.
- Типы iter, i, prime и k выводятся автоматически, уберегая от ошибок с непредвиденным приведением типов.
- За количество элементов в flags отвечает flags.length, а не какая-то независимая переменная.
Последний пункт ведёт к скрытому, но очень важному преимуществу: при обращении к массиву flags происходит проверка его границ. Больше никаких ошибок из-за выхода за границы массива! И нам для этого даже не нужно было ничего делать.
И это только верхушка айсберга всех возможностей Better C, которые позволят вам улучшить выразительность, читабельность и безопасность ваших программ на C. К примеру, в D есть вложенные функции, и по моему опыту, они практически не оставляют мне поводов прибегать к запретной технике goto.
От себя лично могу сказать, что с тех пор, как появилась опция -betterC, я начал переводить на D многие мои старые, но всё ещё используемые программы — по одной функции за раз. Работая по одной функции и запуская набор тестов после каждого изменения я постоянно сохраняю программу в рабочем состоянии. Если что-то сломалось, мне нужно проверить только одну функцию, чтобы найти причину. Мне не очень интересно дальше поддерживать свои программы на C, и с появлением Better C для этого больше нет причин.
===========
Источник:
habr.com
===========
===========
Автор оригинала: Уолтер Брайт (Walter Bright)
===========Похожие новости:
- [IT-инфраструктура, IT-стандарты, Информационная безопасность] Защита документов от копирования
- [DevOps, Python, Анализ и проектирование систем, Искусственный интеллект, Машинное обучение] Общий обзор архитектуры сервиса для оценки внешности на основе нейронных сетей
- [История IT, Старое железо] Компьютерная история: Xerox Alto — персональный компьютер (перевод)
- [Тестирование IT-систем, Тестирование веб-сервисов] Что такое XSS-уязвимость и как тестировщику не пропустить ее
- [Информационная безопасность] Фишинг учетных данных Windows
- [ReactJS, TypeScript, Разработка веб-сайтов] Борьба за производительность по-настоящему больших форм на React
- [Agile, DevOps, Управление персоналом, Читальный зал] DevOps или как мы теряем заработную плату и будущее IT-отрасли
- [IT-компании, Удалённая работа, Управление персоналом, Управление продажами, Фриланс] Увольнять или оставить: есть ли альтернатива бенчу, если грянул кризис
- [Будущее здесь, Космонавтика, Научно-популярное] Пуски всех тяжелых ракет-носителей переносятся на 2021 год… наверное
- [Киберпанк, Научная фантастика] Когда Голливуд впервые заметил веб: что было правильно, и что – совершенно неверно (перевод)
Теги для поиска: #_c, #_c++, #_d, #_dlang, #_betterc, #_c, #_c++, #_d
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 14:16
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Уолтер Брайт — «великодушный пожизненный диктатор» языка программирования D и основатель Digital Mars. За его плечами не один десяток лет опыта в разработке компиляторов и интерпретаторов для нескольких языков, в числе которых Zortech C++ — первый нативный компилятор C++. Он также создатель игры Empire, послужившей основным источником вдохновения для Sid Meier’s Civilization. Данная публикация — первая в серии статей о режиме Better C в языке D. Язык D был с самого начала спроектирован так, чтобы легко и напрямую обращаться к C и, в меньшей степени, C++. Благодаря этому в нём доступны бесчисленные C-библиотеки, стандартная библиотека C и конечно же системные API, которые как правило построены на API языка C. Но C — это не только библиотеки. На C написаны многие большие и неоценимо полезные программы, такие как операционная система Linux и значительная часть программ для неё. И хотя программы на D могут обращаться к библиотекам на C, обратное неверно. Программы на C не могут обращаться к коду на D. Невозможно (или по крайней мере очень сложно) скомпилировать несколько файлов на D и слинковать их в программу на C. Проблема в том, что скомпированные файлы на D могут обращаться к чему-то, что существует только в рантайме D, а добавлять его в линковку обычно оказывается непрактично (рантайм довольно объёмный). Также код на языке D не может существовать в программе, если D не контролирует функцию main(), потому что именно так происходит запуск рантайма D. Поэтому библиотеки на D оказываются недоступны для программ на C, а программы-химеры (смесь C и D) становятся непрактичными. Нельзя взять и «просто попробовать» язык D, добавляя модули на D в существующие модули программы на C. Так было до тех пор, пока не появился Better C. Это всё уже было, идея не новая. Бьёрн Страуструп в 1988 году написал статью под названием A Better C. Его ранний компилятор C++ мог компилировать код на C почти без изменений, и можно было начать использовать возможности C++ тут и там, где это имело смысл — не жертвуя существующими наработками на C. Это была блестящая стратегия, обеспечившая ранний успех C++. Более современный пример — Kotlin, в котором использовался другой подход. Синтаксически Kotlin не совместим с Java, однако он обеспечивает двустороннее взаимодействие с существующими Java-библиотеками, что позволяет постепенно мигрировать с Java на Kotlin. Kotlin — действительно «улучшенная Java», и его успех говорит за себя. D как улучшенный C D использует кардинально другой подход для создания улучшенного C. Это не надстройка над C, не надмножество С, и он не тащит за собой давние проблемы C (такие как препроцессор, переполнение массивов и т.д.). Решение D — это создание подмножества языка D, из которого убраны возможности, требующие инициализирующего кода и рантайма. И сводится оно к опции компилятору -betterC. Остаётся ли урезанная версия D языком D? На это не так просто ответить, и на самом деле это лишь вопрос личных предпочтений. Основная часть языка остаётся на месте. Однозначно остаются все свойства, аналогичные C. В результате мы получаем язык, промежуточный между C и D. Что убрано Очевидно, что убран сборщик мусора, а вместе с ним — возможности, которые от него зависят. Память всё ещё можно выделять точно так же, как и в C: при помощи malloc или собственного аллокатора. Классы C++ и COM всё ещё будут работать, но полиморфные классы D — нет, поскольку они полагаются на сборщик мусора. Исключения, typeid, статические конструкторы и деструкторы модулей, RAII и юниттесты убраны. Но возможно, мы придумаем, как их вернуть. На сегодняшний день в Better C уже стали доступны RAII и юниттесты. (прим. пер.)
(Это неполный список, см. спецификацию Better C). Что осталось Гораздо более важно, что может предложить Better C по сравнению C? Программистов на C в первую очередь могут заинтересовать безопасность доступа к памяти в виде проверок границ массивов, запрет на утечку указателей из области видимости и гарантированная инициализация локальных переменных. Далее следуют возможности, которые ожидаются от современного языка: модульность, перегрузка функций, конструкторы, методы, Юникод, вложенные функции, замыкания, выполнение функций на стадии компиляции (Compile Time Function Execution, CTFE), генератор документации, продвинутое метапрограммирование и проектирование через интроспекцию (Design by Introspection, DbI). Генерируемый код Возьмём следующую программу на С: #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 байт. Перенесём её на D: import core.stdc.stdio;
extern (C) int main(int argc, char** argv) { printf("hello world\n"); return 0; } Размер исполняемого файла тот же самый: 23 068 байт. Это неудивительно, потому что и компилятор C, и компилятор D генерируют один и тот же код, поскольку используют один и тот же генератор кода. (Эквивалентная программа на полноценном D занимала бы 194 Кб). Другими словами, вы ничего не платите за использование D вместо C при аналогичном коде. Но Hello World — это слишком просто. Возьмём что-то посложнее: пресловутый бенчмарк на основе решета Эратосфена: #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; } Перепишем на Better C: import core.stdc.stdio;
extern (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; auto k = i + prime; while (k < flags.length) { flags[k] = false; k += prime; } count += 1; } } } printf("%d primes\n", count); return 0; } Выглядит почти так же, но кое-что следует отметить:
Последний пункт ведёт к скрытому, но очень важному преимуществу: при обращении к массиву flags происходит проверка его границ. Больше никаких ошибок из-за выхода за границы массива! И нам для этого даже не нужно было ничего делать. И это только верхушка айсберга всех возможностей Better C, которые позволят вам улучшить выразительность, читабельность и безопасность ваших программ на C. К примеру, в D есть вложенные функции, и по моему опыту, они практически не оставляют мне поводов прибегать к запретной технике goto. От себя лично могу сказать, что с тех пор, как появилась опция -betterC, я начал переводить на D многие мои старые, но всё ещё используемые программы — по одной функции за раз. Работая по одной функции и запуская набор тестов после каждого изменения я постоянно сохраняю программу в рабочем состоянии. Если что-то сломалось, мне нужно проверить только одну функцию, чтобы найти причину. Мне не очень интересно дальше поддерживать свои программы на C, и с появлением Better C для этого больше нет причин. =========== Источник: habr.com =========== =========== Автор оригинала: Уолтер Брайт (Walter Bright) ===========Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 14:16
Часовой пояс: UTC + 5