MetaProg

Описание: Разработка и отладка приложений. Упор на 3D-графику.

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#19 dyvniy » Ср, 15 октября 2014, 22:58:41

Запахи плохого кода
http://vihv.org/index.php?e=CodeSmells
Спойлер
Оффтоп:
Запахи плохого кода (от английского Code Smells) также иногда называемые Кодом с душком - это ключевые признаки необходимости рефакторинга. В процессе рефакторинга мы избавляемся от запахов, обеспечивая возможность дальнейшего развития приложения с той-же или большей скоростью. При использовании методологии TDD сеанс рефакторинга проводится в среднем раз в 10 минут. Если точнее - после завершения реализации каждого функционального требования. Отсутствие регулярного рефакторинга с течением времени способно полностью парализовать проект и тогда вам скорее всего прийдется выбросить несколько лет разработки и потратить еще пару лет чтоб переписать с нуля. Поэтому запахи надо убивать пока они маленькие.

Дублирование
Повторяющиеся или подобные участки кода. Это основной запах от которого следует избавлятся в первую очередь. Не устраненное дублирование приводит к необходимости вносить одинаковые изменения в различных участках кода, что закономерным образом снижает скорость разработки в разы.
Никогда не разговаривай с незнакомцами
Использование вот таких конструкций: value = ObjectA->ObjectB->GetValue(); Почему это плохо? Потому что код который обращается к классу начинает сильно зависеть от структуры этого класса. В итоге, если мы хотим убрать свойство ObjectB из класса А, например заменить переменную на вызов фабричного метода мы вынуждены бегать по всему коду и править все вызовы. Кроме того код обращающися к объекту А начинает зависить еще и от структуры объекта Б и опять же изменение структуры Б, влечет многочисленные правки по всему коду.
Магические константы (или просто Магия)
Фрагмент кода содержит значение (например: число или строку) откуда оно взялось и почему равно именно этому - по текущему фрагменту кода определить затруднительно или невозможно.
Код непонятен без комментариев
Частая причина - код одного метода выполняет несколько логически слабо связанных действий. Лучше вынести их в отдельные методы, названия этих методов отразят суть происходящего, комментарии станут лишними.
Длинный метод
Если метод занимает более страницы, вполне вероятно что в нем скрыты логически разные блоки, также в длинных методах часто присутствует дублирование в неявном виде, когда дублирующий код раскидан по телу метода отчего его становится сложно обнаружить.
Длинный класс
Как и в случае с длинным методом, длинный класс также может содержать скрытое дублирование. Кроме того это может свидетельствовать о том что в одном классе смешанно несколько логически несвязанных или слабо связанных абстракций, что затрудняет поиск нужного фрагмента кода в приложении.
Кик (от английского KICK - Keep It Complex Kamicadze) или Укус Небестного Архитектора
Попытка создать универсальную архитектуру приложения, вместо того чтобы следовать необходимости. Начинающие программисты часто склонны увлекатся поиском универсальных решений, решением сложных архитектурных задачь. В итоге как правило - перегруженная архитектура, которую трудоемко поддерживать, убытки организации из-за большого количества времени потраченного на её создание, а самое главное - универсальности не получается. И не может получиться. А почему? А потому что приложение с помошью которого вы можете заставить компьютер делать абсолютно любые действия методом нехитрой настройки уже созданно и называется оно Компилятор. Следуйте принципу KISS - Keep Is Simple Stupid, то есть пишите простой и понятный код реализующий именно ту задачу которую необходимо решить в данный момент, возможность легко разобратся в существующем даст вам гораздо больше универсальности.
Использование глобальных переменных
Ничто так не повышает связность кода как использование глобальных переменных. Одна из тех вещей которые нельзя делать никогда. Если уж совсем никак - используйте паттерн Одиночка (Singleton), хотя-бы это избавит вас от необходимости таскать кусок кода гле происходит инициализация глобальной перенной. Но Синглетон - это крайний вариант, если вы решились на его использование, у вас должны быть для этого серьезные основания.
Руки прочь от данных
Никогда ни при каких обстоятельсвах не правьте данные "руками". Забудьте навсегда о phpmyadmin и надстройках вроде mod_custom в Joomla. Это не для вас, это для админов. Это для того чтоб затыкать дыры когда программист не справился с задачей. У программиста на любое изменение БД должен быть скрипт.
Класс имеет более одной зоны отвественности / One responsibility class
По сути, это иначе сформулированный старый принцип ООП. Принцип декомпозиции. Когда мы проектируем архитектуру, мы абстрагируем логические блоки в виде классов. Если у нас класс начинает работать с несколькими логически не связанными единицами, значит наша абстракция более не верна. Значит нам нужно разбить класс на несколько. Например - в процессе устранения дублирования у нас выделился метод отвечающий за установление соединения с удаленным сервером. Мы выносим этот метод в отдельный класс, называем его скажем TFarFarAwayServer. Таким образом у нас появилась новая абстракция - удаленный сервер.
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#20 dyvniy » Пн, 27 октября 2014, 13:35:55

Boost::signals сравнимы с сигналами Qt, однако не требуют MOC.
Вообще использование сигналов крайне полезно для проектирования качественного кода с низкой связанностью классов.
http://habrahabr.ru/post/171471/
Спойлер
О чем эта статья

Сегодня я расскажу про библиотеку Boost Signals — про сигналы, слоты, соединения, и как их использовать.

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



Простой пример

Допустим, мы делаем UI для игры. У нас в игре будет много кнопок, и каждая кнопка по нажатию будет выполнять определенные действия. И хотелось бы при этом, чтобы все кнопки принадлежали одному типу Button — то есть требуется отделить кнопку от выполняемого по нажатию на нее кода. Как раз для такого разделения и нужны сигналы.

Объявляется сигнал очень просто. Объявим его как член класса:
#include "boost/signals.hpp"

class Button
{
public:
boost::signal<void()> OnPressed; //Сигнал
};


Здесь мы создаем сигнал, который не принимает параметров и не возвращает значения.
Теперь мы можем подключить к этому сигналу слот. Проще всего в качестве слота использовать функцию:

void FunctionSlot()
{
std::cout<<"FunctionSlot called"<<std::endl;
}

...

Button mainButton;

mainButton.OnPressed.connect(&FunctionSlot); //Подключаем слот


Кроме функции, можно подключить также функциональный объект:
struct FunctionObjectSlot
{
void operator()()
{
std::cout<<"FunctionObjectSlot called"<<std::endl;
}
};

...

//Подключаем функциональный объект
mainButton.OnPressed.connect(FunctionObjectSlot());

Иногда, если кода очень мало, удобнее писать анонимную функцию и сразу же ее подключать:
//Подключаем анонимную функцию
mainButton.OnPressed.connect([]() { std::cout<<"Anonymous function is called"<<std::endl; });


Если необходимо вызвать метод объекта — его тоже можно подключить, воспользовавшись синтаксисом boost::bind:
#include "boost/bind.hpp"

class MethodSlotClass
{
public:
void MethodSlot()
{
std::cout<<"MethodSlot is called"<<std::endl;
}
};

...

MethodSlotClass methodSlotObject;

//Подключаем метод
mainButton.OnPressed.connect(boost::bind(&MethodSlotClass::MethodSlot, &methodSlotObject));


Про Boost Bind я, вероятно, напишу отдельную статью.
Таким образом, мы подключили к сигналу сразу несколько слотов. Для того, чтобы «послать» сигнал, следует вызвать оператор скобки () для сигнала:
mainButton.OnPressed();


При этом слоты будут вызваны в порядке их подключения. В нашем случае вывод будет таким:
FunctionSlot called
FunctionObjectSlot called
Anonymous function is called
MethodSlot is called


Сигналы с параметрами

Сигналы могут содержать параметры. Вот пример объявления слота, который содержит параметры:
boost::signal<void(int, int)> SelectCell;


В этом случае, очевидно, и функции должны быть с параметрами:
void OnPlayerSelectCell(int x, int y)
{
std::cout<<"Player selected cell: "<<x<<", "<<y<<std::endl;
}

//Передаем функцию с параметрами:
SelectCell.connect(&OnPlayerSelectCell);

//Или так:
SelectCell.connect([](int x, int y) { std::cout<<"Player selected cell: "<<x<<", "<<y<<std::endl; });

//Вызываем сигнал с параметрами:
SelectCell(10, -10);


Сигналы, возвращающие объекты

Сигналы могут возвращать объекты. С этим связана одна тонкость — если вызвано несколько слотов, то ведь, в сущности, возвращается несколько объектов, не так ли? Но сигнал, в свою очередь, может вернуть только один объект. По умолчанию сигнал возвращает объект, который был получен от последнего слота. Однако, мы можем передать в сигнал свой собственный «агрегатор», который скомпонует возвращенные объекты в одно.

Я, конечно, не встречал ситуации, когда мне требуется возвращать значения от сигнала, ну да ладно. Предположим, в нашем случае сигнал вызывает слоты, которые возвращают строки, а сигнал должен вернуть строку, склеенную из этих строк.
Пишется такой агрегатор просто — нужно создать структуру с шаблонных оператором «скобки», принимающим в качестве параметров итераторы:

struct Sum
{
template<typename InputIterator>
std::string operator()(InputIterator first, InputIterator last) const
{
//Нет слотов - возвращаем пустую строку:
if (first == last)
{
return std::string();
}

//Иначе - возвращаем сумму строк:
std::string sum;
while (first != last)
{
sum += *first;
++first;
}

return sum;
}
};


//Функции для проверки:

auto f1 = []() -> std::string
{
return "Hello ";
};

auto f2 = []() -> std::string
{
return "World!";
};

boost::signal<std::string(), Sum> signal;

signal.connect(f1);
signal.connect(f2);

std::cout<<signal()<<std::endl; //Выводит "Hello World!"


Отключение сигналов

Для того, чтобы отключить все сигналы от слота, следует вызвать метод disconnect_all_slots.
Для того, чтобы управлять отдельным слотом, придется при подключении слота создавать отдельный объект типа boost::connection.
Примеры:

//Отключаем все слоты
mainButton.OnPressed.disconnect_all_slots();

//Создаем соеднинение с слотом FunctionSlot
boost::signals::connection con = mainButton.OnPressed.connect(&FunctionSlot);

//Проверяем соединение
if (con.connected())
{
//FunctionSlot все еще подключен.
mainButton.OnPressed(); //Выводит "FunctionSlot called"
}

con.disconnect(); // Отключаем слот

mainButton.OnPressed(); //Не выводит ничего


Порядок вызова сигналов

Не знаю, когда это может пригодиться, но при подключении слотов можно указать порядок вызова:

mainButton.OnPressed.connect(1, &FunctionSlot);
mainButton.OnPressed.connect(0, FunctionObjectSlot());

mainButton.OnPressed(); //Вызовет сначала "FunctionObjectSlot called", а затем "FunctionSlot called"


Заключение

Сигналы и слоты очень удобны в том случае, когда нужно уменьшить связность различных объектов. Раньше, чтобы вызывать одни объекты из других, я передавал указатели на одни объекты в другие объекты, и это вызывало циклические ссылки и превращало мой код в кашу. Теперь я использую сигналы, которые позволяют протянуть тонкие «мостики» между независимыми объектами, и это здорово уменьшило связность моего кода. Используйте сигналы и слоты на здоровье!

Список использованной литературы:

www.boost.org/doc/libs/1_53_0/doc/html/signals.html
с++, c++11, boost, signals, slots
+51 22170
223Mephi1984 10,0
Похожие публикации
Использовать Lua c С++ легче, чем вы думаете. Tutorial по LuaBridge 20 сентября в 19:11
Анимации на лямбдах в C++11 30 июля в 01:13
Спецификатор constexpr в C++11 и в C++14 3 июля в 23:56
Зачем и как я писал BOSS'а и что из этого получилось. Кроссплатформенная система плагинов на C++11 14 мая в 00:56
Сайт на с++ (CppCMS). Часть 1 7 мая в 11:56
Минимализм удаленного взаимодействия на C++11 14 апреля в 15:47
Интерпретация во время компиляции, или Альтернативное понимание лямбд в C++11 9 апреля в 00:11
RMI средствами С++ и boost.preprocessor 29 октября 2013 в 13:48
Реализация Qt signal/slot на Android 6 ноября 2012 в 00:11
DSL для boost::MPL, превращаем f(x) в f<x>::type 7 марта 2010 в 09:41
Комментарии (50)

+1 Door, 4 марта 2013 в 02:24 (комментарий был изменён)#
Я правильно понимаю, что вместо int вот тут boost::signal<int(), Sum> signal должен быть string?
Спасибо за статью, сигналы, возвращающие объекты порадовали… интересно, кто-то использовал эту возможность не на примерах?
0 Mephi1984, 4 марта 2013 в 08:27#↵↑
Спасибо, исправил.
Я использую сигналы и слоты в UI в своем движке. Очень удобно. Уже словил, кстати, несколько грабель — например, нельзя во время вызова сигнала что-то подключать к нему и отключать.
+1 Monnoroch, 4 марта 2013 в 02:31#
Вот блин, а я свой велосипед писал…
+2 Door, 4 марта 2013 в 02:38 (комментарий был изменён)#↵↑
думаю, все через это проходили, куда же без этого, а потом оказывалось, что всё уже давно есть в std::tr1::function, например.
0 lisyarus, 6 марта 2013 в 15:21#↵↑
В с++11 уже и std::function
+2 oYASo, 4 марта 2013 в 02:57 (комментарий был изменён)#
Также стоит дополнить, что сигналы/слоты из boost отлично дружат с сигналами/слотами Qt. Довольно часто это бывает необходимо.

За время использования сигналов/слотов в Qt, у меня сложилось к ним неоднозначное мнение. С одной стороны, это действительно удобный и интуитивно понятный способ вызова одних объектов из других. Так и напрашиваются повесить их на кнопку, тестовое поле или что-то еще.
С другой стороны, сигналы «стреляют во Вселенную», чем очень часто пользуются разработчики. И вот тут начинается дикий геморрой, когда сигнал из одного объекта ловится совершенно никак не относящимся к нему другим объектом, от него уходит еще куда-то и т.д. И так получается, что все объекты системы взаимодействуют между собой только посредством сигналов и слотов, никакого классического ООП.
Я реально такое видел, и исправлять там что-то обычно бессмысленно — проще и лучше написать все заново.
Я не призываю не использовать сигналы/слоты, я призываю использовать их с умом.
+12 Zigmar, 4 марта 2013 в 04:24#↵↑
Как раз независимые объекты, которые обмениваются сообщениями — это самая что не на есть классика ООП. Вызов методов как в С-подобных языках это всего-лишь упрощенная реализация этого механизма. В Smalltalk, например, кажется вообще нету прямого вызова функций (да и функций как таковых) как в процедуральных языках.
+1 ncix, 4 марта 2013 в 23:43 (комментарий был изменён)#↵↑
Поддержу вас. Сигналы-слоты это хоть и неявное но связывание объектов, причем плохоконтролируемое. Можно связать хобот слона с его задницей, и даже не заметить сразу такого конфуза.
Поэтому да, сильно увлекаться не стоит. Механизм мощный, а потому его неосмотрительное использование разрушительно.
+3 JustLuckyGuy, 4 марта 2013 в 05:51#
А как быть с потоками? Ведь слоты вызываются в треде сигнала. Можно ли переложить это в «поток обьекта»?
+2 Mephi1984, 4 марта 2013 в 09:00#↵↑
Я для этого использую boost::asio.
В основном потоке запускаю IoService, который вызывает run_one, а все вызовы сигнала заворачиваю в IoService.post. Получается как-то так, например:

boost::asio::io_service IoService;

boost::signal<void(int int)> TapDownSignal;

//В чужом потоке
void Application::OnTapDown(int x, int y)
{
IoService.post(boost::bind(boost::ref(TapDownSignal), x, y));
}


//В основном потоке:
void ResourceManager::Update(int dt)
{
...
IoService.run_one();
}
0 rg_software, 4 марта 2013 в 09:17#↵↑
Я подробностей не изучал, но есть ещё библиотека Signals2, которая «thread-safe version of Signals». Вероятно, там эти вопросы прорабатываются.
0 Ryadovoy, 4 марта 2013 в 16:59#↵↑
Signals 2 — потокобезопасная реализация с тем-же интерфейсом что и у Signals, вопросами диспетчеризации сообщений между потоками она, к сожалению, не занимается.
+2 AxisPod, 4 марта 2013 в 10:06#
Хм, а почему не boost::signals2?
+1 Qbit, 4 марта 2013 в 11:29#↵↑
И почему boost::bind вместо std::bind? И почему версия 1.51.0 вместо актуальной?
0 Mephi1984, 4 марта 2013 в 11:33#↵↑
Версию исправил.
std::bind не очень хорошо работает в Visual Studio 2010, поэтому я использую boost::bind
0 Door, 4 марта 2013 в 11:35#↵↑
а в каком плане «не очень хорошо работает в Visual Studio 2010»?
0 Mephi1984, 4 марта 2013 в 11:51#↵↑
struct MyStruct
{
void method(int x)
{
}
};

boost::signal<void(int x)> mySignal;

MyStruct myStruct;

mySignal.connect(std::bind(&MyStruct::method, &myStruct, _1));


Компилятор ругается на последнюю строку многоэтажной ошибкой. Я ниасилил понять эту ошибку, поэтому избегаю std::bind.
+5 AxisPod, 4 марта 2013 в 13:14#↵↑
Потому что _1 находится в пространстве std::placeholders, вроде с s на конце. А в бусте в boost.
0 Mephi1984, 4 марта 2013 в 13:29#↵↑
Спасибо, исправил _1 на std::placeholders::_1 — теперь заработало. Буду знать.
НЛО прилетело и опубликовало эту надпись здесь
0 BlackRaven86, 4 марта 2013 в 12:33#↵↑
А разве оно не только под windows?
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
+2 Mephi1984, 4 марта 2013 в 13:50#↵↑
Я посмотрел С++ версию Rx Framework (https://rx.codeplex.com/SourceControl/changeset/view/7881e17c060b#Rx/CPP/RxCpp.sln) — это оно? Я не нашел способа скомпилировать это под Android и iOS, а это для меня критично.

>Нельзя получить сигнал, который бы аггрегировал другие сигналы без кучи boilerplate кода.
boost::signal<void()> signal1;
boost::signal<void()> signal2;

signal2.connect(boost::ref(signal1));

signal2(); //Вызывает signal2, который вызывает signal1

Это оно?

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

НЛО прилетело и опубликовало эту надпись здесь
+3 dima_mendeleev, 4 марта 2013 в 13:58#↵↑
Было бы круто, ввести на хабре обязательным пояснение почему + или -. И складывать эти пояснения где-то возле ответа (но это уже детали дизайна). Тогда бы думали перед тем как тыкать.
0 Robotex, 4 марта 2013 в 12:27#
А какова цена этого комфорта? Что у них с производительностью?
+1 Mephi1984, 4 марта 2013 в 12:54#↵↑
Я вот нашел сравнение: timj.testbit.eu/2013/01/25/cpp11-signal-system-performance/
Вывод — Boost Signals это не самая быстрая реализация сигналов и слотов.
+2 tangro, 4 марта 2013 в 13:20#↵↑
Там разница — в наносекунды. Если значения таких порядков важны — ну тогда уж надо хранить указатели на функции в массиве и вручную вызывать, а еще лучше — сразу джампами на асме писать. А в общем случае — какая разница вызовется обработчик OnKeypressed через 60 наносекунд, или через 200, если человек физически имеет реакцию на уровне 20-50 милисекунд в лучшем случае (это на 6 порядков медленнее).
0 Robotex, 4 марта 2013 в 13:20#↵↑
А по сравнению с Qt Slot/Signals?
0 yshurik, 5 марта 2013 в 03:06 (комментарий был изменён)#↵↑
Когда-то давно (когда были первые версии Qt4) пробегало тестирование сигнал-слотов — скорость около пару-десяти милионов в секунду. Скорость конечно заметно ниже просто прямых вызовов так как Qt4 сигналы завязаны на стринговые сигнатуры. В Qt5 они уже пользуют подход близкий или аналогичный boost.
0 EvilsInterrupt, 4 марта 2013 в 12:47#
Автору: А можете также лаконично и кратко описать новую Boost.Coroutine?
+2 Mephi1984, 4 марта 2013 в 12:52#↵↑
Когда изучу — опишу обязательно!
+1 dima_mendeleev, 4 марта 2013 в 14:00#
>… Про Boost Bind я, вероятно, напишу отдельную статью…
А вы замените его на анонимную функцию.
+2 Athari, 4 марта 2013 в 14:07#
boost::signal<void(int, int)> SelectCell;

Забавно, что в шарпе события и обработчики «родные» для языка, а вот такого красивого и лаконичного синтаксиса там нет. События вообще не first-class citizen, а какой-то костыль. Тот же disconnect_all_slots чёрта с два нормально сделаешь, с несоответствием типов постоянные проблемы. Про проверки на null даже вспоминать не хочется. И ничего в этом направлении не происходит, даже супер-продвинутый Rx Framework с блэкджеком и шлюхами работает с событиями через отражения — ужас на курьих ножках. :(

Кстати, спортивный интерес. Вот допустим, у меня контрол, в котором 150 событий — можно ли как-то свалить все слоты в один объект и сэкономить на 150 объектах сигналов?
+2 Mephi1984, 4 марта 2013 в 14:20 (комментарий был изменён)#↵↑
>Вот допустим, у меня контрол, в котором 150 событий — можно ли как-то свалить все слоты в один объект и сэкономить на 150 объектах сигналов?

Я делаю комбинацией shared_ptr и variant, не судите строго:

//Variant с зараннее определенными типами данных:
typedef boost::variant<int, float, std::string, vec2> TSignalParam;


//Хранитель различных сигналов:
struct TWidgetStruct
{
protected:
//Карта сигналов, ключ - имя сигнала
std::map<std::string, std::shared_ptr<boost::signal<void (TSignalParam)>>> SignalMap;

public:

//Чистим все
void ClearSignals()
{
SignalMap.clear();
}

//Добавляем слот к сигналу
void AddSlot(std::string signalName, std::function<void (TSignalParam)>> func)
{

//Если такого сигнала еще нет - создаем
if (SignalMap[signalName] == std::shared_ptr<boost::signal<void (TSignalParam)>>())
{
SignalMap[signalName] = std::shared_ptr<boost::signal<void (TSignalParam)>>(
new boost::signal<void (TSignalParam)>());
}

//Добавляем слот
SignalMap[signalName]->connect(func);
}
};
0 Mephi1984, 4 марта 2013 в 14:32 (комментарий был изменён)#↵↑
Пример использования:

auto mouseDownFunc = [](TSignalParam param)
{
vec2 v = boost::get<vec2>(param);

std::cout<<"pressed at "<<v.x<<" "<<v.y<<std::endl;
}

auto changeTextFunc = [](TSignalParam param)
{
std::string text = boost::get<std::string>(param);

std::cout<<"text :"<<text<<std::endl;
}

TWidgetStruct WidgetStruct;

WidgetStruct.AddSlot("OnMouseDown", mouseDownFunc);
WidgetStruct.AddSlot("OnChangeText", changeTextFunc);
НЛО прилетело и опубликовало эту надпись здесь
+3 a553, 4 марта 2013 в 14:21#↵↑
Тот же disconnect_all_slots чёрта с два нормально сделаешь

На мой взгляд, это именно disconnect_all_slots – костыль, потому что он позволяет снять все обработчики/слоты внешнему классу (нарушение инкапсуляции), и простого способа предотвратить это, как я понимаю, нет.

В .NET же это просто и удобно:

class MyClass
{
event EventHandler MyEvent;

void MyMethod()
{
this.MyEvent = null;
}
}
НЛО прилетело и опубликовало эту надпись здесь
0 Athari, 4 марта 2013 в 17:28#↵↑
Обойтись можно, и в основном этим способом и пользуюсь, но синтаксис ужасный. Ну почему нельзя в язык добавить нормальный доступ к add/remove по имени события? Проблем с обратной совместимостью, вроде, быть не должно; и в целом цена фичи выглядит небольшой.
НЛО прилетело и опубликовало эту надпись здесь
0 Athari, 4 марта 2013 в 18:34#↵↑
Имея имя события, нельзя обратиться к add и remove как методам этого события. Нельзя передать событие как аргумент: «Вот тебе событие, подпишись на него».
0 a553, 4 марта 2013 в 19:22 (комментарий был изменён)#↵↑
Вот так вы можете передать событие в функцию, а также подписаться на него:

class MyClass
{
event EventHandler MyEvent;

void MyMethod()
{
this.Subscribe(ref this.MyEvent, this.MyHandler);
}

// код аналогичен add_MyEvent
// можно переписать в общем виде, используя касты в System.Delegate
void Subscribe(ref EventHandler e, EventHandler handler)
{
EventHandler fetched;
EventHandler current = e;
do
{
fetched = current;
EventHandler newE = (EventHandler)Delegate.Combine(fetched, handler);
current = Interlocked.CompareExchange(ref e, newE, fetched);
}
while (current != fetched);
}

void MyHandler(object o, EventArgs e)
{
}
}


Вот так вы, имея имя события, можете получить add_MyEvent:

Action<EventHandler> add_MyEvent =
(Action<EventHandler>)
typeof(MyClass)
.GetEvent("MyEvent", BindingFlags.NonPublic | BindingFlags.Instance)
.GetAddMethod(true)
.CreateDelegate(typeof(Action<EventHandler>), myClass);
// myClass – экземпляр MyClass
0 Athari, 4 марта 2013 в 19:40#↵↑
Вот так вы можете передать событие в функцию, а также подписаться на него:

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

Вот так вы, имея имя события, можете получить add_MyEvent

Дык отражения же, по сути хак — ни строгой типизации, ни нормального рефакторинга. О том и речь.
0 a553, 4 марта 2013 в 20:30 (комментарий был изменён)#↵↑
Возможно только внутри класса, который определяет событие.
Ссылку за пределы класса можно вывести через callback-и. Несколько неудобно, да. С другой стороны, мне ещё никогда не приходилось передавать событие как аргумент. Предпочитаю IoC событийно-ориентированному подходу.
0 Athari, 4 марта 2013 в 21:05#↵↑
С другой стороны, мне ещё никогда не приходилось передавать событие как аргумент.

Reactive Extensions не доводилось пользоваться? В основном на стыке между Rx и традиционным кодом с событиями такая проблема и возникает. IoC и коллбэки проблему не решают, потому что в .NET события везде и всюду, свои решения в сам фреймворк не запихнуть.
0 a553, 4 марта 2013 в 21:36#↵↑
Нет, не доводилось. Когда-то хотел познакомиться, но, посмотрев в код реального проекта и увидев монструозные малопонятные конструкции, я быстро ретировался. С тех пор и использую везде IoC – и в ASP.NET, и в WPF – и прекрасно себя чувствую.
0 braindamaged, 4 марта 2013 в 22:38#↵↑
> Кстати, спортивный интерес. Вот допустим, у меня контрол, в котором 150 событий

Если вы про .net, то посмотрите на WinForns, там как раз так и организовано, чтобы не возить с собой 100500 объектов событий, на большинство которых так никто и не подпишется (т.н. «sparse events»)
0 6opoDuJIo, 5 марта 2013 в 14:28#
Я правильно понимаю что сингалы — это те-же многоадресные делегаты?
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#21 dyvniy » Пт, 31 октября 2014, 10:42:13

Почему стоит выбрать D:
http://habrahabr.ru/post/138635/
Спойлер
21 февраля 2012 в 15:49
Почему я выбираю D из песочницы
D*, Программирование*
Вместо введения

Добрый день, Хабралюди.
Хотел бы поделиться со всеми моим скромным опытом выбора языка программирования для своих проектов. Сразу хочу подчеркнуть – я выбирал язык исходя из собственных нужд, и, вполне вероятно, что ваш выбор в аналогичных условиях может быть другим. Все же я искренне надеюсь, что эта статья будет полезной, так как в ней достаточно подробно и аргументировано проводится сравнение D с C++ и C#, а так же упоминаются свыше десяти различных языков, принадлежащих к различным классам и реализующих различные парадигмы. Сам D разрабатывается как высокоуровневый язык для системного и прикладного программирования.

Так же следует отметить, что существуют две версии D: D1 и D2. D1 стабилен, его обновления затрагивают только исправления ошибок, а в D2 в настоящее время добавляются новые возможности. Так как D2 рекомендован для новых проектов, в статье будет рассматриваться именно он, если не указанно обратное.

Об области применения языков программирования вообще

Итак, приступим. Прежде всего хочу заметить, что выбор языка программирования в первую очередь зависит от круга задач – C/C++/Java/C# скорее всего не сможет заменить JavaScript для веб-страниц или SQL для реляционных баз данных. Хотя тут, пожалуй, нужно сделать оговорку: в принципе можно сделать компилятор или написать библиотеку классов, которые бы по заданным правилам преобразовывали один язык в другой. Так, например, поступила Microsoft при разработке платформы ASP.NET – скрипты для браузера могут быть написаны на любом языке платформы .NET (например, на C#) и затем автоматически преобразованы в соответствующий код на JavaScript. Впрочем, лично я проверял данный функционал только для относительно простых примеров генерации валидаторов форм для браузеров, так что возможно, что для какого-то нестандартного случая придется применять именно JavaScript. С другой стороны, учитывая количество свободно распространяемых библиотек и фреймворков (например, jQuery, MooTools, Prototype и др.), протестированных со всеми современными браузерами, возможно, не стоит изобретать велосипед. И раз уж зашла речь о применимости языков для различных технологий, то, например, можно выбрать функциональный язык программирования, скажем Haskell, для написания операционной системы, но выбор JavaScript в качестве системного языка программирования вероятно будет неразумным. К слову, одна ОС на Haskell’ле уже написана, называется House. Ну а на SQL прикладную программу написать не возможно, так как он не обладает полнотой по Тьюрингу. Transact-SQL уже обладает такой полнотой, но поддерживается только для Microsoft SQL Server и Sybase ASE, так что и этот язык не приспособлен для написания прикладных программ.

Второе – это, конечно же, выбор менеджера проекта. Есть мнение, что C++ настолько популярен только потому, что был популярным лет 5 назад. Другими словами, при выборе языка программирования менеджер проекта скорее всего примет решение в пользу более известного языка (те же Java и C++), чем менее известного (например, Haskell или Common Lisp), даже если последний подходит лучше именно для данного проекта. Спрашивается, почему? Философия очень простая: если менеджер завалит проект на C++, то может попробовать оправдаться тем, что ежегодно на C++ умирают сотни проектов. А с Haskell’ем так не получится, так как в этом случае менеджер сам настоял на использовании сравнительно редко используемой технологии. С моей точки зрения это вообще не оправдание, так как менеджер должен предложить наиболее подходящий язык программирования для данной проблемной области, а не руководствоваться какими-то возможными оправданиями из-за популярности языка. Конечно, это не значит, что нужно программировать на заброшенных и никому не нужных языках. Просто хочу подчеркнуть, что к выбору языка нужно подходить творчески. Например, для решения задач компьютерной алгебры может подойти какой-то из функциональных языков (Common Lisp‎, Scheme, Haskell и др.‎), так как математические формулы на таких языках выглядят в более привычном, «математическом» виде, чем на императивных языках. Вот пример кода на Haskell’ле для вычисления факториала в виде рекуррентной формулы:

factorial :: Integer -> Integer
factorial 0 = 1
factorial n | n > 0 = n * factorial (n - 1)

Не правда ли, очень похоже на формулы из учебника? Собственно, свободная система компьютерной алгебры Maxima написана на Common Lisp’е и по своим возможностям сравнима с Maple и Mathematica.
Говоря все это, я лишь хочу подчеркнуть, что выбор языка действительно важен. Не все языки равны между собой, среди них есть более равные. Не верите? Вот пример программы, печатающей «Hello World!», на языке Brainfuck:

++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++
.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.
------.--------.>+.>.

Сразу хочу пояснить – этот содержащий всего 8 команд эзотерический язык был создан Урбаном Мюллером забавы ради, но, тем не менее, является полным по Тьюрингу, а, значит, позволяет написать то, что не может сделать SQL. Вот только спрашивается: кому это нужно, когда есть десятки более достойных кандидатов? Возможно, именно эту идею хотел подчеркнуть Мюллер, давая языку название, созвучное с известным английским ругательством. Другое возможное объяснение – дать языку запоминающееся имя, чтобы хоть как-то увеличить его популярность.

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

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

Немного о недостатках C#

Начнем по порядку. Почему бы не выбрать C# в качестве языка для прикладного программирования, ведь именно для этого он и предназначен? Основная проблема, которая для меня крайне важна, это компиляция в промежуточный язык Common Intermediate Language (CIL), что делает декомпиляцию тривиальной задачей. Больше того, для этого существуют специальные инструменты. Например, .NET Reflector без ошибок генерирует исходный код на C#/VB.NET/F#, включая сохранение оригинальных названий переменных, функций и классов. Полученный исходный код отличается от авторского разве что отсутствием комментариев. Любители поэкспериментировать могут скачать trial-версию программы с официального сайта.

Итак, если для анализа скомпилированной программы на том же C++ необходимы достаточно глубокие знания, включая умение работы с дизассемблером, то с C# получение исходного кода – вопрос пары минут. Конечно, приверженцам Open Source всё это неважно, но тут тоже можно возразить: если я захочу сделать программу свободной, я опубликую её исходные коды, в противном случае у меня есть основания оставить программу закрытой. В конце концов, далеко не всё ПО – свободное. Тут нужно заметить, что существуют сторонние программы-обфускаторы, способные до неузнаваемости изменить исходный код программы различными дополнительными инструкциями, изменением внутренних интерфейсов и имен переменных, генерированием дополнительных исключительных ситуаций, шифрованием кода и т.п. Всё это позволяет сильно усложнить анализ программы на C#. Из существенных недостатков обфускаторов можно отметить: генерацию более медленного кода, чем исходный, потенциальную возможность внесения ошибки в программу и возможность взлома самого обфускатора (после чего вся его защита становится никому не нужной). Задумайтесь на мгновение: люди годами улучшали компиляторы, пытаясь создать все более качественные и оптимизирующие средства разработки, а тут происходит прямо какая-то регрессия.

Впрочем, вопрос важности компиляции именно в двоичные коды зависит от области применения – если программу не планируется применять за пределами компании-разработчика, то, скорее всего, опасность декомпиляции не будет решающим фактором при выборе языка программирования. И чтобы подвести черту, хотел бы добавить: в стандартной поставке компилятора C# от Microsoft существует инструмент для компиляции программ в двоичные коды, но без исходных кодов на CIL программа все равно работать не будет. Так что для защиты от взлома его использовать невозможно.

Второе, о чём стоит задуматься перед использованием C#, это переносимость полученного кода. Разрешите привести цитату Oktal’а: «Я думаю, что Microsoft назвал технологию .Net для того, чтобы она не показывалась в списках директорий Unix». Не знаю, прав ли автор, но из C# очень легко вызвать функции WinAPI, объекты COM и другие компоненты, которые сделают программу непереносимой. Даже без применения специфичных только для Windows компонентов программа сама по себе не будет запускаться в Unix-системах – для запуска потребуется Mono. Да, конечно же, Mono – это широко известный продукт, но при его использовании проблемы всё же могут появиться. Первое, о чем следует помнить – разработчики Mono будут всегда на шаг позади Microsoft, так как они начинают реализовывать уже выпущенные библиотеки и стандарты. Второе: а им случайно не надоест? Да, я прекрасно понимаю, что это – проект со свободной лицензией, который развивает сообщество, но риск прекращения поддержки все равно существует. Это не повлияет на существующие приложения, но новые программы скорее всего станут непереносимы из-за отсутствия поддержки новых библиотек. Третье, о чем следует вспомнить – отсутствие стандартов на компоненты WinForms, ADO.NET и ASP.NET со стороны Microsoft. Их использование может повлечь за собой юридические претензии со стороны Microsoft, поэтому не рекомендуется использовать их совместно с Mono. Последнее означает, что прекрасный компонент ASP.NET, служащий для построения сайтов различных масштабов, не имеет ровным счетом никаких преимуществ перед тем же PHP. Ну а если вдруг вам все же захочется использовать его в своих проектах, будьте морально готовы к покупке лицензий на серверную версию Windows.

И, наконец, третье, о чем стоить себя спросить: а будет ли Microsoft дальше развивать этот язык? В настоящее время все патенты на C#/CLI принадлежат Microsoft, и отказ от поддержки с её стороны будет означать смерть этому языку. Лично я считаю, что C# будет продолжать развиваться – Microsoft слишком много вложила в этот язык. Но гарантий никто дать не может: в свое время Microsoft отказалась от поддержки Visual Basic, создав вместо него совершенно новый язык Visual Basic .NET без обратной совместимости со своим предшественником. В результате это привело к трагедии для тысяч программистов, работающих на Basic’ке. К тому же, по сообщениям в прессе, в 2015-2016 году Microsoft планирует отказаться от бренда «Windows», создав новую ОС для планшетов, смартфонов, компьютеров, консолей, телевизоров и других устройств, а, значит, языки для разработки под эту ОС тоже могут кануть в небытие.

Лично для меня этих доводов достаточно, чтобы отказаться от использования C#. Не поймите меня неверно: C# – это прекрасный современный язык, но подходит он в первую очередь только для Windows, и то не во всех случаях. Немного подумав, можно прийти к выводу, что использование платформы .NET так же опасно, как и C#. Поэтому лично моё мнение – не стоит писать на языках, ориентированных в первую очередь на эту платформу. Например, Nemerle – хороший язык, в чем-то превосходящий C#, главная особенность которого – развитая система метапрограммирования, и сочетающий в себе возможности функционального и объектно-ориентированного программирования. Но дизайн языка ориентирован в первую очередь на платформу .NET, что ставит под вопрос возможность применения его в ряде проектов. F# – прекрасный пример функционального языка программирования для платформы .NET, в чём-то похожий на Haskell, может подойти для разработки математически-ориентированных систем. Но, опять-таки, платформа .NET ограничивает возможность его применения.

Уважаемые приверженцы C# и платформы .NET! Я прекрасно знаю, что у данных технологий много плюсов, и, в первую очередь, это огромная библиотека классов, с помощью которой можно сделать (почти) всё что угодно. К сожалению, подробный обзор преимуществ платформы .NET потребует написания статьи примерно такого же объёма, поэтому прошу меня извинить за заведомо неполное освещение этой технологии и её возможностей.

Немного о недостатках C++

А почему бы не выбрать C++? Конечно же, это промышленный стандарт, про который слышали все. Но на самом деле у этого языка есть ряд существенных недостатков, некоторые из которых я попробую рассмотреть. Первое, что приходит в голову – этот язык сложен. Точнее, очень сложен. По сравнению с ним работа на C# начинает казаться детской забавой. Судите сами: стандарт только на C занимает около 500 станиц, C++ – около 800, C++11 – около 1300. Если сравнить объем технической документации – этот язык по сложности явно превосходит миксер, швейную машинку и автомобиль, приближаясь скорее к самолётам. Для сравнения, стандарт C# 4.0 занимает всего 505 страниц. В этот момент хочется вспомнить цитату Алана Кёртиса Кэйя (Alan Curtis Kay): «Я изобрел понятие «объектно-ориентированный», но могу заявить, что не имел в виду C++ при этом». В противовес этому можно, конечно, вспомнить создателя C++ Бьерна Страуструпа (Bjarne Stroustrup): «Существуют только два вида языков: те, на которые все жалуются и те, которые никто не использует», но эта цитата звучит скорее как оправдание. D в этом смысле достаточно удобный язык – его дизайн был спроектирован в первую очередь на основе C# и Java. Объём спецификации D1 составляет 223 страницы. Спецификация для D2 поставляется виде html-страниц вместе с компилятором, которая также доступна на официальном сайте www.d-programming-language.org. Кроме того, есть книга Андрея Александреску (Andrei Alexandrescu) «The D Programming Language», которая фактически является описанием стандарта D2 (объем – 492 страницы, в настоящий момент переводится на русский язык). Так вот, сложность самого языка отнюдь не облегчает программирование на нём. В D всё сделано проще, разумней и понятней.

Безопасность прежде всего

Следующий серьёзный минус C++ – плохая проверка кода во время компиляции на предмет ошибок. Другими словами, на C++ очень легко написать код, который будет скомпилирован, но не будет работать верно. Оставим составление полного перечня таких спорных возможностей языка специалистам, ограничившись лишь оператором получения элемента массива operator[] (операция индексирования). То, что этот язык не проверяет границы элементов массива, думаю, ни для кого не секрет. Однако, несмотря на это, ошибки переполнения буфера были, есть и будут появляться в программах на C и C++. И хотя эти ошибки допускают программисты, я считаю, что причина именно в языке, который способствует их появлению. Например, C# всегда делает проверки выхода за пределы массива, что делает подобные ошибки гораздо менее вероятными, но теряя при этом в производительности (переполнение буфера всё ещё потенциально возможно из-за возможных ошибок в реализации компилятора или стандартных библиотек). Библиотека STL решает многие проблемы, но это верно только при её правильном использовании. Например, шаблонный класс vector не проверяет выход за свои границы при использовании операции индексирования (operator[]), для проверки границ необходимо использовать функцию at (Х.М. Дейтел, П.Дж. Дейтел, «Как программировать на C++»). Другими словами, STL – это не панацея.

На самом деле ошибки переполнения буфера – проблема более серьёзная, чем кажется на первый взгляд. Знаменитый Червь Морриса, парализовавший интернет в 1988 году, использовал именно этот тип ошибки (ущерб оценивается в 96 миллионов долларов). Еще один пример: при использовании техники Возвратно-ориентированного программирования (Return-oriented programming) достаточно внедрения всего 2-3 инструкций ассемблера для последующего взлома системы. Другими словами, переполнение буфера даже на один байт может представлять собой уязвимость. Разрешите привести цитату экспертов по безопасности Майкла Ховарда и Дэвида Лебланка из книги «Защищенный код»: «В процессе многих кампаний по безопасности в Microsoft мы настойчиво пропагандировали выявление и при необходимости перенос опасных компонентов, написанных на C или C++, на C# или другой управляемый язык. Это не означает, что код автоматически станет безопасным, но некоторые классы нападений – скорее всего атаки с переполнением буфера – станет намного труднее эксплуатировать. Это же верно по отношению к DoS-атакам на серверы, которые возможны из-за утечки памяти и других ресурсов». Задумайтесь на мгновение: предлагается использовать более медленный, менее функциональный и легко поддающийся дизассемблированию язык только из-за наличия встроенной проверки границ массивов и сборщика мусора.

Тут нужно подчеркнуть разницу между DoS-атакой (Denial of Service, Отказ от обслуживания) и DDoS-атакой (Distributed Denial of Service, Распределённый отказ от обслуживания). DoS-атака, как правило, использует «умные» уловки и уязвимости в сервисе и проводится с небольшого числа компьютеров (возможно, даже с одного). Например, как писалось выше, DoS-атака может быть основана на утечке памяти. Другой пример – атака на файловый сервер, в котором все загружаемые файлы проверяются на наличие вирусов. Сначала создаётся файл размером несколько гигабайт, состоящий из одних нулей, архивируется (размер в архиве – несколько килобайт), закачивается на атакуемый сервер, после чего сервер его распаковывает и пытается проверить антивирусом… DDoS-атакой является попытка вызвать отказ в обслуживании просто за счет перегрузки сервиса таким количеством запросов, на которые он заведомо не рассчитан. Другими словами, это – атака «в лоб», проводящаяся сразу с большого количества машин. Несмотря на то, что существует ряд способов защиты от DDoS-атак, от правильно организованной DDoS-атаки защититься на все 100% невозможно, поэтому становится крайне важной производительность самой службы: чем больше запросов мы можем обработать, тем меньшими будут последствия атаки. В результате этого возникает парадокс: C# за счет лучшего контроля ресурсов позволяет заметно усложнить проведение DoS-атаки, но делает службу более уязвимой к DDoS-атаке из-за более низкой скорости работы.

Но вернёмся к проверке границ массивов в C++. Я прекрасно понимаю, что это – системный язык программирования, и любые дополнительные издержки могут сделать программу в конечном итоге недостаточно производительной. Однако следует подчеркнуть, что критерий производительности важен лишь для конечной release-версии продукта. Версия для отладки предназначена для работы в узком кругу разработчиков программы на тестовых примерах, следовательно, может (и должна) содержать разнообразные проверки и отладочные сообщения, способные повысить её качество и снизить вероятность ошибки (подробнее смотрите в книге Стива Макконнелла «Совершенный код»). Поэтому меня огорчает отсутствие в стандарте C++11 подобных средств хотя бы для debug-версии программы. Однако следует отметить, что существуют сторонние продукты для защиты стека от изменений с целью обнаружения ошибок переполнения в нём, например, расширения компилятора gcc Stackguard и Stack-Smashing Protector (старое название — ProPolice), компиляторы Microsoft Visual Studio и IBM Compiler, которые способны значительно усложнить эксплуатацию уязвимостей. Но потенциальная возможность взлома системы всё же остаётся. К тому же кроме переполнения в стеке возможно переполнение в куче, которое является ничуть не менее опасным. Вывод: единственный надежный способ защититься от взлома – писать правильный код.

Приятно осознавать то, что D выучил урок: в нём есть встроенная поддержка проверки границ массивов для отладочной версии программы, которая отключается при компилировании оптимизированной версии. Другими словами, D сочетает в себе качества двух миров – идеальный выбор для языка системного и прикладного программирования. Но возможности по обеспечению безопасности в D на этом только начинаются. Было бы просто неправильным не упомянуть SafeD – подмножество языка D, запрещающее потенциально опасные операции с памятью, такие как арифметические операции над указателями. Это позволяет гарантировать, что память останется неповрежденной. В сочетании со встроенным сборщиком мусора язык приобретает черты, характерные для C#: ошибки переполнения буфера и DoS-атаки станут вас беспокоить гораздо реже. Разрешите немного перефразировать совет Ховарда и Лебланка: пишите потенциально опасные фрагменты программы на D, и наслаждайтесь безопасным, эффективным и компилируемым языком.

Возможность вызова кода на C/C++ – промышленный стандарт

Пришло время упомянуть еще одну важную черту D: он полностью совместим с кодом C/C++ на уровне объектных файлов, что позволяет получить прямой доступ к функциям и классам, написанным на C/C++ и наоборот. Фактически, стандартная библиотека C является частью стандарта D, правда вместо неё лучше использовать соответствующие функции библиотеки D. Это сделано просто потому, что никто не собирается переписывать тонны C++ кода на D – просто вооружитесь своим любимым компилятором C++ – и готово. Для сравнения, вызов управляемого кода, написанного, например, на C#, из неуправляемого, написанного, например, на C++, возможен через COM-объекты, что с моей точки зрения сложнее, чем просто линковка. Правда тут необходимо отметить, что стандарт C++/CLI добавляет расширенные способы взаимодействия C++ с управляемым кодом, однако это означает использование только компилятора Visual C++ от Microsoft.

Сборка мусора против деструктора

Тему сборки мусора следует осветить подробней. Мне не раз приходилось слышать фразу: «если в язык добавили сборку мусора, значит испортили производительность». Сразу хочется спросить у таких людей: «а тесты вы делали?». Будучи системным языком программирования, D позволяет применять ручное управление памятью (правда, в рамки подмножества SafeD эта возможность уже не входит). В вашем инструментарии появятся перегрузка операторов new и delete и C-стиль управления памятью функциями malloc и free. Кроме того, как правило, быстродействие можно повысить на 90% за счет изменения всего лишь 10% кода (по материалам книги Мартина Фаулера «Рефакторинг. Улучшение существующего кода»). Другими словами, большая часть программы может быть написана с помощью инструкций, безопасно работающих с ресурсами, а небольшой критичный к быстродействию фрагмент может быть тщательно проверен. При этом важно осознавать, что используется один и тот же язык программирования, что упрощает создание, поддержку и сопровождение кода. Кроме того, в больших программах сборка мусора ускоряет разработку, так как программист на C++ тратит много времени (по некоторым источникам – до 50%) на управление памятью. Так что вот вам рекомендация: пишите код, а вопросы производительности оставьте профайлеру, который поможет вам выявить проблемный с точки зрения производительности фрагмент кода.

Также существует ряд алгоритмов, которые просто не могут работать правильно без сборки мусора. Разрешите привести простейший пример: у вас есть абстрактный класс Number, символизирующий какое-то число, и его подклассы: Integer, Long, BigInt, Float, Double, Real, Complex и т.д. Теперь представьте, что где-то в вашей программе появляется примерно такая строчка:

Number* c = a + b;

где a и b – указатели на Number, т.е. фактический тип переменных не известен. При этом предполагается, что если a и b – Long, то в результате получим Long или BigInt (чтобы избежать ошибок переполнения), если Integer и Complex, то Complex, если Double и Double, то Integer, Long, Float или Double (в зависимости от полученного числа знаков после запятой, например 0,5 + 0,5 = 1) и т.д. Скажите, как правильно реализовать функцию operator+(Number* a, Number* b)? Подробный анализ реализаций выходит за рамки этой статьи, но все желающие могут ознакомиться с несколькими возможностями в книге Джеффа Элджера «C++ для настоящих программистов», в главах, посвящённых множественной передаче (и двойной передаче в частности). Хочу лишь отметить, что функция operator+ должна будет создать объект в куче с помощью оператора new, так как вызывающий её код не может ничего знать о фактическом типе объекта c, и поэтому не может выделить память в стеке. Как следствие, нам нужен механизм сборки мусора для освобождения памяти. В данном случае можно применить умные указатели с подсчётом ссылок, но они имеют принципиальные ограничения. Другими словами, встроенной в язык сборки мусора ничто не заменит, и то, что в стандарте C++11 указана поддержка лишь его базовых функций (реализация расширенной сборки мусора в стандарт не вошла), меня отнюдь не радует. Под конец хочется вспомнить цитату Френсиса Бэкона: «Тот, кто не хочет прибегать к новым средствам, должен ожидать новых бед». Да, если вдруг вас заинтересовал предыдущий пример с числами, рекомендую также обратить внимание на CLOS – объектную систему Common Lisp’а, в которой уже имеется встроенная поддержка множественной диспетчеризации.

Деструктор против сборки мусора

Следующее, о чём нужно знать – в D есть RAII (получение ресурса есть инициализация) – характерная для C++ модель управления ресурсами, но отсутствующая в C# и Java. Другими словами, в D есть деструктор, и вызывается он независимо от сборщика мусора. Как следствие, эта парадигма облегчает управление ресурсами (в первую очередь – их освобождение).

Как известно, C# и Java отказались от этой модели, заменив деструкторы завершителями, которые вызываются уже после сборки мусора. Последнее делает невозможным использовать завершители для освобождения критичных для системы ресурсов, так как вызов завершителя может произойти через длительный промежуток времени. Следовательно, освобождать ресурсы нужно явно. «А как же сокрытие информации? Ведь я теперь должен помнить, какие классы требуют явного освобождения ресурсов, а какие – нет! Именно здесь появляются ошибки», – спросите вы, и будете правы. В ответ на это остается разве что развести руками и ответить, что оно было в C++ и есть в D. Правда, в случае неверно написанной программы освобождение ресурсов «неизвестно когда» всё же лучше, чем «никогда», так что однозначный выбор между моделями управления ресурсов в C++ и C#/Java сделать сложно. Возможно, именно поэтому между приверженцами C++ и Java возникает столько споров.

Также в C# возможна ситуация, когда в результате пропуска кода, освобождающего ресурсы явно, вызов завершителя не сможет освободить ресурсы верно (произойдет потеря данных, генерация исключения и т.п.). Например, рассмотрим класс FileStream, являющийся подклассом от Stream и позволяющий писать двоичные данные в файловый поток, и класс StreamWriter, позволяющий писать произвольные данные (например, строки, различные числа, и т.д.) в различные двоичные потоки (в любой из подклассов Stream, например, в FileStream, MemoryStream, NetworkStream и т.д.). В результате мы можем писать произвольные данные в любые потоки, что фактически реализует паттерн проектирования Мост (Э. Гамма, Р. Хелм, Р. Джонсон, Д. Влиссидес, «Приемы объектно-ориентированного проектирования. Паттерны проектирования»). Теперь представьте, что во всех этих классах с целью повышения производительности есть встроенные буферы, и данные отправляются на запись только после заполнения буфера (именно так реализованы эти классы в .NET). Теперь добавим ко всему этому ошибку в программе: программист забыл явно освободить ресурсы. После сборки мусора, когда объекты классов FileStream и StreamWriter больше не будут использоваться, произойдёт вызов их завершителей. Но порядок выполнения завершителей в .NET не определён! Это означает, что примерно в 50% случаев завершитель объекта FileStream закроет файл до того, как завершитель объекта StreamWriter финализирует свой буфер. Что теперь должен сделать завершитель объекта StreamWriter, ведь ему просто некуда писать данные? Есть две очевидных возможности: просто проигнорировать, что приведёт к потере данных, или сгенерировать исключение, что приведет к потере данных и аварийному завершению программы (так как данное исключение останется необработанным). С моей точки зрения генерация исключения предпочтительней, так как она упростит отладку программы, но программисты Microsoft со мной не согласны: в своей реализации данных классов они предпочли молчаливую потерю данных. Больше того, для совершения подобной ошибки необязателен пропуск явного освобождения ресурсов, достаточно лишь освободить ресурсы объекта типа FileStream до освобождения ресурсов объекта типа StreamWriter… В такие моменты начинаешь тосковать по правилу C++: «вызовы деструкторов выполняются в порядке, обратном вызовам конструкторов». Вывод: одного лишь сборщика мусора или RAII недостаточно для эффективного управления ресурсами, именно поэтому D поддерживает обе этих технологии.

Вместо заключения

К сожалению, в этой статье не хватило места для обзора ряда особенностей D. Однако хотелось бы пусть и бегло, но всё же их перечислить:
Эффективная модель создания многопоточных программ, значительно превосходящая C++ в плане безопасности
Мощнейшая система метапрограммирования, сильно упрощающая создание нетривиальных шаблонов. Сравнима по функциональности с макросами в языке Nemerle (которые ничего общего с макрокомандами C/C++ не имеют).
Константность и неизменяемость переменных
Вычисления на этапе компиляции
Наличие черт, характерных для функциональных языков. В частности, в языке есть чистые функции, ленивые вычисления, лямбды, замыкания, функции высших порядков и т.д.
Наличие возможности запрета наследования классов и переопределения методов, примерно так же, как в C#/Java
Контрактное программирование, способное сильно упростить отладку программ и помочь компилятору лучше оптимизировать код. То, что таких возможностей нет в стандартах C++ и C#, меня огорчает.
Встроенная поддержка unit-тестирования, способная помочь при проведении рефакторинга и сопровождении программы
Поддержка понятия «модуль» как единицы сборки программы, присутствующая в Ada, C# и Java, но не вошедшая в стандарт C++11
Перегрузка операторов, которую использовать проще, чем в C++
Поддержка минус бесконечности, бесконечности и NaN (не число) для числовых типов, добавленных в стандарт C99, но не включенных в C++
Поддержка кросплатформенности программ (стандартная библиотека D сделана переносимой)
Очень быстрая компиляция программ, особенно для больших проектов
Генератор документации встроен в компилятор (как, например, и в C#)
Профайлер встроен в компилятор, что упрощает поиск «узких» мест в программе
Анализ покрытия кода встроен в компилятор, что упрощает отладку программы
и многое другое…

Что еще можно почитать?

Официальный сайт языка
Официальный сайт разработчика
Книга Андрея Александреску (Andrei Alexandrescu) «The D Programming Language»
Описание языка в Википедии
Описание языка в английской Википедии (находится в более актуальном состоянии)
Перевод статьи Андрея Александреску «Место для D»
Статья «Место для D» и список часто задаваемых вопросов
Статья с беглым описанием языка
Статья с описанием метапрограммирования и вычислений на этапе компиляции
Статья с более подробным описанием метапрограммирования
Статья с примерами перегрузки операторов
Статья с описанием чистых функций
Англоязычная вики, посвящённая D
Список IDE и редакторов с поддержкой D. Могу порекомендовать Mono-D, но также следует обратить внимание на D-IDE, Poseidon и Visual D.
Список отладчиков для D (фактически подойдёт любой отладчик с поддержкой C++)
Список open source проектов, написанных на D (там же можно найти ряд дополнительных библиотек)

Дополнительная литература

Стив Макконнелл, «Совершенный код»
Майкл Ховард и Дэвид Лебланк, «Защищенный код»
Майкл Ховард, Дэвид Лебланк и Джон Виега, «19 смертных грехов, угрожающих безопасности программ. Как не допустить типичных ошибок»
Э. Гамма, Р. Хелм, Р. Джонсон, Д. Влиссидес, «Приемы объектно-ориентированного проектирования. Паттерны проектирования»
Мартин Фаулер, «Рефакторинг. Улучшение существующего кода»
Х.М. Дейтел, П.Дж. Дейтел, «Как программировать на C++»
Джефф Элджер, «C++ для настоящих программистов»

Под конец хочется пожелать всем успехов в программировании и в разумном подборе языка для своих будущих программ.
D, языки программирования, язык d, D2, C++, C
+68 5997
190Vseslav 0,0
Похожие публикации
Распространенность серверов и серверных языков программирования в Рунете 3 июля в 08:37
Какие языки программирования наиболее востребованы в сфере финансов 24 апреля в 14:26
ArnoldC: язык программирования из цитат Шварценеггера 28 февраля в 16:24
Процентное соотношение языков программирования, которые сейчас преподаются в средней школе 23 декабря 2013 в 12:55
Язык программирования J. За что любить? 24 ноября 2013 в 22:02
Самые востребованные языки программирования 31 октября 2012 в 12:00
База по языкам программирования: Синтаксический сахар или история развития языков 11 июля 2012 в 23:27
База по языкам программирования: Принцип сохранения функционала 11 июля 2012 в 23:27
База по языкам программирования: Как появлялись языки и зачем 11 июля 2012 в 23:26
Язык программирования Gentee 18 июня 2012 в 09:00
Комментарии (169)

+33 silvansky,21 февраля 2012 в 16:39#
Фух, неужели я это дочитал? =)

Статья неплохая, но, как мне кажется, слишком много отвлечённых разговоров о других языках, их проблемах и парадигмах. Информация про сам D и его плюсы теряется среди всего прочего.
+5 Vseslav,21 февраля 2012 в 16:51#↵↑
Просто не хотел писать короткий список с перечислением особенностей языка, хотелось дать именно сравнение с другими языками. Если Вас интересуют именно особенности D, могу порекомендовать статью на Википедии, ссылку приводил.
+5 silvansky,21 февраля 2012 в 16:54#↵↑
Нет, я немного не про то.
Сравнение с другими языками штука хорошая, но в статье больше описание других языков, статья получилась «Почему C++ и C# хуже D».
+2 Vseslav,21 февраля 2012 в 17:04#↵↑
Эх, если начать расписывать достоинства всех языков, то получилось бы страниц 20-30. Наверно, тогда бы никто до конца не дочитал. Итак пришлось статью сильно сократить — ряд особенностей D пришлось просто вынести в раздел «Заключение», хотя те же многопоточное программирование и создание шаблонов были достойны развёрнутого описания.
+2 mclander,22 февраля 2012 в 12:02#↵↑
очень похожа на курсовую, например по летающей тарелке. Сперва воспоминания конструкторов, оказавшихся от проектирования воздушных шаров, потом обзор существующих летательных аппаратов, их недостатков и достоинств нашей тарелки. На которые поиски почемуто не хватило места. Если бы я представлял например перл, я бы привел пару связок типа map grep sort и объяснил какие задачи эти конструкции решают.
0 levap,21 февраля 2012 в 16:45#
А под какие аппаратные архитектуры существуют компиляторы этого языка?
+1 Vseslav,21 февраля 2012 в 16:58#↵↑
Под i386 и x86_64.
Список поддерживаемых ОС можете посмотреть сами на оф. сайте в разделе загрузки.
Так же есть сторонние компиляторы под платформы LLVM для D1 и D2 и .NET для D2 и компилятор gdc из проекта gcc. Подробнее см. здесь.
+2 Nekuromento,21 февраля 2012 в 17:56#↵↑
GDC в теории поддерживает все архитектуры поддерживаемые GCC, но на практике не везде портировали рантайм языка.

Люди успешно собирали под ARM и MIPS
+20 basilisk,21 февраля 2012 в 17:01#
Уффф. Слишком много воды и размышлений не по делу. Хватило бы развёрнутого (и с примерами) заключения.

Всё остальное словоблудие (увы, но иначе я это назвать не могу) можно спокойно убрать: зачем рассказывать про полноту по Тьюрингу здесь? Кто-то сомневается, что D тьюринг-полный язык? Зачем вообще упоминать SQL, Brainfuck? Они же совсем мимо кассы…

А куча утверждений весьма спорна:

1. Менеджер выбирает C++ не потому, что ему проще отмазаться в случае завала (причина достаточно глупая), а потому, что программисты и знания по языку не являются дефицитом. По языку есть тонна устоявшихся методик программирования, оценки проектов и рисков, известна и подтверждена опытным путём сфера эффективного применения и т.п.

2. Название Brainfuck не служит цели популяризации языка: язык шуточный и название объясняет саму суть языка. Не более того.

и т.д.
+3 silvansky,21 февраля 2012 в 17:07#↵↑
И «realize-версия» =)
0 Vseslav,21 февраля 2012 в 18:40#↵↑
Согласен, вводную часть статьи можно было опустить. Просто нужно было с чего-то начать, подумал, что так будет правильней.
А можете написать чуть-чуть подробней про «устоявшиеся методики программирования, оценки проектов и рисков, сферу эффективного применения»? Или хотя бы ссылку на литературу дать? Мне правда интересно.
0 rumatavz,21 февраля 2012 в 17:53#
Так, например, поступила Microsoft при разработке платформы ASP.NET – скрипты для браузера могут быть написаны на любом языке платформы .NET (например, на C#) и затем автоматически преобразованы в соответствующий код на JavaScript.


Подскажите плз где про это почитать.
0 szKarlen,21 февраля 2012 в 18:06#↵↑
возможно автор имел ввиду ASP.NET AJAX с его кодогенерацией.
0 Vseslav,21 февраля 2012 в 18:06#↵↑
Если честно, не помню, где именно про это читал. Просто сами попробуйте создать веб-форму и добавить туда валидаторы — соответствующий код на JavaScript должен быть создан автоматически (собственно, в статье я писал именно об этом функционале).
Ну а если Вы спрашивали именно о компиляции C# в JavaScript, то можете посмотреть script# для компиляции C# в javascript и jsc compiler для компиляции на IL в javascript, actionscript
+2 rumatavz,21 февраля 2012 в 18:19#↵↑
Можно ли убить дракона? Можно, но не убить и не дракона


1)Валидаторы не конвертят код на шарпе в джаваскрипт. Если в создаете стандартный валидатор, то он, конечно же добавит на страницу фиксированный даваскрипт. Если валидатор кастомный, то писать джаваскрипт нужно вручную.

2)script# не есть часть ASP.NET и не написан MS.
0 Vseslav,21 февраля 2012 в 18:34#↵↑
Спасибо, учту. Если не возражаете, добавлю примечание к статье с ссылкой на Вас как на указавшего недочёт.
0 ApeCoder,21 февраля 2012 в 19:47#↵↑
У MS была попытка под названием Volta www.zdnet.com/blog/microsoft/microsoft-architect-compares-volta-and-googles-gwt/1023

Еще есть WebSharper под F# (кстати кто-то из наших делает)
+1 Yavanosta,22 февраля 2012 в 02:12#↵↑
Есть кстати еще sharpkit.net/ но тоже не от MS. Это я не спорю, а так, вдруг вам интересно :)
+4 szKarlen,21 февраля 2012 в 18:04#
ну где же D с примерами? где пример кроссплатформенной работы с потоками?
не могу промолчать про .NET и C# в частности.
C# за счет лучшего контроля ресурсов позволяет заметно усложнить проведение DoS-атаки, но делает службу более уязвимой к DDoS-атаке из-за более низкой скорости работы.

ну-ну, а если сразу на ассемблере будет написано, то DDoS может пройти мимо.

Основная проблема, которая для меня крайне важна, это компиляция в промежуточный язык Common Intermediate Language (CIL), что делает декомпиляцию тривиальной задачей. Больше того, для этого существуют специальные инструменты.

проблема заключается именно в модуле лицензирования для ПО, который обходится практически для всех коммерческих программ. вопрос про сверхсекретные алгоритмы чего-либо думаю опустим. так что MSIL не является проблемным местом.

Не много подумав, можно прийти к выводу, что использование платформы .NET так же опасно, как и C#. Поэтому лично моё мнение – не стоит писать на языках, ориентированных в первую очередь на эту платформу.

честно говоря каких-либо вменяемых доводов так и не было, кроме покупки лицензии на windows. т.е. денег на разработку есть, на оплату менеджеров и т.д., а вот $400 на хотя бы Windows Web Server нету.
и кстати, почему не вспоминаем сотни undefined behaviour в стандарте самого языка C или C++, где уже надо ориентироваться на сам компилятор. а тут еще и D — темная лошадка.
0 Vseslav,21 февраля 2012 в 20:47#↵↑
>> ну где же D с примерами? где пример кроссплатформенной работы с потоками?
Надеюсь, войдут в следующую статью.

>> ну-ну, а если сразу на ассемблере будет написано, то DDoS может пройти мимо.
Тут, конечно, Вы правы, но речь шла именно о совместном использовании средств защиты против DDoS-атак и устойчивости работы самой службы. Согласитесь, всегда есть вероятность пропуска запроса от робота во время DDoS-атаки. Собственно, это я и имел ввиду в статье.

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

>> денег на разработку есть, на оплату менеджеров и т.д., а вот $400 на хотя бы Windows Web Server нету
Тут уже каждый сам для себя решает, но критика принимается.

>> почему не вспоминаем сотни undefined behaviour в стандарте самого языка C или C++
Просто решил не лезть в эти дебри. Возможно, в следующий раз упомяну и их тоже.
+3 dikarik,21 февраля 2012 в 21:54#↵↑
В D нет понятия undefined bahavior — это принципиальная позиция автора языка. Но вот со спецификацией слукавили, и сильно — то, что есть для D, и рядом не лежало по полноте со стандартом плюсов. Если детализировать — то объёмы получатся схожими. D очень красивый и умно спроектированный язык, но что он много проще плюсов я бы не сказал. На поверхности — да, но если лезть в нюансы того же duck typing, во взаимодействие с миксинами и модель преопредления опреаторов — ох много там углов… Однако мощно, куда мощнее плюсов в большинстве аспектов.
+11 utrack,21 февраля 2012 в 18:15#
Почему-то у вас все преимущества D сводятся к недостаткам С*. =)
0 Vseslav,21 февраля 2012 в 20:23#↵↑
Возможно, потому что сам программировал на C# и C++. Как известно, все познается в сравнении, вот и решил написать статью в таком ключе.
–3 KvanTTT,21 февраля 2012 в 18:38#
Вы то его может и выбираете, но зато все остальные — нет.
+2 Vseslav,21 февраля 2012 в 20:25#↵↑
Статья именно об этом. Удачного Вам программирования на Вашем любимом языке.
Может быть, поделитесь с сообществом о своих предпочтениях?
0 jtootf,22 февраля 2012 в 03:56#↵↑
C++ и Haskell. Первое — как неизбежное зло (я работаю с околоэмбедщиной и нестандартным железом), второе — как универсальный рабочий инструмент.

И к математике Haskell, вообще говоря, имеет не большее отношение, чем C++ или D.
+4 EvilShadow,22 февраля 2012 в 00:55#↵↑
Смело Вы так, сразу за всех остальных сказать.
+1 KvanTTT,22 февраля 2012 в 14:46#↵↑
Минусы к комментарию заставили меня прочитать статью :)

Я то не против языка D. Мне он даже нравится в качестве замены С++.

Но я имел ввиду то, что не видел, чтобы данный язык использовался где-то в промышленных масштабах. И это самое печальное.
+8 taliban,21 февраля 2012 в 18:55#
А если от D откажутся? Шансы не меньше чем Micrisoft откажется от C#. Вот захочет человек и откажется.
+1 Vseslav,21 февраля 2012 в 20:30#↵↑
Вероятность отказа от поддержки всегда существует, тут конечно, Вы правы.
Из плюсов D в этом смысле могу сказать:
1) Развивался с 1999 года, т.е. он старше C#
2) Имеются по меньшей мере 3 открытых разрабатываемых компилятора. Собственно, C++ безопаснее C# именно из-за отсутствия патентных ограничений.
+1 augur,21 февраля 2012 в 19:03#
Действительно статье не хвататет конкретизированной практики, например:
одна и та же задача, реализованная в C++, Java и D, где последний явно выигрывает по всем характеристикам.

P.S. Всё равно статья позитивная, рад читать, что новые языки борются за свое место под солнцем и претендуют на то, чтобы потеснить старичков.
0 Vseslav,21 февраля 2012 в 20:51#↵↑
Можете почитать статью Андрея Александреску «Место для D», там есть несколько интересных примеров. Да и в приведенных мною статьях тоже есть ряд интересных примеров. Но просьба принята, в следующий раз постараюсь сделать упор в том числе на такое сравнение.
+5 tenzink,21 февраля 2012 в 19:12#
Как программисту на C++ было интересно сравнить D и C++:

1. Мне кажется, что про сборку мусора пример не очень удачный.
>> Number* c = a + b;
как вы и предлагаете, можно создавать реальный тип объекта с помощью new. Возможна техника, когда полученный таким образом объект скрывается внутри класса, который управляет его временем жизни (как пример умный указатель). Какие ограничения вы имеете ввиду, говоря о проблеме с умным указателем внутри, скрывающим детали реализации?

Судя по своему практике могу сказать, что пока не сталкивался с задачами, где отсутствие GC в языке ставило бы в тупик. Однако слышал, что GC был бы очень полезен при реализации lock-free структур данных.

2. Мне показалось не совсем корректным сравнение размеров стандарта: в стандарте языка C++11 специфицирована STL (standard template library), описание которой начинается с примерно 500-й страницы. То есть по объёму уделённому только языку, получается примерно столько же, сколько C#4. Понятно, что без библиотек сложно программировать, и программисту C#/D точно так же нужно знать библиотеки стандартные для языка. Просто они описаны в стандарте C++, но в отдельной документации для C#.
0 Vseslav,21 февраля 2012 в 21:24#↵↑
>> Какие ограничения вы имеете ввиду, говоря о проблеме с умным указателем внутри, скрывающим детали реализации?
Если в двух словах, две.
1) При передаче указателя в функцию, которая ничего не знает об умных указателях (например, из внешней библиотеки). Тогда придется блокировать умный указатель, чтобы он не освободил объект до того, как функция вернет управление (актуально в первую очередь для многопоточных программ). Соответственно, возникают проблемы связанные с вызовом функции разблокировки. Например, ее просто могут забыть вызвать или ее вызов будет пропущен в результате генерации исключения.
2) При создании циклических структур данных, например — кольцевых связанных списков, когда класс A ссылается на класс B, а B — на A. Тут, конечно, можно применять другой вид умных указателей (тот же weak_ptr), чтобы разорвать кольцо, но это еще больше усложняет проектировку и эксплуатацию таких классов.

>> Мне показалось не совсем корректным сравнение размеров стандарта
Критика принимается. С другой стороны, думаю, Вы не станете возражать, что синтаксис C++ сложен. Взять хотя бы пример со структурой классов, унаследованных от одного базового Object, который обеспечивает какую-то базовую функциональность (например, toString). Тогда в случае применения тех же умных указателей в программе будет возможен примерно такой код:

smartPrt p;
//Разные функции
p->toString();
p.toString()
0 NickLion,22 февраля 2012 в 10:37#↵↑
Всё равно пример неудачный.
Теперь представьте, что где-то в вашей программе появляется примерно такая строчка:

Number* c = a + b;

где a и b – указатели на Number, т.е. фактический тип переменных не известен.

В программе на C++ не может встретиться такая строчка, ибо не разрешено переопределять операторы для указателей. Хотя бы один должен быть классом. Так что данный пример вообще невозможен.
0 Vseslav,22 февраля 2012 в 10:42#↵↑
Оба должны быть умными указателями, т.е. классами. Извините, что не рассмотрел этот вопрос подробно — все же не основная тема статьи.
+1 NickLion,22 февраля 2012 в 13:54#↵↑
Вот. Тогда и ответ будет умным указателем. :)
Или это реализовать таким образом:
class Number;
class Integer : public Number;
class BigInt : public Number;
...
class AnyNumber
{
public:
...
private:
Number* value;
...
};

AnyNumber operator+ (const AnyNumber& a, const AnyNumber& b);

При необходимости в AnyNumber зашивается логика copy-on-write, позволяющая избежать лишних копирований таких типов как BigInt. А напрямую доступ к Number и его потомкам не давать. По сути тоже как бы умный указатель.
+1 Vseslav,22 февраля 2012 в 14:02#↵↑
Да, Вы правы. Спасибо за пример кода. Можете еще книгу Джеффа Элджера полистать, думаю, Вам может быть интересно. Там вообще очень много написано про умные указатели (их там добрый десяток) и сборку мусора в C++.
+3 leotsarev,21 февраля 2012 в 19:30#
Например, C# всегда делает проверки выхода за пределы массива, что делает подобные ошибки гораздо менее вероятными, но теряя при этом в производительности (переполнение буфера всё ещё потенциально возможно из-за возможных ошибок в реализации компилятора или стандартных библиотек).
не всегда, а только при необходимости.
var sum = 0;
for (var i=0; i<arr.Length; i++)
{
sum += arr[i];
}
никаких проверок на выход за пределы массива.
0 Vseslav,21 февраля 2012 в 20:15#↵↑
Приятно читать такие комментарии, сразу видно — в вопросе Вы разбираетесь.
В данном примере лучше использовать цикл foreach, но, думаю, Вы знаете это не хуже меня.
Действительно, компилятор C# «умный», и проверяет границы массивов только при необходимости.
Вот пример кода, где проверка границ массивов будет проведена только один раз (предполагается, что длинна массива больше 5):
var sum = 0;
int maxi = 5;
for (var i=0; i<maxi; i++)
{
sum += arr[i];
}
Тут проверка границ будет проводиться непосредственно перед входом в цикл по формуле
if(maxi<arr.Length)

Все это не вошло в статью с целью хоть как-то сократить ее объем, так как на общую мысль проверки границ массивов не влияет.
+1 leotsarev,21 февраля 2012 в 21:31#↵↑
Это я к тому, что проверка на выход за границы не так сильно замедляет программу, как кажется на первый взгляд. В большинстве случаев они будут там, где она все равно нужна и была бы в хорошо написанной программе на C
0 Vseslav,22 февраля 2012 в 03:22#↵↑
Знаете, Вы правы, но не во всех случаях. В первую очередь это касается произвольного доступа в массивах C#. Например, взять тот же алгоритм быстрой сортировки: в нормальной реализации на C будет проверка только для входных данных, в C# — при каждом обращении к массиву.
+1 jtootf,22 февраля 2012 в 03:58#↵↑
Я не специалист по C#, но отказаться от проверки выхода за границы массива можно даже в Haskell.
0 Vseslav,22 февраля 2012 в 04:14#↵↑
В C# можно делать вставки unsafe кода, для которых проверки выхода за границы массива будут отключены. Впрочем, это одновременно отключает практически все другие проверки компилятора, сборку мусора, и т.п., так что код становится похожим на C++. Собственно, поэтому небезопасный код писать на C# не рекомендовано, и подобная информация не вошла в статью.
+1 jtootf,22 февраля 2012 в 04:16#↵↑
Ну, не рекомендовано не означает нельзя. Оба языка, в общем-то, уже отошли от принципа «не платить за то, что не используется» — так какая разница, кто отошёл дальше?

Надо выполнить оптимизацию — выполним.
0 Vseslav,22 февраля 2012 в 04:21#↵↑
Тут, конечно, Вы правы, я не спорю. Просто C# по умолчанию делает проверки, а C++ — нет. Собственно, именно об этом я в статье и писал.
0 leotsarev,22 февраля 2012 в 15:23#↵↑
На C# нормальная реализация алгоритма быстрой сортировки — стандартная библиотека. Там никакой проверки внутри при каждом обращении нет
0 Vseslav,24 февраля 2012 в 14:32#↵↑
Хорошо, а что Вы будете делать, когда потребуется реализовать алгоритм, отсутствующий в стандартной библиотеке, но требующий произвольного доступа к элементам массива?
Например, при реализации:
1) алгоритма сортировки слиянием (допустим, потому что этот тип сортировки устойчивый, а реализация быстрой сортировки в C# — неустойчивая)
2) алгоритма поразрядной сортировки (потому что в ряде случаев она работает быстрее быстрой сортировки)
3) алгоритма внешней сортировки, например по алгоритму четно-нечетной сортировки слиянием Бэтчера (потому что все данные просто не помещаются в оперативную память)
4) алгоритма вычисления произведения матриц
5) алгоритма расчета обратной матрицы
и т.д.
Ваши действия?
0 leotsarev,24 февраля 2012 в 14:46#↵↑
Я выкину свои сертификаты в корзину, покаюсь и перейду на D.

Действия такие же, как и всегда:
1. Проверить наличие хорошо реализованного в системной/внешней библиотеке алгоритма. (например стабильного Enumerable.OrderBy)
2. Реализация алгоритма наиболее простым-прямым способом.
3. При необходимости — профилирование, отладка, оптимизация.

Если узким местом окажутся проверки границ массива — то в ход пойдет тяжелая артиллерия, unsafe вставки и прочая фигня.

Только практика показывает, что это все нужно в 1% случаев.
0 Mrrl,25 февраля 2012 в 23:37#↵↑
Сортировка слиянием в C# работает быстрее сортировки Array.Sort. Правда, для этого ее надо написать в unsafe mode на указателях.
+3 diamant,21 февраля 2012 в 19:49#
Введение есть, и довольно большое, перечисление особенностей вместо заключения есть, а самой статьи нет :)
+2 ApeCoder,21 февраля 2012 в 19:55#
Контрактное программирование, способное сильно упростить отладку программ и помочь компилятору лучше оптимизировать код. То, что таких возможностей нет в стандартах C++ и C#, меня огорчает.

CodeContracts msdn.microsoft.com/en-us/devlabs/dd491992
0 Vseslav,21 февраля 2012 в 20:18#↵↑
Речь шла о стандартной поставке компиляторов C# и C++.
Справедливости ради нужно добавить, что для C++ тоже есть библиотеки, добавляющие поддержку контрактного программирования.
+1 niq,22 февраля 2012 в 12:52#↵↑
В 4й фреймворк они уже включены и стороннего ничего ставить не надо. Делается это не через компилятор, а уже после компиляции «дохачивается» IL-код. Правда, из-за этого оно работает не быстро, но все таки это вполне стандартная поставка
–2 me76,21 февраля 2012 в 20:19#
«Знаменитый Червь Морриса, парализовавший интернет в 1988 году»

Прямо как в Терминаторе — Моррис заслал свой червь в прошлое, за 2 года до появления интернета, чтобы заранее парализовать его работу?
+1 Vseslav,21 февраля 2012 в 20:22#↵↑
Червь Морриса
0 Barsik107,27 февраля 2012 в 16:35#↵↑
Интернету в прошлом году исполнилось сорок лет. Отправить в 1-й класс учится заново отнимать в столбик.
–2 limon_spb,21 февраля 2012 в 20:24#
Интересно было почитать, спасибо автору.
Зацепил вопрос про сборщик мусора. Шутка, конечно удобная, но то, что из-за него теряется производительность ИМХО — факт. GC — работает — значит занимает ресурсы. Тесты, которые вы предлагаете провести — как их проводить? В языке D можно включить/выключить сборщик? А если сравнить по скорости C++ без сборщика и D со сборщиком — получится глупо.

Вот, пример из жизни про GC:
Как-то давно писал я мелкие игрушки на C++, Delphi. При грамотно написанном коде — никаких лагов и задержек. все «мегагладко».
Во всеми ивестном Flash Actionscript 3.0 есть GC. Но даже, если вы напишите на нем что-то ооочень простое, типа несколько шариков летают и умирают — вы заметите небольшие рывки раз в секунду. При большом количестве объектов они не заметны ибо и так все мельтешит, поэтому в играх их не видно.
Та же история касается и JS с его GC, вот пример
jsfiddle.net/paul/XQpzU/
если присмотреться — шарик дергается раз в секунду или две, хотя код — проще некуда.

так что GC — не есть добро по умолчанию.
0 limon_spb,21 февраля 2012 в 20:27#↵↑
забыл добавить, что когда копался из-за чего эти рывки в JS и AS — знающие люди рассказали мне, что это GC :-)
0 mayorovp,21 февраля 2012 в 20:32#↵↑
К сожалению, я настолько привык к лагающим программам, что эти мини-рывки до сегодняшнего дня не замечал.
0 limon_spb,21 февраля 2012 в 21:22#↵↑
Конечно, они еле заметны, но тем не менее из-за GC сделать гладкую анимацию, как в C++ в JS и AS — невозможно. Интересно, как обстоят дела в D?
Может, автор статьи напишет что-то простенькое в стиле jsfiddle.net/paul/XQpzU/ для проверки?
0 mayorovp,21 февраля 2012 в 21:29#↵↑
Они не еле заметны, они ужасно заметны, но уже успели прочно войти в нашу жизнь.
0 limon_spb,21 февраля 2012 в 22:49#↵↑
Надеюсь, вы не отвернетесь от флэша или хтмл5 :-) Когда я показал эти лаги знакомому флэшеру, тот сказал: «ну что делать?.. забей! они не таки уж и сильные :-)»
+2 mayorovp,21 февраля 2012 в 23:09#↵↑
Я боюсь, придется отвернуться от компьютера.
Мне уже снятся сны, лагающие на красивых эффектах.
Если они будут еще и подергиваться…
0 solver,23 февраля 2012 в 12:07#↵↑
Есть такая штука — пул объектов. Поможет в борьбе рпотив GC.
0 leotsarev,21 февраля 2012 в 21:35#↵↑
не знаю, как в D, но в C# сборщик мусора в пользовательских приложениях конкуретный, т.е. ему не нужно усыплять все остальные потоки.
–1 dikarik,21 февраля 2012 в 22:01#↵↑
Включить/выключить — да, можно. Можно писать вообще без использования сборщика мусора — но тогда потребуется своя стандартная библиотека. По скорости — как раз за счёт сборки мусора (и иммутабельности) в D получилась очень эффективная работа со строками — к примеру, подстрока отдается как ссылка, без копирования и подсчета ссылок.
+1 dmbreaker,22 февраля 2012 в 21:55#↵↑
Это к GC отношения не имеет. Такие строки на C#, на Java, и можно написать такой класс для C++, где нет GC.
+2 szKarlen,21 февраля 2012 в 22:12#↵↑
проблема всех VM для JS в том, что на данный момент их GC являются stop the world сборщиками. в таких платформах как .NET GC — Generational.
в этом плане у C# проблем нет. более того с .NET 4 дела обстоят еще лучше.
0 limon_spb,21 февраля 2012 в 22:44#↵↑
У меня никаких претензий к минусующим, и даже ответные минусы ставить не буду, если они откроются :-). Мне действительно интересно, в чем же ляп, или ошибка, или почему не стоило писать этот комментарий/вопрос?
Это отлично, что в D сборщик сделан по другому принципу, нежели, чем в JS или AS. Я, например, этого не знал, возможно кто-то другой — тоже не знает. А возможно, для кого-то эти лаги окажутся открытием.
+4 jtootf,22 февраля 2012 в 04:03#↵↑
Ошибка как минимум в выводах, основанных на незнании. Примитивный mark and sweep GC — это одно, современный GC с регионами и поколениями — совсем другое. Есть и RT Java в конце концов.
+1 limon_spb,22 февраля 2012 в 09:23#↵↑
Вроде выводов особо не сделал. Я же не утверждаю, что GC — доктор зло. Просто сказал, что наличие сборщика мусора в языке само по себе ни о чем не говорит. В подтверждение привел пример, когда GC реально «тормозит процесс». И, как выяснилось, я был прав, а вот наличие «современного GC с регионами и поколениями» — уже может о чем-то сказать. И это дописали в комментариях, за это спасибо вам и остальным понимающим людям :-)
+1 burjui,22 февраля 2012 в 01:05#↵↑
В D можно сборщик мусора отключить.
0 elmm,22 февраля 2012 в 02:12#↵↑
В немалой степени лаговость GUI андроида обьясняют именно наличием сборщика.
Я склонен этому верить.
Так что тут палка о двух концах.
+1 Vseslav,22 февраля 2012 в 03:43#↵↑
Вы знаете, C++ и Delphi — языки компилируемые, а Flash Actionscript 3.0 — интерпретируемый. Так что, с моей точки зрения, сравнивать производительность C++ и Actionscript просто некорректно.
0 limon_spb,22 февраля 2012 в 09:26#↵↑
Согласен с вами, однако, именно GC в AS и JS, как выяснилось приводит к тому, что гладкой анимации не добиться. К счастью в D он устроен иначе и не тормозит процесс. Жаль, что в подтверждение не приводят не «лагающих» примеров =)
+4 mayorovp,21 февраля 2012 в 20:30#
Про отсутствие деструкторов в C# вы зря так категорично написали
В большинстве ситуаций интерфейс IDisposable и конструкция using — неплохая альтернатива

По крайней мере, у нас за два года работы не было ни одной ошибки, связанной с забытым using
–1 Yavanosta,22 февраля 2012 в 02:16#↵↑
Кстати есть еще и деструкторы. Но не рекомендуется полагаться на них, т.к. они вызываются недетерминированно. Но как «последний рубеж» освобождения unmanaged ресурса в случае краха (или забытого using) самое то.
+2 Vseslav,22 февраля 2012 в 02:28#↵↑
Это не деструкторы (в C++ смысле). В C# именно деструкторов нет. То, что Вы показали — именно завершители, вы же сами пишете, что они вызываются недетерминированно. Просто синтаксис объявления завершителей в C#и деструкторов в C++ фактически одинаковый (~ИмяКласса), отсюда и путаница.
+1 mayorovp,22 февраля 2012 в 10:06#↵↑
Как уже было сказано, это не деструкторы.
Аналогом плюсового деструктора является именно метод Dispose интерфейса IDisposable.

Конструкция using является аналогом создания объекта на стеке.
0 Vseslav,22 февраля 2012 в 11:19#↵↑
Согласен, между методом Dispose и деструктором C++ можно провести параллель. Тем не менее, согласитесь, в C# нужно или явно вызывать метод Dispose, или явно использовать конструкцию using, а C++ вызывает деструкторы автоматически.
Хотя я не спорю — при вызове метода Dispose в нужных местах проблем с освобождением ресурсов не будет.
+1 dokwork,21 февраля 2012 в 20:59#
Статья, ради которой приятно зайти на хабр. Плюсов вряд ли наберет много, тк тема порно и копирастов не раскрыта, но мое «спасибо» автор честно заслужил.
+1 gribozavr,21 февраля 2012 в 21:07#
> Судите сами: стандарт только на C занимает около 500 станиц, C++ – около 800, C++11 – около 1300. Если сравнить объем технической документации – этот язык по сложности явно превосходит миксер, швейную машинку и автомобиль, приближаясь скорее к самолётам. Для сравнения, стандарт C# 4.0 занимает всего 505 страниц.

Давайте сделаем честное сравнение. Приложите к спецификации на C# спецификацию на его библиотеки.

И получите следующую картинку (Going Native 2012, keynote day 2): www.habrastorage.com/images/zzz.png
0 Monnoroch,21 февраля 2012 в 21:39#↵↑
Ну знаете, библиотеки — несомненно важная вещь, но они не язык. Разговори идет о сложности языка, а не библиотек. Хотя я тоже против того, чтобы числом страниц мерить сложность. Я читал книгу Страуструпа про С++, ту, которая основная, без продвинутых штук, и читал книгу Алуксандреску про D. Так вот, во-первых мое личное впечатление таково, что первая существенно сложнее, несмотря даже на то, что вторая на английском.
Более того, от книги страуструпа у меня остался такой осадок, что примерно 40-50% информации — описание и способы избежания возможных ошибок.
В кнге про D это примерно 4% — навскидку припоминается только: «Не используйте сишные указатели, это небезопасно». Так что касательно сложности языка автор абсолютно прав. Способов выстрелить себе в ногу при использовании SafeD почти что и нет, а если использовать низкоуровневые штуки — так они просто копируют функциональность си.
+1 gribozavr,21 февраля 2012 в 21:50#↵↑
> Ну знаете, библиотеки — несомненно важная вещь, но они не язык.

Я говорил о другом. Из упомянутых 1300 страниц C++11 больше половины — STL. Сравнивать со спецификацией на C# по объёму нужно ту часть стандарта, которая описывает язык — а их там остаётся как раз около 500.

> Более того, от книги страуструпа у меня остался такой осадок, что примерно 40-50% информации — описание и способы избежания возможных ошибок.

А у D есть спецификация, при составлении которой дотошно рассмотрена каждая возможная комбинация фич языка? Продуманы undefined behaviors? Есть наработки по статическому анализу? Если нет — то мы просто не знаем, какие паттерны в D являются склонными к ошибкам, поэтому и в книгах об этом не пишут.
0 dikarik,21 февраля 2012 в 22:04#↵↑
по D еще мало статистики касательно склонности к ошибкам, но в обшем выстрелить себе в ногу в нем обычно можно, но для этого надо что-нибудь явно сделать. К примеру, хотите экземпляр класса положить на стек — можно, но нужно будет использовать специальный хелпер из библиотеки, который легко ищется/рефакторится.
0 Yavanosta,22 февраля 2012 в 02:24#↵↑
А как коррелируют характеристики языка и объем документации? Если чем меньше документация тем лучше язык то давайте кодить на brainfuck или машине тьюринга. Там документация на страницу влезет размашистым почерком.

Сложность языка не определяется объемом документации по-моему. Ну хотя как, если документации вообще нет, то язык не простой получился. А если она есть и исчерпывающая, то чем её больше, чем больше там примеров и best pracrice тем лучше, разве нет?
0 Vseslav,22 февраля 2012 в 04:07#↵↑
Критика по поводу объема документации принимается.
Тем не менее, писал в первую очередь о том, что C++ сам по себе сложен.
+3 CleverMouse,21 февраля 2012 в 21:54#
в нём есть встроенная поддержка проверки границ массивов для отладочной версии программы, которая отключается при компилировании оптимизированной версии

Что толку от проверки, которая включена только в отладочном режиме? Ошибки в безопасности, как правило, при нормальной работе не проявляются, нужно скормить программе специально подобранные данные. Злобные хакеры-то будут смотреть на конечный продукт, в котором компилятор заботливо отключил проверки.
он полностью совместим с кодом C/C++ на уровне объектных файлов, что позволяет получить прямой доступ к функциям и классам, написанным на C/C++ и наоборот

Даже сам C++ несовместим с кодом C++ на уровне объектных файлов. Нестандартизированного манглинга имён уже достаточно, чтобы объектные файлы от разных компиляторов были несовместимы, а уж когда появляются исключения — а они всегда появляются, мало кто использует new(nothrow) — то на переносимости можно смело ставить крест.
0 dikarik,21 февраля 2012 в 22:07#↵↑
Автор очень много оставил за скобками. Разумеется, совместимость не полная — она достаточна для того, чтобы без особой мороки связать код на плюсах и D. Ну а если совсем припрёт — придётся имена с явным манглингом писать как extern «C» — так тоже можно. Впрочем, в никсах царит GCC и вроде как совместимый с ним LLVM.
+2 burjui,22 февраля 2012 в 01:23#↵↑
Автор статьи некорректно выразился: проверку границ можно отключить при помощи ключа -noboundscheck, а так она есть и в оптимизированной версии.

Что до совместимости с C++, это только с компилятором от Digital Mars, увы. Впрочем, хоть так можно — и то хорошо: C++ в плане интеграции с другими языками особняком стоит.
0 Vseslav,22 февраля 2012 в 10:48#↵↑
Если точнее, компилятор Digital Mars D нужно использовать вместе с Digital Mars C для Windows и с GCC для остальных платформ.
0 eveel,21 февраля 2012 в 23:24#
Всем хорош язык D, особенно возможностью линковаться с сишным кодом в обе стороны (по крайней мере, при использовании компилятора от Digital Mars). Особенно, если закрыть глаза на то, что у языка есть две разных стандартных библиотеки.

Жалко, что нет крупных компаний, которые делают ставку на этот инструмент. Вот у языка Go этой проблемы нет.
0 Vseslav,21 февраля 2012 в 23:36#↵↑
У Вас немного устаревшие данные. Две стандартных библиотеки (Phobos и Tango) были в D1. В D2 осталась только одна (Phobos), Tango все еще не портирована под D2.
0 Volfram,22 февраля 2012 в 16:35#↵↑
Портирована.
forum.dlang.org/post/jgagrl$1ta5$1@digitalmars.com
+3 f1ac,21 февраля 2012 в 23:29#
Самое главное — в D нет .h файлов :)
0 Vseslav,22 февраля 2012 в 11:07#↵↑
Да, Вы правы, нет.
Всю информацию компилятор получает сам из исходного кода. Если необходимо выпустить библиотеку без исходных кодов, то компилятор сам может создать D Interface Files, которые фактически будут играть роль .h файлов.
0 btd,21 февраля 2012 в 23:33#
Статья интересная, спасибо. Только маленькая поправочка — SQL с рекурсивными подзапросами полный по тъюрингу (SQL1999).
0 Vseslav,22 февраля 2012 в 11:11#↵↑
Спасибо за уточнение.
+4 egorinsk,21 февраля 2012 в 23:45#
D — очень хороший, продуманный и синтаксически сладкий язык. Меня например, привлекает то, что он компилируемый (несмотря на рост процессоров и железа, интерпретаторы по-прежнему адски медленные, особенно когда построишь большое дерево классов и объектов), но при этом на нем удобно писать.

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

И совместимость с Си-библиотеками — тоже отличная особенность.

Язык очень хорош. Увы, почему-то он не популярен. Видимо, потому, что за ним не стоит ни маркетинга от крупных корпораций, ни компаний вроже 37 signals, ни армии «чайников», ни элитного флера зазнаек-хаскеллистов, ни производителя телефонов, ни линуксоидов.
+3 Yavanosta,22 февраля 2012 в 02:20#↵↑
Вот пока язык не найдет себе кого-то кто будет за ним стоять, никто на нем писать не будет. Автор столько распинался на то что в 2016 году майкрософт выкинет виндоус и тысячи c# программистов на мороз, поэтому на нем опасно писать, но совершенно не ясно что будет если автору D — профессиональному разработчику компиляторов надоест его разрабатывать. Ну или начнет делать E.
+2 leotsarev,22 февраля 2012 в 15:26#↵↑
интерпретаторы по-прежнему адски медленные, особенно когда построишь большое дерево классов и объектов
Большинство серьезных конкурентов D — языки с JIT.
0 andreycha,22 февраля 2012 в 00:55#
Вы можете мне объяснить, почему Tango D так зверски работает с XML'ем?

dotnot.org/blog/archives/2008/03/10/xml-benchmarks-parsequerymutateserialize/
dotnot.org/blog/archives/2008/03/12/why-is-dtango-so-fast-at-parsing-xml/

Что-то объяснения про оптимизированную работу строк не сильно впечатляют.
0 Vseslav,22 февраля 2012 в 10:38#↵↑
Tango — библиотека для D1. Выше упомянутая работа со строками относится к D2 — именно там строки сделаны неизменяемыми.
0 Vseslav,27 февраля 2012 в 21:31#↵↑
По оси X на указанном Вами графике — среднее число циклов в секунду.
>> After the run, I take the average cycle time, and turn that into the followin graph showing cycles per second.

Другими словами, Tango примерно в 78 раз быстрее Java6 DOM.

>> Вы можете мне объяснить, почему Tango D так зверски работает с XML'ем?
Может быть Вы подумали, что по оси X — время?
0 icegreenberry,22 февраля 2012 в 01:37#
Как обстоят дела с GUI библиотеками?
0 Vseslav,22 февраля 2012 в 03:53#↵↑
Есть:
GtkD — порт GTK+
DWT — порт SWT
QtD — порт Qt
Полный список смотрите здесь.
0 lightman,22 февраля 2012 в 01:58#
Странно, я где-то читал, что
а. У D проблемы с цеплянием C++ библиотек. Или это относится только к либам, а с объектниками всё нормально?

б. Сборщик мусора в последних версиях чуть ли не обязателен, а delete считается deprecated. Не так?
0 Vseslav,22 февраля 2012 в 03:49#↵↑
В D1 правда нельзя подключать C++ библиотеки, только C. В D2 также добавили поддержку подключения C++ библиотек.

Выше в комментариях уже писали, что в D можно отключить сборщик мусора.
+1 Volfram,22 февраля 2012 в 16:51#↵↑
a.
«проблем», как таковых, нет, просто, в отличии от поддержки C ABI, поддержка C++ ABI не полная. Поэтому, насколько мне известно, в release-версиях стараются использовать extern «C» прослойку, просто для надёжности. Подробнее здесь: www.d-programming-language.org/cpp_interface.html

б.
delete как ключевое слово языка, действительно, deprecated. Причины можно найти на офф. форуме, сейчас, увы, нет времени искать — довольно давно было. Помню жаркую дискуссию. Что, впрочем, ничуть не мешает иметь функциональность delete, если есть такая необходимость ( а она есть редко ).

Сборщик мусора никогда не был и не будет обязательным. Просто нужно отдавать себе отчёт, в том, что при его полном отключении изрядная часть стандартной библиотеки будет недоступна ( вся память будет утекать ), как и некоторые особо «сладкие» нативные фишки. Язык в таком состоянии больше напоминает better C, нежели better C++.

Обсуждения насчёт того, что было бы неплохо иметь стандартную библиотеку в режиме reference counting, если сборщик мусора отключен, ведутся постоянно и, в целом, особо никто не возражает. Но соотношение практической ценности этой фичи относительно трудозатрат делают её далеко не приоритетным направлением.
0 IRainman,23 февраля 2012 в 02:34#↵↑
***Сборщик мусора никогда не был и не будет обязательным. Просто нужно отдавать себе отчёт, в том, что при его полном отключении изрядная часть стандартной библиотеки будет недоступна ( вся память будет утекать ), как и некоторые особо «сладкие» нативные фишки. Язык в таком состоянии больше напоминает better C, нежели better C++.***

Жалко, «продолжаем писать на С++». Есть много мест где возможность самостоятельного управления памятью критична по соображениям производительности.
+1 Volfram,23 февраля 2012 в 02:41#↵↑
Возможно, я недостаточно точно выразился.
Ручное управление памятью и gc — не противоречащие друг-другу понятия. Даже при включенном gc всегда доступны malloc, free и аллокация на стеке + можно пометить любые блоки памяти как недоступные gc для обхода. Или приостановить gc на какой-то момент для выполнения real-time блока. Это ничем не грозит.

Речь же в предыдущем комментарии шла о ситуации, когда gc отсутствует в принципе, не включен druntime на этапе компиляции. Это важно, в основном, только если вы хотите уместить программу на D на какую-нибудь очень embedded платформу, куда garbage collector просто не влезает.
+6 Yavanosta,22 февраля 2012 в 02:07#
> В результате это привело к трагедии для тысяч программистов, работающих на Basic’ке. К тому же, по сообщениям в прессе, в 2015-2016 году Microsoft планирует отказаться от бренда «Windows», создав новую ОС для планшетов, смартфонов, компьютеров, консолей, телевизоров и других устройств, а, значит, языки для разработки под эту ОС тоже могут кануть в небытие.

Про mono я полностью согласен, но вот про windows… Я не пойму, вы вот представляете это как? Что вот придет 2016 год, выйдет глава майкрософт, скажет: «все, нет больше виндовс, и мы выкидываем ее, еще заодно и c#, вместо них теперь Doors и Ab (а бемоль)»

Мы все: «А-а-а-а-а-а, как дальше жить?», а вы так гордо «хаха, а я вот сижу на D, и мне ничего не страшно».

Вам не кажется, что в условиях такого резкого перехода D тоже не выживет? Скорее уж выживет CLR, которую просто портируют на новую Doors. Не будет работать то что завязано на WinAPI, а все что работает в рамках CLR прекрасно заведется и на новой Microsoft Doors с портированной на нее CLR.

Мне лично такой вариант развития событий представляется более вероятным.

Еще:
>Последнее означает, что прекрасный компонент ASP.NET, служащий для построения сайтов различных масштабов, не имеет ровным счетом никаких преимуществ перед тем же PHP.
Как же это то что .NET завязан на windows, позвольте спросить, означает что он не имеет никаких преимуществ перед php? При всем моем уважении к php .NET даст ему гору очков вперед почти по всем критериям оценки. Начиная с того что c# типизированный, компилируемый и имеет в составе гору встроенных примитивов, в отличии от скудной объектной модели php и заканчивая тем что средства разработки и тестирования под .NET стоят на три лестничных пролета выше чем под php.

> Ну а если вдруг вам все же захочется использовать его в своих проектах, будьте морально готовы к покупке лицензий на серверную версию Windows.
Я не понимаю, ну и что? Первая ссылка из гугла показывает что Windows Web Server 2008 стоит 10 765 рублей. Это зарплата одного программиста максимум за неделю. А еще её можно «взять в аренду» в месте с хостингом: арендуя VPS на Windows вы автоматически «арендуете» совершенно лицензионную копию windows. Это стоит от 700р в месяц. Вот то что вижуал студия стоит 25к за рабочее место еще можно было бы рассмотреть, но есть скидки и спец предложения для малого бизнеса. Да и не так это много в масштабах организации. Снять в Москве офис на 2-3 месяца будет стоить столько же сколько купить лицензионного софта на всю команду.
0 Yavanosta,22 февраля 2012 в 02:10#↵↑
я мог бы еще кое с чем поспорить, но тогда будет слишком уж длинно
0 Vseslav,22 февраля 2012 в 11:38#↵↑
Если есть критика по существу — всегда готов выслушать. Именно в результате подобных дискуссий рождается взаимное понимание.
0 Yavanosta,22 февраля 2012 в 02:50#↵↑
топику тем не менее поставил плюс. написано хорошо, хоть я и не согласен со многим
0 Vseslav,22 февраля 2012 в 11:36#↵↑
>> Скорее уж выживет CLR, которую просто портируют на новую Doors.
Вы знаете, .NET почти полностью написана с использованием WinAPI. Просто в документации все методы, реализованные именно с помощью .NET, отмечены специальным значком, и таких методов сравнительно мало. Так что фактически придется переписывать большую часть кода платформы .NET, чтобы ее портировать под другую ОС.

>> Как же это то что .NET завязан на windows, позвольте спросить, означает что он не имеет никаких преимуществ перед php?
Я имел ввиду, что на Unix-платформах нельзя использовать ASP.NET, а раз его нельзя использовать, то он не даёт никаких преимуществ. Если сравнивать именно языки, то я с Вашей позицией согласен — платформа ASP.NET действительно лучше PHP. Извините, что не выразил свою мысль достаточно внятно.

>> Первая ссылка из гугла показывает что Windows Web Server 2008 стоит 10 765 рублей.
Собственно, я и имел в виду, что при использовании ASP.NET скорее всего придется покупать лицензионную Windows. А нужно ли это делать — каждый решает для себя сам. У разных ОС свои плюсы и свои минусы, с вашего разрешения, давайте отложим эту дискуссию на другой раз — всё же это не является непосредственной темой статьи.
+4 Yavanosta,22 февраля 2012 в 11:48#↵↑
> Вы знаете, .NET почти полностью написана с использованием WinAPI. Просто в документации все методы, реализованные именно с помощью .NET, отмечены специальным значком, и таких методов сравнительно мало. Так что фактически придется переписывать большую часть кода платформы .NET, чтобы ее портировать под другую ОС.
Ну так оно и предполагалось в общем-то. CLR своего рода прослойка между .NET приложением и конкретной ОС. Отсюда и необходимость JIT компиляции из MSIL. Вполне логично что в windows CLR использует WinApi. А в Unix Mono использует наверное какие-нибудь вызовы ядра. И что в этом плохого? .NET приложению не надо об этом думать. CLR сама как-нибудь разберется, ну не сама, а конечно благодаря труду программистов портировавших её на целевую платформу, но программисту .NET приложения это безразлично. С его точки зрения все «само».

Про заначек. Ни разу не видел, как он выглядит? Скиньте ссылку на MSDN где он есть. Не в порядке спора или требования пруфлинка, просто заинтересовали меня, я с удовольствием почитаю про этот значок и отличие методов помеченных им и не помеченных.
0 Pavel7,22 февраля 2012 в 14:39#↵↑
Тоже заинтересовал этот значок, где его можно посмотреть в MSDN?
0 Vseslav,22 февраля 2012 в 15:19#↵↑
Знаете, видел этот значок в документации к Microsoft Visual Studio 2005 (не Express версия, а обычная). Пользовался именно встроенной в среду справкой, а не ее онлайн версией, для .NET 2.0.
Мне кажется, что в то время онлайн версии документации вообще не было, но за последнее утверждение не ручаюсь. Собственно, именно в MS VS 2005 я этот значок и видел. Встречался он почти в каждом классе, примерно у одной-двух функций. Выглядел примерно как оранжевая иконка .NET, всплывающая подсказка гласила примерно следующее: «This method provided by .NET» (за точность фразы ручаться не могу, пишу по памяти), что наводило на простую мысль — остальные методы предоставляются кем-то еще. В литературе встречались утверждения, что .NET почти полностью реализован на основе WinAPI, поэтому в отношении «не помеченных» функций сделал соответствующий вывод.
Дальнейшая судьба значка мне неизвестна, возможно, Microsoft отказалась от распространения подобной информации.
+2 leotsarev,22 февраля 2012 в 15:30#↵↑
Вы знаете, .NET почти полностью написана с использованием WinAPI
А стандартная библиотека С/С++/D/whatever для Windows что, с использованием святого духа написана? Все равно, если мы на платформе Windows открываем файл, мы всегда вызываем CreateFile, а он всегда вызывает NtCreateFile.
Если Microsoft откажется от WinAPI, то стандартную библиотеку для С/С++/D/whatever все равно переписать придется.
–2 qmax,22 февраля 2012 в 05:59#
«Константность и неизменяемость переменных» — это весело!
Веселее (и безопаснее) может быть только язык вообще без переменных.

Если не лень — нельзяли вот об этой особенности поподробнее?
Какие мега-удобства это привносит (ну, кроме очевидных проблем с многопоточностью)
0 Vseslav,22 февраля 2012 в 11:52#↵↑
Вы знаете, как правило в библиотеке D для всех ленивых функций реализован их обычный вариант. Так что Вы сами выбираете, что именно Вам нужно — получить результат сейчас, или отложить вычисления «до востребования».
Что касается преимуществ ленивых вычислений, то, если навскидку, возможность оперировать с бесконечно длинными последовательностями.
По поводу многопоточных вычислений — Вы неправы (по крайней мере в отношении D). В языке память по умолчанию локальна для потока, чтобы передать данные в другой поток переменную нужно явно объявить разделяемой, что позволяет компилятору проводить ряд проверок.
По поводу «константности и неизменяемости переменных» — речь шла о такой возможности (которая, к слову, облегчает написание многопоточных программ). Будете Вы ее применять или нет — зависит от Вас.
0 VoidEx,22 февраля 2012 в 07:32#
Почти весь ряд особенностей есть и в Haskell, и многое другое. Почему же нет раздела «немного о недостатках Haskell»?
0 Vseslav,22 февраля 2012 в 12:03#↵↑
Возможно, потому что не было достаточно много опыта программирования на Haskell’е.
0 VoidEx,22 февраля 2012 в 15:05#↵↑
Тогда рекомендовал бы приобрести
Если выбор и не поменяете, то время проведёте весело :)
+1 Volfram,22 февраля 2012 в 16:55#↵↑
Если некий С-подобный язык и Haskell обладают сравнимым набором особенностей в заданном контексте, какой же окажется естественным выбором для программиста с опытом С/С++?
0 VoidEx,22 февраля 2012 в 17:39#↵↑
Я не говорил «сравнимым». Ряд особенностей Haskell куда больше, поэтому не изучить и игнорировать его было бы глупо. А уж выберет ли его потом программист с опытом на C/C++, дело программиста. Я лично выбрал, и далеко не только я (почти все Haskell'исты с C/C++ background'ом).
0 Volfram,22 февраля 2012 в 17:50#↵↑
«в заданном контексте»
Ряд особенностей D2 тоже куда больше. Я бегло пробовал и D2, и Haskell, увидел, что D2 покрывает все возможности, что мне интересны в Haskell и не нашел причин изучать его дальше. Более того, Haskell ведь, как я понимаю, всё ещё придерживается строго функционального подхода? Если да, это большой аргумент против.
0 VoidEx,22 февраля 2012 в 18:05#↵↑
В заданном лично вам контексте — может быть. Но речь не только о вас.
А строгая функциональность — это большой аргумент «за». Но понять это можно только попрограммировав на Haskell.
+1 Volfram,22 февраля 2012 в 18:11#↵↑
Заданном автором в статье.
Строгая функциональность — отрада для математика и бессмысленное ограничение для программиста. Особенно для С/С++ программиста. По крайней мере, пока не появятся компьютеры строго функциональные на уровне машинного кода ;) Я вполне понимаю преимущества, которые дают гарантии функционального подхода, особенно для многопоточности. В D2 это есть.
В общем же случае предпочитаю не создавать дополнительных абстракций там, где они не требуются.
0 VoidEx,22 февраля 2012 в 18:40#↵↑
Строгая функциональность — отрада для математика и бессмысленное ограничение для программиста.

Вы много писали на Haskell?

Особенно для С/С++ программиста.

Странно говорить это C++ программисту, вы не находите?
0 Volfram,22 февраля 2012 в 18:43#↵↑
А как насчёт аргументации по существу?
Назовите преимущества строгой функциональности, из-за которой я мотивирован себя ограничивать в способе выражения мыслей.
0 VoidEx,22 февраля 2012 в 20:40#↵↑
Ну например строгая статическая типизация, по-вашему, это лишняя сущность и бессмысленное ограничение для программиста?

По типу функции Haskell можно видеть не только типы аргументов и результата, но и некоторые важные аспекты функции, наличие состояния, использование ввода-вывода и т.п. Языки с зависимыми типами идут и того дальше, позволяя зафиксировать в типе функции например факт того, что она именно сортирует список, а не делает что-либо ещё.
Причём на существующую систему типов ложатся и исключения, и ввод-вывод, и множественные результаты, и то многое другое, ради чего в других языках городятся специальные конструкции.
Но при этом лишние сущности почему-то именно в Haskell.
+2 Volfram,22 февраля 2012 в 23:54#↵↑
1) Строгая типизация не ограничивает возможностей, она лишь заставляет описывать преобразования типов явно. Размен небольшого синтаксического сахара на предсказуемость поведения.
2) Статическая типизация ограничивает, предлагая взамен большую производительность и, в сочетании со строгой, возможность задавать некие контракты.

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

Всё ещё не вижу связи между обязательной строгой функциональностью и списком фич. В D2 тоже по типу функции можно определить некоторые аспекты, такие как pure, nothrow или safe. Обошлись как-то без ограничений.

Хочу заметить, что я нигде не упоминал «лишние сущности», только «бессмысленные ограничения». Это не имеет отношения к теме.
0 VoidEx,23 февраля 2012 в 06:52#↵↑
никак неявных преобразований целых чисел

Ну если это для вас действительно лишняя сущность, то значит у нас слишком разные взгляды. От неявных преобразований тоже немало ошибок возникает, а ничего плохого в явном касте (очевидном, в первую очередь, для читающего код) я не вижу. А вот в неявном — вижу. Где там floor произойдет? Для этого при чтении в уме надо типы держать, а не просто читать явное floor b + c.

В D2 тоже по типу функции можно определить некоторые аспекты, такие как pure, nothrow или safe. Обошлись как-то без ограничений.

А асинхронный код как синхронный (плоско) там можно писать?
А ФВП позволяет указать, что принимаемая ей функция pure и nothrow (и соот-но проверит ли это компилятор)?

Нет никаких ограничений, вы вполне вольны написать
foo :: MVar Int -> MVar Int -> IO (MVar Int)
Тут и мутабельность, и ввод-вывод.
Если пугает синтаксис, ну так можно ж упросить, это непринципиально.

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

Ограничения на практике оказываются полезными.
То, что мутабельный стейт-фул код писать немного напряжнее — это ж хорошо! Задаёт верные приоритеты. Никто не мешает его писать, но:
1. Это явно отражено в типе функции и читатель сразу видит контракты
2. Провоцирует писать более чистый код с декомпозицией (что упрощает, например, тестирование, так как набор чистых функций тестировать — сказка)
3. «Грязный» код пишется лишь тогда, когда действительно нужен, а не везде, где ни попадя.
0 Volfram,23 февраля 2012 в 23:56#↵↑
Отвечая на ваше сообщение, наткнулся на любопытное поведение компилятора dmd, которое явно стоит внимания. Пардон, нужно некоторое время, чтобы ответить точно.
+1 Volfram,24 февраля 2012 в 16:11#↵↑
А ФВП позволяет указать, что принимаемая ей функция pure и nothrow (и соот-но проверит ли это компилятор)?
Да.
void hof( int function() nothrow pure f ) { }


А асинхронный код как синхронный (плоско) там можно писать?
Этот вопрос не понял, мне незнаком термин «плоское написание асинхронного кода».

Касательно неявных преобразований, как минимум, привычной идиомы if(ptr) мне бы не хватало. Есть некоторое количество преобразований, которые на практике достаточно безопасны и удобны в неявном виде. Я это к тому, что цель языка программирования — быть удобным и практичным, а не предоставить строгую красивую модель.

Как насчёт вставки в ваш мутабельный пример inline assembler'a? Если уж он действительно такой императивно-мутабельный, а не просто имитирует стиль, взгромождая ещё одну абстракцию?

Вы исходите из предпосылки, что императивный код «грязнее» и хуже только потому, что он императивный. Кстати, императивный стиль не обязательно подразумевает мутабельность, что вполне показано в D2. До тех пор, пока эта предпосылка существует, нам очень трудно будет найти общий язык.
+1 VoidEx,24 февраля 2012 в 19:57#↵↑
Этот вопрос не понял, мне незнаком термин «плоское написание асинхронного кода».

Например как тут.
Т.е. вместо явной передачи callback мы пишем, к примеру
x <- download "foo";
lift $ print "foo is downloading"
y <- download "bar";
lift $ print "bar is downloading"
x' <- await x
y' <- await y
return (x' + y')

Это как пример, нафантазировать можно то, что удобнее.

Я это к тому, что цель языка программирования — быть удобным и практичным, а не предоставить строгую красивую модель.

Основной момент в «мне бы не хватало». Поверьте, мне очень многого не хватало из C++ поначалу, но потом оказалось, что это от лукавого :)
Строгая красивая модель делает язык удобным для чтения, ибо мало частных случаев и каких-то ситуаций, где только знание каких-то нюансов стандарта помогает понять, что происходит. Всё декларируется достаточно явно.

Как насчёт вставки в ваш мутабельный пример inline assembler'a?

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

Кстати, никто не мешает и в чистой функции пользоваться state'ом через ST монаду, она позволяет использовать переменные только внутри, поэтому результат гарантированно чистый (причём опять же, никаких хаков). Это похоже на ограничения pure в D.

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

Вовсе нет. Я исхожу из предпосылки, что чем меньше мы можем наложить контракта на функцию, тем больше вероятность ошибок.
В динамике никаких ограничений не наложить, в статике дела получше — можно задать типы, но как правило не запретить ввод-вывод и прочее. В Haskell благодаря чистой функциональности и это выводится на уровень системы типов — можем по типу задать эффекты функции. В языках с зависимыми типами (Agda, Coq) можно статически гарантировать практически всё, что угодно. Например, что функция именно конкатенирует списки, а не делает что-либо ещё.
0 Volfram, 1 марта 2012 в 17:27#↵↑
Прошу прощения за долгие ответы, надо дать себе зарок не ввязываться в дискуссии в разгаре дедлайна.

import std.stdio;
import std.file;
import std.parallelism;

// Синтаксический сахар, которого не нашёл в стандартной либе.
// Возможно стоить сделать pull request для phobos :)
auto launch(T)( T new_task )
{
struct Launched
{
private:
T task;
public:
this(T new_task)
{
task = new_task;
task.executeInNewThread();
}

auto result()
{
return task.yieldForce();
}
}

return Launched(new_task);
}

void main()
{
auto task1 = launch( task!readText("test1.txt") );
auto task2 = launch( task!readText("test2.txt") );
write( task1.result ~ task2.result );
}


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

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

… мало частных случаев и каких-то ситуаций, где только знание каких-то нюансов стандарта помогает понять, что происходит

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

И хороший практичный язык, ИМХО, должен иметь множество частных случаев и нюансов стандарта. И сам стандарт на тысячи страниц. С тем лишь отличием от С++, что незнание этих нюансов не должно бить новичка по лицу, пока он с ними не знаком.

Ну ассемблер напрямую не вставить, ибо Haskell куда выше уровнем, да и компилироваться может через LLVM. Но звать сишные функции никто не запрещает
Я это к тому, что у Haskell и у D несколько разные дизайн-цели. И в D необходимым требованием является то, что встроенный ассемблер должен выглядеть так же естественно, как и функциональный стиль. Равноправное использование всех парадигм.
0 VoidEx, 1 марта 2012 в 20:20#↵↑
Что такое task!readText?

Я привёл несколько неудачный пример. Вот пример поудачнее:

do
send int 1
x <- receive int
send string ("hello" ++ show x)
y <- receive string
return (show x ++ y)


send — отправляет сообщение клиенту
receive — получает сообщение

Однако receive не блокирует, так как на деле он не ждёт сообщения, а регистрирует callback. Т.е. всё, что ниже receive — автоматом callback.

Другой пример:
do
line <- lines contentOfFile
word <- words line
ch <- word
if ch == 'x' return True else return False


Аналогичен:
foreach (line in lines(contentOfFile))
foreach (word in words(line))
foreach (ch in word)
if (ch == 'x') { yield return true; } else { yield return false; }


Однако тут нет вложенных foreach, это делает монада.

Контракт может быть полезным, может не быть.

Однако если у нас нет возможности его наложить, сложно обсуждать его полезность.
В Agda можно статически гарантировать, что функция, принимающая число, вернёт список с длиной, равной этому числу.
В Haskell и C++ этого гарантировать нельзя, и в этом смысле Agda более мощный язык.
Тот факт, что в язык вводят nothrow и pure, говорит о полезности таких контрактов.
А как в D дела с STM (Software Transactional Memory)? В Haskell это органично вписалось без каких-либо проблем на существующую систему типов. Как это сделать в D так же органично?

У Haskell выбраны другие умолчания: чистый язык и навешиваемые расширения (STM, IO, State, Cont, list-monad...). На мой взгляд этот путь себя оправдывает.

А то чувство, когда горы кода после первой же удачной компиляции сразу работают как надо, незабываемо, и про него говорит почти каждый новоявленный Haskell'ист :)
0 VoidEx,24 февраля 2012 в 20:23#↵↑
Haskell, конечно, далёк от идеала. Например, там есть две функции:
map :: (a -> b) -> [a] -> [b]
mapM :: Monad m => (a -> m b) -> [a] -> m [b]

Хотя вообще говоря достаточно лишь одной реализации (первой), а во вторую она превращается автоматически переносом на стрелки Клейсли.
Т.е. map для списка может быть генерализован до:
map :: (Arrow a, ArrowChoice a) => a b c -> a [b] [c]

с одной единственной имплементацией, подходящей как для функций с эффектами, так и для чистых.

Т.е. любая лямбда-функция типа \f x -> f x + 2 * f x может иметь сразу обобщённый тип. Но в Haskell этого нет, и пока вроде нигде.
0 VoidEx,23 февраля 2012 в 07:08#↵↑
По поводу ограничений, в Haskell даже Data.Dynamic есть, какие ограничения? Хоть динамику используйте.
Правда практика показывает, что нужно это ну очень редко. Но если очень нужно — то есть.
Просто переходя с языков, где всё цветёт мутабельностью, по первости непривычно, да, но это не потому, что Haskell плохой, а потому, что инструмент надо сначала изучить, а не кидаться им сразу гвозди забивать, а потом удивляться, что в руке-то не молоток.
–1 AxisPod,22 февраля 2012 в 10:27#
Number* c = a + b;

Не смог такое представить, такое скомпилится правильно в одном случае, если a указатель, а b число, иначе как минимум будет варнинг. А представить что оба указатели и надо сложить значения по их адресам я вообще не могу, да и вменяемый сяшник не сможет. Да и оперировать указателями в таком виде, чтобы реализовать свой оператор сложения именно для указателя, да это бред какой-то. Лечиться от этого надо.
0 Vseslav,22 февраля 2012 в 10:44#↵↑
Вы уж извините, решил не приводить код на пол-страницы. a и b должны быть умными указателями, для которых перегрузка операторов разрешена.
0 Vseslav,24 февраля 2012 в 13:47#↵↑
NickLion в комментариях написал возможную реализацию, можете посмотреть здесь.
0 nameless_one,22 февраля 2012 в 12:30#
Вот меня давно волнует следующий вопрос: если язык так хорош, почему он до сих пор не попал в мейнстрим, в особенности учитывая то, что он вроде как для мэйнстрима и планировался? Как бы с одной стороны понятно, что не помешала бы поддержка кого-нибудь крупного, но, с другой стороны — а почему ее до сих пор нет? И еще вот интересно: может кто из хабрасообщества участвовал в разработке реального проекта на D? Если так — реквестирую хотя бы краткую выжимку такого опыта!

P.S. Вот сейчас специально глянул и не нашел ни одной вакансии «разработчик D» в Москве — поправьте меня, если это не так
0 Volfram,22 февраля 2012 в 16:57#↵↑
А как связан мэйнстрим и качество языка?
0 nameless_one,22 февраля 2012 в 17:37#↵↑
В общем случае никак, разумеется =) Я вот о чем: имеем язык N, язык этот не эзотерический и не предназначенный для решения узкого круга специфических задач. Авторами языка позиционируется как альтернатива плюсам. Многие, в.т.ч. автор статьи, утверждают, что он удобнее плюсов, и приводят ряд аргументов, часть из которых для меня звучит разумно. Отсюда у меня и возникает резонный вопрос: почему язык не популярен? Вопрос, кстати, ни разу не риторический, мне на самом деле это интересно =)
+2 Volfram,22 февраля 2012 в 18:20#↵↑
Язык умеренно популярен для личных проектов — на уровне всех остальных не-эзотерических-но-и-не-устоявшихся языков. Что касается индустрии, то там качество языка играет чуть ли не последнюю роль. Важна доступность кадров, надёжность toolchain, полнота библиотек, успешный практический опыт. При этом одна из целевых ниш языка — «замена С++» подразумевает большие, сложные, долгоиграющие проекты, а не что-то формата стартапов, где риск уместен.

И, лично моё мнение, сейчас сложилась патовая ситуация для языка, из-за того, что текущая команда разработчиков не может выдать количественно нужный темп улучшений, не говоря уж о поддержке для корпоративного пользователя. При этом автор языка, Walter Bright всё ещё не хочет отдавать reference toolchain в полный open-source ( полностью открыт только front-end, исходники back-end доступны но проприетарны ), что, несомненно, отпугивает некоторых больших игроков, которые могли бы поддержать проект. Остальным мешает синдром NIH.

Так же нет никакого централизованного пиара, распространение информации о языке, по большей части — community effort. Если бы не сочный язык статей Александреску и его репутация в сообществе С++, возможно, про D слышали бы ещё меньше :)

При этом те, кто может позволить себе выбирать язык для нового проекта и готов к некотором количеству работы с напильником — используют вполне успешно. Например, мне известно, что некий Adam D. Ruppe использует D2 для вполне коммерческой веб-разработки ( во всяком случае так он утверждал сам :) ), и в очень интересном стиле ( github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff ). Если следить за сообщениям на офф. форуме, то такие маленькие «истории успеха» вполне проскакивает.

Но в мэйнстриме играют по другим правилам и я не знаю, в каком направлении надо двигаться D, чтобы преодолеть этот барьер.
0 nameless_one,22 февраля 2012 в 19:12#↵↑
Доступность кадров, надёжность toolchain, полнота библиотек, успешный практический опыт — это проблемы, которые возникают перед любым новым языком программирования. Причем, редко когда язык занимает абсолютно новую нишу, но удалось же в свое время PHP потеснить Perl, а потом ASP.NET и Ruby / RoR, в свою очередь, потеснить PHP.

Вот объяснение, предложенное вами во втором абзаце, на мой взгляд выглядит достаточно разумно и многое объясняет =)

В любом случае, спасибо за развернутый ответ =)
0 nameless_one,22 февраля 2012 в 17:49#↵↑
Кстати, глянул ваш профиль, увидел статьи про D. Почитал. Т.к. судя по-всему с языком вы знакомы достаточно плотно, я конкретизирую свой вопрос: можете рассказать о проектах, написанных на D?
0 Volfram,22 февраля 2012 в 18:21#↵↑
Какого рода проекты вас больше интересуют?
0 nameless_one,22 февраля 2012 в 18:51#↵↑
Хм, конечно, наиболее интересен был бы любой опыт коммерческой разработки на D, ежели таковой имеется =) Если вы про тип приложения — мне лично интересно, как язык проявляет себя в разработке GUI-приложений практически любого рода.
0 Volfram,23 февраля 2012 в 00:22#↵↑
Тогда, увы, не могу помочь. Я слежу, в основном, за развитием библиотек и всего, что касается серверных/сетевых технологий, в силу своего рода занятий. Касательно коммерческого — Adam'а я уже упоминал; несколько человек упоминали, что долгое время используют D для научных вычислений.

Сам я несколько раз использовал D для написания плагинов, которые в нормальных условиях предполагалось писать на С/С++, во вполне коммерческих проектах. Кодом поделиться не могу, ибо NDA. Могу победить лень и на основе оного написать статью, это максимум.

Что касается GUI — так оно в большей степени определяется GUI-библиотекой, нежели языком. Тот же GTK он и в D GTK.
+3 shai_xylyd,22 февраля 2012 в 12:52#
От статьи веет пафосом, что еще больше раздражает при наличии фактических ошибок.

Так, например, поступила Microsoft при разработке платформы ASP.NET – скрипты для браузера могут быть написаны на любом языке платформы .NET (например, на C#) и затем автоматически преобразованы в соответствующий код на JavaScript

Ms так не поступала так, как такой функциональности в asp.net нет.

Первое, о чем следует помнить – разработчики Mono будут всегда на шаг позади Microsoft, так как они начинают реализовывать уже выпущенные библиотеки и стандарты.

Не стоит проводить равенство между mono и .net, они пересекающиеся технологии, но не равные. где-то mono задерживается от ms, где-то заметно впереди (managed компилятор от ms выпущен только в прошлом году, а от mono точно был в 2009), а где то вобще не имеет аналогов — monotouch и monodroid. Кстати, на D можно писать приложения под android и iphone, существуют биндинги к gui?

В настоящее время все патенты на C#/CLI принадлежат Microsoft..

Вообще то на c# стандарт есть (Ecma (ECMA-334) и ISO (ISO/IEC 23270:2006)) в отличии от, например, Java. А D стандартизован?

P.S. Я не понимаю, как можно доверять технической статье, которая содержит фактические ошибки, про которые автор пишет «Если честно, не помню, где именно про это читал» это не гуматитарщина, не знаешь — не пиши.
+1 Volfram,22 февраля 2012 в 18:49#↵↑
Насчёт android — как раз недавно появился к этому интерес, вот статья по тому, как можно собрать программу для Andoid, используя NDK + gdc. Но без полного портирования druntime и phobos это совсем не так круто, как хотелось бы, увы.
+1 Seekeer,22 февраля 2012 в 13:29#
>К тому же, по сообщениям в прессе, в 2015-2016 году Microsoft планирует отказаться от бренда «Windows», создав новую ОС для планшетов, смартфонов, компьютеров, консолей, телевизоров и других устройств, а, значит, языки для разработки под эту ОС тоже могут кануть в небытие.
Мелкософт тратит очень много усилий именно для поддержки обратной совместимости. Нет никаких причин для того, чтобы так резко менять курс. Даже если такое изменение и произойдёт, о нём должны заблаговременно проинформировать, ибо слишком много сейчас у них на .Net завязано.
0 Vseslav,23 февраля 2012 в 10:26#↵↑
Вы знаете, очень хочу верить, что именно так и будет. В смысле, что будет обратная совместимость.
Однако хочу заметить, что Microsoft уже отказывалась от обратной совместимости, например:
Есть старое программное обеспечение, которое несовместимо с Windows Vista, а также драйверы и устройства.
0 Seekeer,23 февраля 2012 в 22:25#↵↑
Это всё таки единичные случаи. Если программа написана грамотно, никакие хаки и недокументированная информация в ней не использует, то она будет работать и на следующей версии винды. А так чтобы отказываться от поддержки целой технологии, причём своей же, о таком я не читал.
Ну а по указанной вами ссылке: кто же виноват, что производители устройств не следовали USB спецификациям:)
0 Throwable,22 февраля 2012 в 14:23#
Интересно услышать от автора подобное сравнение с Vala: live.gnome.org/Vala
— переносимость (в рамках gcc)
— компиляция в native (посредством gcc)
— полная интеграция с существующими библиотеками на C (для этого собственно и разработан)
— С# подобный синтаксис и основные концепции
— вместо сборщика мусора и деструкторов выбран усредненный вариант — счетчик ссылок
— компилируется в JavaScript (пока экспериментально)
0 Volfram,22 февраля 2012 в 17:00#↵↑
Если оценивать по приведенному списку, то D разве что в JavaScript не компилируется. Почему бы не предоставить линк на обзор Vala? Тогда можно и сравнить, а ставить рядом язык, на котором написано N строк кода и язык, который только что вычитан в интернетах — не практично.
+2 sdramare,22 февраля 2012 в 15:32#
Основная проблема, которая для меня крайне важна, это компиляция в промежуточный язык Common Intermediate Language (CIL), что делает декомпиляцию тривиальной задачей.

И в чем проблема? Сверхсекретные алгоритмы можно вынести на С и вызвать через PInvoke, а ваш код вью-модели очередного диалога никому не интересен. Это не говоря о том, что используя ту же IDA, провести реверс-инженеринг программы, написанной на С/С++ тоже не представляется чем-то особо трудным. Собственно по-этому и появились запаковщики и прочее.

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

Во-первых вы путает Free Soft и Open Source. Во-вторых если у вас есть действительно задача защитить программу от реверс-инженеринга, то ее придется целенаправленно решать не зависимо от того, на какой платформе вы пишете.

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


Самая простая защита от какого-нибудь рефлектора/dotPeek'a это когда делается небольшая программа на С, ей в ресурсы кладутся все ассембли, программа инициализирует CLR и запускает стартовую на выполнение. После чего сам ваш код работает так же, как и без запаковки. Так что вопрос вполне решаемый, хотя на мой взгляд в 90% такая проблема вообще не стоит.

Не знаю, прав ли автор, но из C# очень легко вызвать функции WinAPI, объекты COM и другие компоненты, которые сделают программу непереносимой.


А из С++ или скажем Python вызвать WinAPI разве трудно? Так называемый «system coupling», который трудно развязать, это ошибка архитектуры приложения, никак не связная с языком.

Да, конечно же, Mono – это широко известный продукт, но при его использовании проблемы всё же могут появиться. Первое, о чем следует помнить – разработчики Mono будут всегда на шаг позади Microsoft, так как они начинают реализовывать уже выпущенные библиотеки и стандарты. Второе: а им случайно не надоест? Да, я прекрасно понимаю, что это – проект со свободной лицензией, который развивает сообщество, но риск прекращения поддержки все равно существует.


А вдруг сообществу в целом и Торвальдсу в частности надоест поддерживать linux-kernel? А вдруг гугл завтра обанкротится? Такие предположения в отношении проекта, которому почти 10 лет, звучат как-то странно. Безусловно, в этом мире ни в чем нельзя быть уверен, но надо же как-то жить.

следует вспомнить – отсутствие стандартов на компоненты WinForms, ADO.NET и ASP.NET со стороны Microsoft. Их использование может повлечь за собой юридические претензии со стороны Microsoft, поэтому не рекомендуется использовать их совместно с Mono."


Ну там все тоже не так плохо на самом деле. Ребята с Моно говорят, что если что, просто постараются обойти патенты «Should patent issues ever arise, the Mono project's stated strategy for dealing with them is as follows:
Work around the patent by using a different implementation technique that retains the API, but changes the mechanism; if that is not possible, they would
Remove the pieces of code that were covered by those patents, and also
Find prior art that would render the patent useless.» Ходя судя по тому, как развивается ситуация, MS довольно либеральны в отношении Mono.

а будет ли Microsoft дальше развивать этот язык? В настоящее время все патенты на C#/CLI принадлежат Microsoft, и отказ от поддержки с её стороны будет означать смерть этому языку. Лично я считаю, что C# будет продолжать развиваться – Microsoft слишком много вложила в этот язык. Но гарантий никто дать не может


Во-первых на C# есть стандарт. Делайте свой компилятор и вперед. Развитие C# не привязано к MS, хотя безусловно именно MS двигает его. Что касается гарантий — где гарантии дальнейшего развития любого другого языка?

В результате это привело к трагедии для тысяч программистов, работающих на Basic’ке.


Во-первых об этом было заявлено заранее. Во-вторых никакой трагедии не было, народ просто мигрировал на .net.

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


Во-первых первый раз слышу, чтобы язык был под ОС. .net приложения на С# вполне себе пишутся под ту же WP7(это к слову о переносимости .net). И думаю в новой ос поддержка .net точно так же никуда не денется.

Лично для меня этих доводов достаточно, чтобы отказаться от использования C#


Вы знаете, я согласен, что есть примеры задач, когда C#.net не уместен — это микроконтроллеры, задачи реального времени, работы в ограниченных размерах памяти, необходимость максимальной оптимизации кода под процессор или, например, необходимость быстрого изменения кода без перезапуска приложения. Но доводы, которые привели вы более чем спорны. Что же касается самой статьи, то она скорее должна была называться «Критика С# и С++». Если говорить о D, то интересно было например посмотреть сравнение при решении одних и тех же задач. Ну пример взять простую задачу составление частотного словаря по нескольким файлам. Решить ее с учетом параллельного вычисления и/или распределительного. Сравнить решения на C#\C++\D.

0 lightman,22 февраля 2012 в 23:17#
Программисты на D, как часто встречаетесь с ситуацией, что нужной либы нет и её необходимо писать самостоятельно?
+2 Volfram,22 февраля 2012 в 23:56#↵↑
Примерно столь же часто, как и программисты на С. Чуть реже.
Вы недооцениваете значение совместимости ABI.
0 lightman,23 февраля 2012 в 00:04#↵↑
Хм, хорошая новость.

А кто щупал QtD, насколько он полноценен (или, может, есть какие-то его аналоги)?
0 Volfram,23 февраля 2012 в 00:07#↵↑
Если верить вики, то не вполне.
Сам ни разу ещё не пробовал писать на D что-либо с GUI, не могу сказать.
0 Vseslav,24 февраля 2012 в 13:09#↵↑
Из аналогов QtD (в смысле, порт библиотеки Qt на D) ничего нет.
Вот здесь можете посмотреть, какие части Qt уже портированы, какие — еще нет. На данный момент не портированны QtSql, QtScript, QtTest и QtUiTools. Хочу заметить, что в целом поддержка библиотеки улучшается — раньше было больше не портированных компонентов (в частности, не было поддержки QtWebKit).
Возможно, текущей функциональности Вам будет достаточно, или нужные Вам классы появятся в ближайшем будущем.
Есть так же порты других GUI библиотек, в том числе:
GtkD — порт GTK+
DWT — порт SWT
wxD — порт wxWidgets
Возможно, Вам подойдёт что-то из этих библиотек.
0 beduin01,28 марта 2013 в 23:51#
Кстати открылся официальный русский сайт языка программирования D dlang.ru
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#22 dyvniy » Пт, 31 октября 2014, 10:54:59

Подключение Qt
http://dlang.ru/4-podklyuchaem-qtgui-dll#cut

Код: Выделить всё


import std
.c.stdio;
import std.c.windows.windows;

// Типы вызываемых функций 
extern (Calias void* function(void*, void*, char**) t_QApplication_QApplication
extern (Calias int   function(void*) t_QApplication_Exec
extern (Calias void* function(void*, void*, void*) t_QWidget_QWidget;
extern (Calias void  function(void*) t_QWidget_show;

int main(string[] args) {
    
HINSTANCE hQtGui
    
// Переменные для хранения адресов конструкторов и методов
    
t_QApplication_QApplication pQApplication_QApplication;
    
t_QWidget_QWidget pQWidget_QWidget;
    
t_QWidget_show pQWidget_show;
    
t_QApplication_Exec pQApplication_exec;

    
hQtGui LoadLibraryA("QtGui4.dll");
    if (
hQtGui) {
        
// Забираем адреса конструкторов и методов
            
pQApplication_QApplication cast(t_QApplication_QApplication)GetProcAddress(hQtGui"_ZN12QApplicationC1ERiPPc");
            
pQWidget_QWidget cast(t_QWidget_QWidget)GetProcAddress(hQtGui"_ZN7QWidgetC1EPS_6QFlagsIN2Qt10WindowTypeEE");
            
pQWidget_show cast(t_QWidget_show)GetProcAddress(hQtGui"_ZN7QWidget10setVisibleEb");
            
pQApplication_exec cast(t_QApplication_Exec)GetProcAddress(hQtGui"_ZN12QApplication4execEv");
            
char bufApp[8];    // Данные экземпляра QApplication
        
char bufW1[20];    // Данные экземпляра QWidget

        // Готовим argc и argv
            
int aargs args.length;      int fl 0;
        
        
// bufApp = QApplication(argc, argv);
            
(*pQApplication_QApplication)(bufAppcast(void*)&aargscast(char**)args);
        
// bufW1 = QWidget(null, null);
            
(*pQWidget_QWidget)(bufW1null, &fl);
        
// bufw1.show();
            
(*pQWidget_show)(bufW1);
            
// bufApp.exec();
            
(*pQApplication_exec)(bufApp);
    }
    else {
            
printf("BAD LoadLibrary QtGui.dll");
    }
    return 
0;
}
 
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#23 dyvniy » Сб, 1 ноября 2014, 20:30:57

Плагины для студии
http://habrahabr.ru/post/99670/
productively power tools
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#24 dyvniy » Вт, 18 ноября 2014, 17:56:33

Многопоточность в стиле Эрланга
http://dml.compkaluga.ru/forum/index.php?showtopic=51622
http://habrahabr.ru/post/138248/
Спойлер
Мне очень хочется показать, что в многопоточности С++ «очень быстро» не исключает «очень безопасно». А значит можно писать эффективные и стабильные программы со сколь угодно большим количеством потоков и при этом избегать траты кучи времени на отладку многопоточности. Если Вам интересно, как мне удаётся не выстрелить себе в ногу, и чем я за это плачу, добро пожаловать

Когда лет 7-8 назад пришлось писать всё более многопоточные программы, мой друг — Капитан Очевидность — обратил моё внимание на следующий факт: чем больше потоков, чем активнее они взаимодействуют, тем больше требуется объектов синхронизации и тем больше бессонных ночей я трачу на этапе тестирования. Ситуация усложняется тем, что многопоточные ошибки подобны красивым барышням: они всегда случайно появляются в поле зрения, но встретить какую-то конкретно ещё раз — гораздо труднее.

В общем, когда число потоков в программах стабильно ушло за 5-6, я понял, что с этим нужно что-то делать, и делать очень быстро, пока их не стало 10 и больше.
При этом, если в том же Win API есть печеньки вроде WaitForMultipleObjects, то при переходе в кросс-платформенную среду, у нас остаются только мьютексы, критические секции и небезусловная поддержка сигналов (а так и случилось, когда я перешёл на замечательный кроссплатформенный фреймворк U++).

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

Если своими словами и вкратце, речь идёт о том, что каждая подзадача работает со своим «адресным пространством» (вернее, набором данных), то есть изолирована от других. И единственный способ взаимодействия подзадач — обмен асинхронными сообщениями. Это всё имеет под собой серьёзную теорию, массу умных названий и особенностей, но мой друг Капитан Очевидность очень не любит все эти скучные детали и просит меня перейти сразу к сути.

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

Давайте на секунду забудем, что входящие сообщения с аргументами передаются нам из многопоточной среды. Внутри конвейера мы имеем простейший однопоточный цикл обработки входящих потокобезопасных аргументов с использованием внутренних данных класса. Понятно, что для такой работы объекты синхронизации не нужны. Это обычный код, который и знать ничего не знает про многопоточность. Напоминаю, что наш класс — мизантроп, не поделился ни с кем своими данными, а значит на них не может повлиять ни один объект из другого потока (что, кстати, красиво ложится в требования инкапсуляции).

Теперь остаётся решить, как и в каком виде мы будем передавать эти самые асинхронные сообщения. На этом месте мой друг К.О. упорно замолчал, так что решение мне пришлось принимать самому. Довольно быстро стало понятно, что любое перечисление видов сообщений для каждого такого класса
enum {MESSAGE_....., MESSAGE_....., MESSAGE_.....}
будет выглядеть жестоко, а их обработка
switch (messageType)
{
case MESSAGE_.....: ....... break;
case MESSAGE_.....: ....... break;
case MESSAGE_.....: ....... break;
case MESSAGE_.....: ....... break;
}
будет кастовать законные лучи ненависти и поноса от благодарных потомков.

В общем, класть в очередь на обработку я стал сразу колбэки с аргументами. Так я избавился от ненужных перечислений и сделал код хорошо читаемым. Выглядит это следующим образом:
//объект, который превращён в отдельный конвейерный поток
class SomeJobThread : public CallbackThread
{
public: //thread SomeJobThread
void DoSomeJob(String arg1, double arg2)
{
//обработка аргументов с учётом моих данных
//в простейшем однопоточном стиле
}

private:
//мои данные
};

SomeJobThread someJob;
someJob.Request(&SomeJobThread::DoSomeJob, "OMG!", -1);


Такой код выглядит слегка необычно, но лишь поначалу. При этом он довольно выразителен, не перегружен объектами синхронизации, циклами и прочим.
По сути, я объединяю данные потока в единый класс, путём наследования делаю поток конвейерным и пишу в нём обработчики, состоящие из простого однопоточного кода. И всё!

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

Здесь мой друг К.О., ласково прищурившись, замечает, что за потокобезопасность я плачу:
наличием цикла обработки сообщений
синхронизацией потоков с очередью
необходимостью копировать аргументы
памятью, которую съедает очередь потока


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

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

По поводу копирования аргументов. Копирование pod-типов — довольно быстрая операция, которая в общем-то незаметна в абсолютном большинстве применений. Когда речь идёт о более сложных аргументах, то имеет смысл воспользоваться разрушающим копированием. Например, строку можно передавать через разрушающую передачу указателя на внутренние данные (этот механизм активно используется в U++). Поэтому передача в виде аргумента списка или ассоциативного массива — очень дешёвая операция. По сути, передача сколь угодно сложных параметров в реальных программах сводится, как максимум, к копированию нескольких POD-переменных.

Ну и наконец, использование памяти. Каждый элемент очереди — небольшая структура, содержащая, кроме аргументов, указатель и виртуальную таблицу из двух указателей (всего == (1+1+2)*sizeof(void *) ), что совсем немного.

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

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

Что тут ещё скажешь… очереди — это круто, не бойтесь очередей!


P.S. В статье я лишь описал принцип, обозначил подход. Есть много моментов, связанных с проектированием проектов с применением конвейерных потоков, обратной нотификацией, созданием пула конвейерных потоков для высоконагруженных приложений. Если статья будет интересна сообществу, эти моменты я раскрою подробнее.
Исходники http://upp-mirror.googlecode.com/svn/trunk/bazaar/MtAlt/
Пример http://upp-mirror.googlecode.com/svn/trunk/bazaar/MtAltExample1/
Суть метода — в другой модели взаимодействия потоков (чётко оговорены ограничения, которые я накладываю на код С++). Я утверждаю, что применение данной модели позволяет создавать более стабильные и масштабируемые приложения без потери эффективности (практически или полностью) в сравнении с обычными объектами синхронизации. Как заметили здесь в комментариях, данный подход становится всё более актуальным с переходом на многоядерные процессоры.

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


Название раздела: Программирование (под Desktop и Android)
Описание: Разработка и отладка приложений. Упор на 3D-графику.

Быстрый ответ


Введите код в точности так, как вы его видите. Регистр символов не имеет значения.
Код подтверждения
:) ;) :hihi: :P :hah: :haha: :angel: :( :st: :_( :cool: 8-| :beee: :ham: :rrr: :grr: :* :secret: :stupid: :music: Ещё смайлики…
   

Вернуться в «Программирование (под Desktop и Android)»

Кто сейчас на форуме (по активности за 15 минут)

Сейчас этот раздел просматривают: 122 гостя