<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Объектно-ориентированное программирование на языке C++</title>
		<link>http://www.cplus-plus.ru/</link>
		<description></description>
		<lastBuildDate>Tue, 08 Feb 2011 05:33:47 GMT</lastBuildDate>
		<generator>uCoz Web-Service</generator>
		<atom:link href="https://cpplus.my1.ru/news/rss" rel="self" type="application/rss+xml" />
		
		<item>
			<title>Внимание!!!</title>
			<description>&lt;p&gt;&lt;span style=&quot;background-color: #ffdab9;&quot;&gt;Сайт переезжает на новый движок, и другой сервер! В связи с этим активность сайта будет прекращена до 1 марта, так же будет остановлена рассылка статей ! После этого в меню появится пункт &quot;вакансии&quot; , мы производим набор журналистов и публикаторов, если Вы хотите участвовать в жизни портала и хотите заработать, зарегистрируйтесь и свяжитесь с Администрацией!&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffdab9;&quot;&gt;Общие вопросы можно задавать в этой теме !&lt;/span&gt;&lt;/p&gt;</description>
			<content:encoded>&lt;p&gt;&lt;span style=&quot;background-color: #ffdab9;&quot;&gt;Сайт переезжает на новый движок, и другой сервер! В связи с этим активность сайта будет прекращена до 1 марта, так же будет остановлена рассылка статей ! После этого в меню появится пункт &quot;вакансии&quot; , мы производим набор журналистов и публикаторов, если Вы хотите участвовать в жизни портала и хотите заработать, зарегистрируйтесь и свяжитесь с Администрацией!&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffdab9;&quot;&gt;Общие вопросы можно задавать в этой теме !&lt;/span&gt;&lt;/p&gt;</content:encoded>
			<link>https://cpplus.my1.ru/news/vnimanie/2011-02-08-80</link>
			<category>Новости</category>
			<dc:creator>FazaNaka</dc:creator>
			<guid>https://cpplus.my1.ru/news/vnimanie/2011-02-08-80</guid>
			<pubDate>Tue, 08 Feb 2011 05:33:47 GMT</pubDate>
		</item>
		<item>
			<title>Утечки памяти в программах на С/С++ — история нескольких багов</title>
			<description>Истории нескольких проблем, связанных с утечками памяти. Большинство 
таких проблем являются достаточно тривиальными, легко воспроизводятся, 
легко обнаруживаются соответствуюшим инструментарием, и исправляются. 
Но, временами, проблемы оказываются необычными, и требуют необычного 
подхода или решения…...</description>
			<content:encoded>Истории нескольких проблем, связанных с утечками памяти. Большинство 
таких проблем являются достаточно тривиальными, легко воспроизводятся, 
легко обнаруживаются соответствуюшим инструментарием, и исправляются. 
Но, временами, проблемы оказываются необычными, и требуют необычного 
подхода или решения…$CUT$&lt;br&gt;
&lt;h5&gt;Кодопотерятор памяти&lt;/h5&gt;
Очередное тестирование в QA выявило небольшую постоянную утечку памяти 
под нагрузкой в новой версии продукта. Воспроизведение не представляло 
проблемы, но первоначальный прогон программы под Valgrind не выявил 
утечек. &lt;br&gt;
&lt;br&gt;
После некоторых раздумий была включена опция --leak-check=full и 
Valgrind начал отчитываться об утечках. Но среди ожидаемых утечек 
разного рода статических переменных, зачастую осознанно не 
освобождаемых, не находилось ничего, напоминающего утечку. Обычно при 
прогоне нескольких тысяч итераций несложно выделить аналогичное 
количество потерянных блоков памяти выделенных malloc-ом. В данном-же 
случае утечка на 10,000 запросов к серверу была минимальна.&lt;br&gt;
&lt;br&gt;
После анализа стеков выделений памяти и отсева ожидаемых случаев, 
остался только один кандидат, который отвечал за несколько десятков 
потерянных блоков — количество, абсолютно не ассоциирующееся с 10,000 
запросами. Но этому нашлось объяснение — выделения памяти происходили в 
классе строки STL, который активно использует memory pool для уменьшения
 количества выделений памяти. Поэтому вместо 10,000 потерянных блоков 
памяти Valgrind отчитывался о 40+. Стек вызовов выглядел примерно так:&lt;br&gt;
&lt;br&gt;
&lt;code&gt;==15882== 76,400 bytes in 8 blocks are definitely lost in loss record 2 of 3&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; at 0x401B007: operator new(unsigned int) (vg_replace_malloc.c:214)&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; by 0x40A40F0: std::__default_alloc_template&lt;true, 
0&amp;gt;::_S_chunk_alloc(unsigned int, int&amp;amp;) (in 
/usr/lib/libstdc++.so.5.0.3)&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; by 0x40A3FFC: std::__default_alloc_template&lt;true, 
0&amp;gt;::_S_refill(unsigned int) (in /usr/lib/libstdc++.so.5.0.3)&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; by 0x40A3B6B: std::__default_alloc_template&lt;true, 
0&amp;gt;::allocate(unsigned int) (in /usr/lib/libstdc++.so.5.0.3)&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; by 0x40A9B67: std::string::_Rep::_S_create(unsigned int, 
std::allocator&lt;char&amp;gt; const&amp;amp;) (in /usr/lib/libstdc++.so.5.0.3)&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; by 0x40A9C98: 
std::string::_Rep::_M_clone(std::allocator&lt;char&amp;gt; const&amp;amp;, 
unsigned int) (in /usr/lib/libstdc++.so.5.0.3)&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; by 0x40A7A05: std::string::reserve(unsigned int) (in /usr/lib/libstdc++.so.5.0.3)&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; by 0x8049826: std::basic_string&lt;char, 
std::char_traits&lt;char&amp;gt;, std::allocator&lt;char&amp;gt; &amp;gt; 
std::operator+&lt;char,&lt;br&gt;
std::char_traits&lt;char&amp;gt;, std::allocator&lt;char&amp;gt; &amp;gt;(char 
const*, std::basic_string&lt;char, std::char_traits&lt;char&amp;gt;,&lt;br&gt;
std::allocator&lt;char&amp;gt; &amp;gt; const&amp;amp;) (basic_string.tcc:619)&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; by 0x804956A: A::A(A const&amp;amp;) (class_a.cpp:20)&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; by 0x80491BC: foo(int) (test.cpp:23)&lt;br&gt;
==15882== &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; by 0x80492EA: main (test.cpp:32)&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
Вроде-бы источник утечки памяти был найден, но код выглядел абсолютно 
невинно — вызов функции с передачей ей временной копии объекта:&lt;br&gt;
&lt;br&gt;
&lt;code&gt;doSomething( condition ? Object( params ) : getObject() );&lt;/code&gt;&lt;br&gt;
&lt;br&gt;&amp;nbsp;
Мы были уже точно уверены, что память теряется в этой строке, и начали 
смотреть на код, генерируемый компилятором для данной строки. Вроде-бы 
все было на месте — вызов &quot;basic_string::length()”, вызов конструктора 
класса параметра для одной ветви условия, вызов &quot;Parent::getB()” и 
конструктора копирования для другой ветви, вызов собственно функции 
&quot;A::create”, освобождения временных объектов — всё, кроме вызова 
деструктора временной копии класса — созданной на стеке, но содержащей 
внутри копию строки, которая и не освобождалась в результате!&lt;br&gt;
&lt;pre&gt;110: return A::create(b1, b2, s.length() &amp;gt; 0 ? B(s) : getB());
 13d4: 83 ec 0c sub $0xc,%esp
 13d7: 8d 45 d8 lea 0xffffffd8(%ebp),%eax
 13da: 50 push %eax
 13db: e8 fc ff ff ff call std::basic_string&lt;wchar_t&amp;gt;::length() const &lt;&lt;=========
 13e0: 83 c4 10 add $0x10,%esp
 13e3: 85 c0 test %eax,%eax
 13e5: 74 18 je 13ff &lt;A::create+0x341&amp;gt;
 13e7: 83 ec 08 sub $0x8,%esp
 13ea: 8d 45 d8 lea 0xffffffd8(%ebp),%eax
 13ed: 50 push %eax
 13ee: 8d 85 f8 fe ff ff lea 0xfffffef8(%ebp),%eax
 13f4: 50 push %eax
 13f5: e8 fc ff ff ff call B::B( std::basic_string&lt;wchar_t&amp;gt; const &amp;amp; ) &lt;&lt;=========
 13fa: 83 c4 10 add $0x10,%esp
 13fd: eb 21 jmp 1420 &lt;A::create+0x362&amp;gt;
 13ff: 83 ec 08 sub $0x8,%esp
 1402: 83 ec 04 sub $0x4,%esp
 1405: ff 75 0c pushl 0xc(%ebp)
 1408: e8 fc ff ff ff call Parent::getB() &lt;&lt;=========
 140d: 83 c4 08 add $0x8,%esp
 1410: 50 push %eax
 1411: 8d 85 f8 fe ff ff lea 0xfffffef8(%ebp),%eax
 1417: 50 push %eax
 1418: e8 fc ff ff ff call B::B( B const &amp;amp; ) &lt;&lt;=========
 141d: 83 c4 10 add $0x10,%esp
 1420: 83 ec 0c sub $0xc,%esp
 1423: 8d 85 f8 fe ff ff lea 0xfffffef8(%ebp),%eax
 1429: 50 push %eax
 142a: 0f b6 45 f6 movzbl 0xfffffff6(%ebp),%eax
 142e: 50 push %eax
 142f: 0f b6 45 f7 movzbl 0xfffffff7(%ebp),%eax
 1433: 50 push %eax
 1434: ff 75 0c pushl 0xc(%ebp)
 1437: ff 75 08 pushl 0x8(%ebp)
 143a: e8 fc ff ff ff call A::create(bool, bool, B) &lt;&lt;=========
 143f: 83 c4 1c add $0x1c,%esp
 1442: 83 ec 0c sub $0xc,%esp
 1445: 8d 85 68 ff ff ff lea 0xffffff68(%ebp),%eax
 144b: 50 push %eax
 144c: e8 fc ff ff ff call BS&lt;100, char&amp;gt;::~BS()
 1451: 83 c4 10 add $0x10,%esp
 1454: 83 ec 0c sub $0xc,%esp
 1457: 8d 45 d8 lea 0xffffffd8(%ebp),%eax
 145a: 50 push %eax
 145b: e8 fc ff ff ff call std::basic_string&lt;wchar_t&amp;gt;::~basic_string()
 1460: 83 c4 10 add $0x10,%esp
 1463: eb 55 jmp 14ba &lt;A::create+0x3fc&amp;gt;
 1465: 89 85 f0 fe ff ff mov %eax,0xfffffef0(%ebp)
 146b: 8b b5 f0 fe ff ff mov 0xfffffef0(%ebp),%esi
 1471: 83 ec 0c sub $0xc,%esp
 1474: 8d 85 68 ff ff ff lea 0xffffff68(%ebp),%eax
 147a: 50 push %eax
 147b: e8 fc ff ff ff call BS&lt;100, char&amp;gt;::~BS()
 1480: 83 c4 10 add $0x10,%esp
 1483: 89 b5 f0 fe ff ff mov %esi,0xfffffef0(%ebp)
 1489: eb 06 jmp 1491 &lt;A::create+0x3d3&amp;gt;
 148b: 89 85 f0 fe ff ff mov %eax,0xfffffef0(%ebp)
 1491: 8b b5 f0 fe ff ff mov 0xfffffef0(%ebp),%esi
 1497: 83 ec 0c sub $0xc,%esp
 149a: 8d 45 d8 lea 0xffffffd8(%ebp),%eax
 149d: 50 push %eax
 149e: e8 fc ff ff ff call std::basic_string&lt;wchar_t&amp;gt;::~basic_string()
 14a3: 83 c4 10 add $0x10,%esp
 14a6: 89 b5 f0 fe ff ff mov %esi,0xfffffef0(%ebp)
 14ac: 83 ec 0c sub $0xc,%esp
 14af: ff b5 f0 fe ff ff pushl 0xfffffef0(%ebp)
 14b5: e8 fc ff ff ff call _Unwind_Resume&lt;/pre&gt;Так и не найдя вызов деструктора к коде, начали искать похожие проблемы, и практически сразу нашли &quot;&lt;a href=&quot;http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9946&quot;&gt;gcc 3.2 bug 9946 — object destructor not called, potentially causing a memory leak&lt;/a&gt;&quot;.&lt;br&gt;
&lt;br&gt;
Проблема была в генерации кода для оператора &quot;?:”, и решалась либо 
обновлением компилятора, либо косметическим изменением оператора &quot;?:” на
 простой if().&lt;br&gt;
&lt;br&gt;
&lt;code&gt;// Есть функция, возвращающая константную ссылку на объект&lt;br&gt;
const Object &amp;amp; getObject();&lt;br&gt;
&lt;br&gt;
// Другая функция принимает объект по значению&lt;br&gt;
void doSomething( Object obj );&lt;br&gt;
&lt;br&gt;
// При вызове подобном нижеследующему, может произойти утечка памяти.&lt;br&gt;
// Автоматически созданный на стеке экземпляр объекта может быть не освобожден,&lt;br&gt;
// приводя к утечке памяти если экземпляр содержал динамически выделенную память.&lt;br&gt;
doSomething( condition ? Object( params ) : getObject() );&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
Простая тестовая программа выводила следующее (обратите внимание на количество созданных и освобожденных экземпляров класса А):&lt;br&gt;
&lt;br&gt;
&lt;code&gt;main() start&lt;br&gt;
A::A( &apos;on stack&apos; )&lt;br&gt;
B::B()&lt;br&gt;
A::A( &apos;static instance&apos; )&lt;br&gt;
A::A( &apos;copy of static instance&apos; )&lt;br&gt;
B::boo()&lt;br&gt;
B::~B()&lt;br&gt;
A::~A( &apos;on stack&apos; )&lt;br&gt;
main() end&lt;br&gt;
A::~A( &apos;static instance&apos; )&lt;br&gt;
Class A created 3 times and destroyed 2 times&lt;br&gt;
Class B created 1 times and destroyed 1 times&lt;/code&gt;&lt;br&gt;
&lt;br&gt;Проблема происходила только при генерации 32-битного кода в gcc 3.2.3, и
 не происходила в 64 битном коде, или коде сгенерированном более 
поздними версиями компилятора.&lt;br&gt;
&lt;h5&gt;Я — не я, и память не моя&lt;/h5&gt;
Одно время я поддерживал и дорабатывал программу для сбора данных и 
передачи их на сервер. Эта программа работала на добром десятке 
платформ, в том числе на Linux. Так как программа была коммерческая, то 
компилировалась она компилятором, соответствующим минимальной 
поддерживаемой версии Linux, в нашем случае gcc 3.3.x, и предоставлялись
 исполняемые файлы.&lt;br&gt;
&lt;br&gt;
В один прекрасный момент, в нашем отделе QA зарегистрировали и даже 
сумели воспроизвести (во время длительного, несколько дней, теста под 
большой нагрузкой) падение программы по причине нехватки памяти — 
процесс съедал 3GB памяти и благополучно падал, создавая core dump 
аналогичного объема. Причем взрывной рост использования памяти 
происходил буквально за 10-15 минут до печального конца, и в это время 
нагрузка процессора была порядка 12% (на сервере было 4 двух-ядерных 
процессора, так что один поток, крутящийся в цикле, как раз и должен 
использовать 12.5%).&lt;br&gt;
&lt;br&gt;Стек вызовов упавшего потока указывал на совершенно тривиальный код в 
конструкторе копирования, было только понятно, что это как-то связано с 
исключительными ситуациями.&lt;br&gt;
&lt;pre&gt;(gdb) where
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xb7dbd8d0 in raise () from /lib/libc.so.6
#2 0xb7dbeff3 in abort () from /lib/libc.so.6
#3 0xb7f86da5 in dlopen () from /usr/lib/libstdc++.so.5
#4 0xb7f86de2 in std::terminate () from /usr/lib/libstdc++.so.5
#5 0xb7f85e89 in __cxa_allocate_exception () from /usr/lib/libstdc++.so.5
#6 0xb78f7f07 in Uuid::Uuid () from .../lib32/libourlibrary.so
#7 0xb782409d in ...&lt;/pre&gt;Попытки воспроизвести проблему с отладчиком памяти или отладочной 
версией исполняемых файлов не увенчались успехом — проблема исчезала. Да
 и само воспроизведение обходилось дорого — обычно требовалось 
тестировать процесс под нагрузкой в течение 2-4 суток, прежде чем 
возникала проблема воспроизводилась — а это задерживало тесты других 
компонент, требовавших эту-же инфраструктуру для тестов под нагрузкой.&lt;br&gt;
&lt;br&gt;
Поиск похожих стеков вызовов не дал практически никаких результатов, и 
ситуация была тупиковая. Надо было как-то обнаружить причину утечки 
памяти без использования отладчика.&lt;br&gt;
&lt;br&gt;
В одной реализации библиотеки выделения памяти для С++, с которой я 
сталкивался, каждый блок, выделяемый под новый экземпляр класса, в 
отладочной версии библиотеки помечался строкой, содержащей имя этого 
класса. Такой способ позволял легко определит по core dump-y, какие 
объекты того или иного типа были выделены. Я попробовал поискать строки,
 содержащиеся в core dump файле, запустив сначала программу «strings», а
 затем отсортировав с «sort».&lt;br&gt;
&lt;br&gt;
Любопытно, но обнаружилось, что файл содержит 32,123,751 строк вида 
«++CCUNG0o» — только эти строки занимали около 275 MB в файле размером 3
 GB. При поиске этих строк в файле выяснилось, что каждая такая 
сигнатура начинала блок размером 96 байт (96b * 32,000,000 = 3Gb!!!)&lt;br&gt;
&lt;br&gt;
Блоки начинались инвертированной строкой «++CCUNG0o» с нулем в начале 
(так как она инвертированная), и отличались только парой из четырех байт
 в разных местах, образуя, по-видимому, связанный список.&lt;br&gt;
&lt;br&gt;
&lt;code&gt;0x248b818: 0x00 0x2b 0x2b 0x43 0x43 0x55 0x4e 0x47 &lt;&lt;== строка «++CCUNG0o»&lt;br&gt;
0x248b820: 0x30 0x6f 0xf8 0xb7 0x00 0x00 0x00 0x00&lt;br&gt;
0x248b828: 0x6c 0xa8 0x78 0xb7 0x00 0x00 0x00 0x00&lt;br&gt;
0x248b830: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00&lt;br&gt;
0x248b838: 0xa8 0x0e 0xd3 0xb7 0x3c 0xa2 0xfa 0xb7&lt;br&gt;
0x248b840: 0xe8 0xae 0x82 0xb7 0x65 0x00 0x00 0x00&lt;br&gt;
0x248b848: 0xc0 0x0e 0xd3 0xb7 0x38 0x9c 0xc8 0xb7&lt;br&gt;
0x248b850: 0xf4 0x9b 0x04 0x08 0xe4 0x9c 0x04 0x08&lt;br&gt;
0x248b858: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00&lt;br&gt;
0x248b860: 0x03 0x00 0x00 0x00 0xbe 0x80 0x79 0xb7&lt;br&gt;
0x248b868: 0x58 0x80 0x79 0xb7 0x54 0xa9 0x78 0xb7&lt;br&gt;
0x248b870: 0x98 0xb8 0x48 0x02 0x00 0x00 0x00 0x00&lt;br&gt;
&lt;br&gt;
0x248b878: 0x00 0x2b 0x2b 0x43 0x43 0x55 0x4e 0x47 &lt;&lt;== строка «++CCUNG0o»&lt;br&gt;
0x248b880: 0x30 0x6f 0xf8 0xb7 0x00 0x00 0x00 0x00&lt;br&gt;
0x248b888: 0x6c 0xa8 0x78 0xb7 0x00 0x00 0x00 0x00&lt;br&gt;
0x248b890: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00&lt;br&gt;
0x248b898: 0xa8 0x0e 0xd3 0xb7 0x3c 0xa2 0xfa 0xb7&lt;br&gt;
0x248b8a0: 0xff 0xff 0xff 0xff 0x65 0x00 0x00 0x00&lt;br&gt;
0x248b8a8: 0xc0 0x0e 0xd3 0xb7 0x38 0x9c 0xc8 0xb7&lt;br&gt;
0x248b8b0: 0xf4 0x9b 0x04 0x08 0xe4 0x9c 0x04 0x08&lt;br&gt;
0x248b8b8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00&lt;br&gt;
0x248b8c0: 0x03 0x00 0x00 0x00 0xbe 0x80 0x79 0xb7&lt;br&gt;
0x248b8c8: 0x58 0x80 0x79 0xb7 0x54 0xa9 0x78 0xb7&lt;br&gt;
0x248b8d0: 0xf8 0xb8 0x48 0x02 0x00 0x00 0x00 0x00&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
Поиск такой строки в Интернете поначалу не принес каких-либо полезных результатов, но потом обнаружилась одна ссылка на &lt;a href=&quot;http://www.opensource.apple.com/&quot;&gt;http://www.opensource.apple.com/&lt;/a&gt; (к сожалению, уже не рабочая) со следующим фрагментом:&lt;br&gt;
&lt;pre&gt;// This is the exception class we report -- &quot;GNUCC++&amp;#92;0&quot;.
const _Unwind_Exception_Class __gxx_exception_class
= ((((((((_Unwind_Exception_Class) &apos;G&apos;
 &lt;&lt; 8 | (_Unwind_Exception_Class) &apos;N&apos;)
 &lt;&lt; 8 | (_Unwind_Exception_Class) &apos;U&apos;)
 &lt;&lt; 8 | (_Unwind_Exception_Class) &apos;C&apos;)
 &lt;&lt; 8 | (_Unwind_Exception_Class) &apos;C&apos;)
 &lt;&lt; 8 | (_Unwind_Exception_Class) &apos;+&apos;)
 &lt;&lt; 8 | (_Unwind_Exception_Class) &apos;+&apos;)
 &lt;&lt; 8 | (_Unwind_Exception_Class) &apos;&amp;#92;0&apos;);&lt;/pre&gt;Тогда я вернулся к стекам вызовов других потоков, и обнаружил очень 
подозрительный поток, явно работающий в цикле в момент падения — и этот 
цикл тоже занимается обработкой исключения:&lt;br&gt;
&lt;pre&gt;(gdb) thread 20
[Switching to thread 20 (process 27635)]#0 0xb7e87921 in dl_iterate_phdr () from /lib/libc.so.6
(gdb) where
#0 0xb7e87921 in dl_iterate_phdr () from /lib/libc.so.6
#1 0x0804e837 in _Unwind_Find_FDE (pc=0xb782409c, bases=0xb70209b4) at ../../gcc/unwind-dw2-fde-glibc.c:283
#2 0x0804c950 in uw_frame_state_for (context=0xb7020960, fs=0xb7020860) at ../../gcc/unwind-dw2.c:903
#3 0x0804cfbf in _Unwind_RaiseException_Phase2 (exc=0xbfde3f38, context=0xb7020960) at ../../gcc/unwind.inc:43
#4 0x0804d397 in _Unwind_Resume (exc=0xbfde3f38) at ../../gcc/unwind.inc:220
#5 0xb78f82b0 in Uuid::Uuid () from /home/&apos;work/lib32/libourlibrary.so
#6 0xb782409d in ...&lt;/pre&gt;После этого мы начали смотреть внимательнее на исходный код, где 
вызывалось исключение, и предположили, что проблема лежит в операторе 
placement new, который использовался в данном участке кода. Простой тест
 подтвердил предположение — создание исключения в конструкторе объекта, 
вызванном из оператора placement new, приводил к бесконечному циклу и 
взрывной утечке памяти. Тест был простой — симуляция исключительной 
ситуации в проблемном участке кода, но к сожалению, совсем не простой в 
отрыве от контекста нашего приложения — небольшие тестовые программы 
проблему не воспроизводили. Так же оказалось, что данная проблема 
присутствует только в версии компилятора, которую мы использовали, то 
есть в gcc 3.4 проблема отсутствовала. На радостях, что конкретная 
проблема, уже обошедшаяся в копеечку, решена, дальнейшее расследование 
свернули.&lt;br&gt;
&lt;h5&gt;Не совсем утечка&lt;/h5&gt;
Однажды, после обновления программного продукта, предварительное 
тестирование у клиента обнаружило утечку памяти, не происходившую ранее.
 &lt;br&gt;
&lt;br&gt;
Программа работала под Solaris, под нагрузкой, и использование памяти 
росло резкими скачками по 10-100 MB, при этом достаточно редко — 
временами раз в 2-3 дня, а временами 2-3 раза в день. Рано или поздно 
память, используемая процессом, вырастала до 2+ GB. При этом даже при 
снижении нагрузки до нуля, используемая память (значения RSS и VSS) 
никогда не уменьшалась.&lt;br&gt;
&lt;br&gt;
Непосредственный доступ к серверам получить было нельзя (для настройки 
libumem, к примеру), а воспроизвести проблему в QA не получалось. Хорошо
 и то, что удалось получить обрезанный core dump файл — снимок памяти 
процесса. Но анализ core dump файла практически ничего не дал — стек 
вызовов показывал падение при выделении памяти из-за нехватки оной. При 
этом бОльшая часть памяти не использовалась — почти весь core dump файл 
был занят пустыми страницами по 4 KB, забитыми нулями.&lt;br&gt;
&lt;br&gt;
Ситуация была странная, но постепенно, методом исключения различных 
компонент системы и вдумчивого анализа лог-файлов, была восстановлена 
картина происходящих событий.&lt;br&gt;
&lt;br&gt;
При обновлении системы, одна из клиентских программ перестала посылать 
часть сообщений, предназначенных для мониторинга системы. По 
случайности, отсутствующие сообщения сигнализировали о закрытии 
транзакций, и они должны были чистить внутренние буферы. Соответственно,
 при повышении нагрузки, начинали накапливаться буферизованные 
сообщения, ждущие пары. По случайному совпадению, в данном случае 
сообщения были нетипично большими (десятки килобайт вместо сотен байт), и
 количество клиентских процессов было относительно велико (несколько 
десятков вместо обычных 2-5). Но на самом деле все эти параметры 
великолепно укладывались в ограничения, поддерживаемые системой (при 
условии нормального функционирования). И, хотя программа не ограничивала
 размер внутренних буферов, но она поддерживала механизм для очистки 
&quot;потерянных” транзакций — все неполные сообщения старше 10 минут 
автоматически удалялись. И как раз этот счетчик всегда показывал 0 из-за
 ошибки — механизм был предназначен для практически невероятной 
исключительной ситуации, практически никогда не задействован, и 
недостаточно проверен. Сама очистка исправно работала, но была 
недостаточно эффективна из-за большой нагрузки на память, вызванной 
большим количеством клиентов и большим размером нестандартных сообщений.
 А диагностика проблемы оказалась сильно затруднена из-за неисправного 
счетчика в статистике.&lt;br&gt;
&lt;br&gt;
Почему-же память не возвращалась при снижении нагрузки? Очень просто — 
стандартный диспетчер памяти процесса в Solaris только увеличивает 
адресное пространство процесса, и резервирует освобождаемую процессом 
память для повторного использования, оставляя страницы «занятыми» 
процессом с точки зрения стороннего наблюдателя.&lt;br&gt;
&lt;br&gt;
В нашем случае, одиночные пики активности проблемных клиентов приводили к
 периодическим большим выделениям памяти, в большинстве случаев 
незаметных, так как не превышался предыдущий максимум. И только если 
последний максимум превышался, получалась очередная ступенька, которая 
уже никогда не уменьшалась. Через 10 минут после пика вся память 
освобождалась, но снаружи этого было не видно, только снимок памяти 
показывал, что большая часть памяти забита нулями и не используется.&lt;br&gt;
&lt;br&gt;Решение было простым даже до исправления проблемных клиентов и защиты от
 переполнения буферов — возраст &quot;старых” транзакций был временно 
ограничен 30 секундами, и этого было вполне достаточно для своевременной
 вычистки буферов при данной нагрузке, с большим запасом. Но диагностика
 и поиск неисправности отняли немалое время, главным образом из-за 
некорректной статистики в логах.&lt;br&gt;
&lt;h5&gt;Вместо эпилога&lt;/h5&gt;
У кого были интересные случаи в практике — пишите!</content:encoded>
			<link>https://cpplus.my1.ru/news/utechki_pamjati_v_programmakh_na_s_s_istorija_neskolkikh_bagov/2011-02-01-79</link>
			<category>Новости</category>
			<dc:creator>FazaNaka</dc:creator>
			<guid>https://cpplus.my1.ru/news/utechki_pamjati_v_programmakh_na_s_s_istorija_neskolkikh_bagov/2011-02-01-79</guid>
			<pubDate>Tue, 01 Feb 2011 11:49:53 GMT</pubDate>
		</item>
		<item>
			<title>Обертка для вызова функций по их адресу</title>
			<description>Доброго времени суток!&lt;br&gt;
Было дело — делал я интерфейс для работы с модулями для USB от &lt;a href=&quot;http://www.ftdichip.com/&quot;&gt;FTDI&lt;/a&gt;.
 Пришлось изрядно повозиться с подключением DLL-интерфейса. 
Разочаровавшись в возможностях автоматической линковки Microsoft Visual 
Studio 2008 (&lt;b&gt;UPD&lt;/b&gt;: потом я разобрался с этой темой), я решил это делать вручную. По ходу дела &lt;s&gt;задолбался&lt;/s&gt;&amp;nbsp;
 очень надоело вручную подключать несколько десятков функций. И тогда я 
обратился к Google, C++ и шаблонам. И если подключение DLL в стиле C++ 
вопросов не вызвало, то...</description>
			<content:encoded>Доброго времени суток!&lt;br&gt;
Было дело — делал я интерфейс для работы с модулями для USB от &lt;a href=&quot;http://www.ftdichip.com/&quot;&gt;FTDI&lt;/a&gt;.
 Пришлось изрядно повозиться с подключением DLL-интерфейса. 
Разочаровавшись в возможностях автоматической линковки Microsoft Visual 
Studio 2008 (&lt;b&gt;UPD&lt;/b&gt;: потом я разобрался с этой темой), я решил это делать вручную. По ходу дела &lt;s&gt;задолбался&lt;/s&gt;&amp;nbsp;
 очень надоело вручную подключать несколько десятков функций. И тогда я 
обратился к Google, C++ и шаблонам. И если подключение DLL в стиле C++ 
вопросов не вызвало, то$CUT$ удобный вызов подключенных функций в стиле 
«Error = FT_Open (Num, &amp;amp;_Handler)», где FT_Open- объект, удался не 
сразу. Итог (для таких вот функций) — под катом. Если вкратце — я сделал
 обертку вокруг указателя на функцию.&amp;nbsp;&lt;br&gt;
&lt;h4&gt;Постановка задачи&lt;/h4&gt;
Сразу оговорюсь — я работаю в Windows XP Prof, Visual Studio. Это 
принципиально для получения адреса функции. Впрочем, при работе с 
указателями на функции это не важно.&lt;br&gt;
&lt;br&gt;
Ну так вот, для тех, кто не в теме, вот последовательность для 
нахождения той самой функции FT_Open из FTD2XX.dll средствами WinAPI:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; #include &quot;FTD2XX.h&quot; // библиотека от FTDI&lt;br&gt;typedef FT_STATUS (*pFT_Open) (int, FT_HANDLE *); // тип данных &quot;функция FT_OPEN&quot;&lt;br&gt;&lt;br&gt;// ...&lt;br&gt;&lt;br&gt;HMODULE hMod = LoadLibrary (&quot;FTD2XX.dll&quot;); // загрузка библиотеки - д. б. не ноль&lt;br&gt;pFT_Open pOpen = GetProcAddress (hMod, &quot;FT_Open&quot;); // получили адрес функции - также д. б. не ноль&lt;br&gt;&lt;br&gt;// ...&lt;br&gt;&lt;br&gt;FT_STATUS st = pOpen (0, &amp;amp;hDev); // вызываем функцию&lt;br&gt;&lt;br&gt;// ...&lt;br&gt;&lt;br&gt;FreeLibrary (hMod); // закрыли библиотеку&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;Это не беда, когда функция у вас одна, но в этой самой библиотеке я насчитал 51 функцию. И для каждой мне нужно сделать следующее:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; typedef FT_STATUS (*pFT_Open) (int, FT_HANDLE *); // тип данных &quot;указатель на функцию&quot;&lt;br&gt;pFT_Open pOpen; // переменная &quot;указатель на функцию&quot;&lt;br&gt;pFT_Open pOpen = GetProcAddress (hMod, &quot;FT_Open&quot;); // получение адреса функции &quot;FT_Open&quot;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;Особенно раздражает необходимость генерить кучу typedef. Да, я знаю, можно писать и без typedef, но это выглядеть будет ОМЕРЗИТЕЛЬНО!&lt;br&gt;&lt;br&gt;Посему хочется как-то упростить себе жизнь:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; Funct2&lt;FT_STATUS, int, FT_HANDLE *&amp;gt; Open; // тип данных &quot;функция 2х аргументов&quot;&lt;br&gt;Open = GetProcAddress (hMod, &quot;FT_Open&quot;); // получение адреса функции &quot;FT_Open&quot;&lt;br&gt;&lt;br&gt;// ...&lt;br&gt;&lt;br&gt;FT_STATUS st = Open (0, &amp;amp;hDev); // вызов функции&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;strong&gt;Решение&lt;/strong&gt;&lt;br&gt;&lt;br&gt;В ходе экспериментов и кипения мозгов я получил такой вот шаблон класса:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; template &lt;typename Ret, typename Arg1, typename Arg2,&lt;br&gt;typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue&amp;gt;&lt;br&gt;class Funct2&lt;br&gt;{&lt;br&gt;public:&lt;br&gt; typedef Ret (*tfPtr) (Arg1, Arg2);&lt;br&gt; tfPtr fPtr;&lt;br&gt;&lt;br&gt;public:&lt;br&gt; Funct2 (tfPtr Ptr = 0): fPtr (Ptr) {}&lt;br&gt;&lt;br&gt; Funct2 &amp;amp;operator= (void *Ptr) { fPtr = reinterpret_cast&lt;tfPtr&amp;gt; (Ptr); return *this; }&lt;br&gt; Ret operator () (Arg1 A1, Arg2 A2) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (A1, A2); }&lt;br&gt;}; // class Funct2&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;Думаю, тут все элементарно, но все-таки для непосвященных объясню.&lt;br&gt;&lt;br&gt;Создается шаблон Funct2, которому первым параметром задается тип Ret, возвращаемый функцией. Следующими двумя параметрами — Arg1 и Arg2 — задаются типы аргументов функции. С целью универсиализации обработки ошибок задается тип исключения Except и его значение Value (параметры по умолчанию задаются #define FunctPtrExceptionType и #define FunctPtrExceptionDefValue).&lt;br&gt;&lt;br&gt;В теле шаблона класса задается тип tfPtr «указатель на функцию с двумя параметрами» и сам указатель fPtr.&lt;br&gt;&lt;br&gt;Конструктор по умолчанию задает нулевой указатель или конкретный адрес, если он задан. Также адрес может быть задан через перегруженный operator= (void *Ptr). Почему void * — потому что GetProcAddress () возвращает именно void *. Нет нужды перегружать его сигнатурой operator= (tfPtr Ptr) — компилятор и так понимает, о чем речь.&lt;br&gt;&lt;br&gt;Ну и, наконец, перегружая operator (), мы добиваемся использования класса как функтора, а для пользователя класса — так и вообще простого вызова функции.&lt;br&gt;&lt;br&gt;Удобно? Очень! Смотрите:&lt;br&gt;&lt;br&gt;&lt;strong&gt;Результат&lt;br&gt;&lt;/strong&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; #include &lt; Windows.h &amp;gt; // для GetProcAdress&lt;br&gt;#include &lt; stdio.h &amp;gt; // для printf&lt;br&gt;&lt;br&gt;// **&lt;br&gt;// ** Настройка исключений по умолчанию&lt;br&gt;// **&lt;br&gt;&lt;br&gt;#define FunctPtrExceptionType int // тип данных для исключения по умолчанию&lt;br&gt;#define FunctPtrExceptionDefValue 0 // значение исключения по умолчанию&lt;br&gt;&lt;br&gt;// **&lt;br&gt;// ** Указатель на функцию без аргументов&lt;br&gt;// **&lt;br&gt;&lt;br&gt;template &lt; typename Ret = void, typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue &amp;gt;&lt;br&gt;class Funct0&lt;br&gt;{&lt;br&gt;public:&lt;br&gt; typedef Ret (*tfPtr) (void);&lt;br&gt; tfPtr fPtr;&lt;br&gt;public:&lt;br&gt; Funct0 (tfPtr Ptr = 0): fPtr (Ptr) {}&lt;br&gt; Funct0 &amp;amp;operator= (tfPtr Ptr) { fPtr = Ptr; return this; }&lt;br&gt; Ret operator () (void) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (); }&lt;br&gt;};&lt;br&gt;&lt;br&gt;// **&lt;br&gt;// ** Указатель на функцию с 1 аргументом&lt;br&gt;// **&lt;br&gt;&lt;br&gt;template &lt; typename Ret, typename Arg1, typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue &amp;gt;&lt;br&gt;class Funct1&lt;br&gt;{&lt;br&gt;public:&lt;br&gt; typedef Ret (*tfPtr) (Arg1);&lt;br&gt; tfPtr fPtr;&lt;br&gt;public:&lt;br&gt; Funct1 (tfPtr Ptr = 0): fPtr (Ptr) {}&lt;br&gt; Funct1 &amp;amp;operator= (void *Ptr) { fPtr = reinterpret_cast&lt;tfPtr&amp;gt; (Ptr); return *this; }&lt;br&gt; Ret operator () (Arg1 A1) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (A1); }&lt;br&gt;};&lt;br&gt;&lt;br&gt;// **&lt;br&gt;// ** Указатель на функцию с 2 аргументами&lt;br&gt;// **&lt;br&gt;&lt;br&gt;template &lt; typename Ret, typename Arg1, typename Arg2, typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue &amp;gt;&lt;br&gt;class Funct2&lt;br&gt;{&lt;br&gt;public:&lt;br&gt; typedef Ret (*tfPtr) (Arg1, Arg2);&lt;br&gt; tfPtr fPtr;&lt;br&gt;public:&lt;br&gt; Funct2 (tfPtr Ptr = 0): fPtr (Ptr) {}&lt;br&gt; Funct2 &amp;amp;operator= (void *Ptr) { fPtr = reinterpret_cast&lt;tfPtr&amp;gt; (Ptr); return *this; }&lt;br&gt; Ret operator () (Arg1 A1, Arg2 A2) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (A1, A2); }&lt;br&gt;};&lt;br&gt;&lt;br&gt;// **&lt;br&gt;// ** Примеры вызова функций&lt;br&gt;// **&lt;br&gt;&lt;br&gt;int add (const int A, const int *B)&lt;br&gt;{ int C; C = A + *B; printf (&quot; int add (const int %d, const int %d) = %d&amp;#92;n&quot;, A, *B, C); return C; }&lt;br&gt;&lt;br&gt;void prn (void)&lt;br&gt;{ printf (&quot; void prn (void)&amp;#92;n&quot;); }&lt;br&gt;&lt;br&gt;// **&lt;br&gt;// ** Точка входа&lt;br&gt;// **&lt;br&gt;&lt;br&gt;void main (void)&lt;br&gt;{&lt;br&gt; int i, i2;&lt;br&gt; double d;&lt;br&gt; long l;&lt;br&gt; Funct0&lt;&amp;gt; prner (prn);&lt;br&gt; Funct1&lt; double, long *, int, 2 &amp;gt; longer;&lt;br&gt; Funct2&lt; int, const int, const int * &amp;gt; adder;&lt;br&gt;&lt;br&gt; adder = add;&lt;br&gt; longer = GetProcAddress (0, &quot;Longer&quot;);&lt;br&gt;&lt;br&gt; try&lt;br&gt; {&lt;br&gt; prner ();&lt;br&gt; i2 = 6;&lt;br&gt; i = adder (5, &amp;amp;i2);&lt;br&gt; d = longer (&amp;amp;l);&lt;br&gt; }&lt;br&gt; catch (int val)&lt;br&gt; {&lt;br&gt; switch (val)&lt;br&gt; {&lt;br&gt; case 2: printf (&quot; *** не удалось определить адрес функции!&amp;#92;n&quot;); break;&lt;br&gt; default: printf (&quot; *** ошибка вызова функции!&amp;#92;n&quot;); break;&lt;br&gt; }&lt;br&gt; }&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;&lt;strong&gt;Итог&lt;/strong&gt;&lt;br&gt;&lt;br&gt;Дизассемблер в режиме Release показал, что накладные расходы при вызове такой функции — проверка 0-го значение и в связи с этим еще один call. Я думаю, для современных PC это не беда.&lt;br&gt;&lt;br&gt;Для совершенства тут можно как-то доработать тему исключений — было бы хорошо туда передавать текстовые строки, свои произвольные классы ошибок и т. п. Но лень я недостаточно хорошо знаю шаблоны, чтобы это реализовать.&lt;br&gt;&lt;br&gt;Ну и, понятное дело, надо наклепать разных вариантов Funct для 3х, 4х и т. д. аргументов. Хорошо бы придумать какой-то макрос, который бы их генерил…&lt;br&gt;&lt;br&gt;Ну и, еще более понятное дело, надо все это вынести в отдельный .H-файл.&lt;br&gt;&lt;br&gt;Я надеюсь, кому-то сэкономил время. Буду благодарен за конструктивные комментарии!&lt;br&gt;&lt;br&gt;P. S. По ходу эксплуатации вскрылась такая неприятная вещь, как соглашение о вызовах. Похоже, надо делать Funct0Stdcall, Funct0Cdecl; Funct1Stdcall, Funct1Cdecl…&lt;br&gt;</content:encoded>
			<link>https://cpplus.my1.ru/news/obertka_dlja_vyzova_funkcij_po_ikh_adresu/2011-01-26-78</link>
			<category>Новости</category>
			<dc:creator>FazaNaka</dc:creator>
			<guid>https://cpplus.my1.ru/news/obertka_dlja_vyzova_funkcij_po_ikh_adresu/2011-01-26-78</guid>
			<pubDate>Wed, 26 Jan 2011 12:06:07 GMT</pubDate>
		</item>
		<item>
			<title>Объективные недостатки С++</title>
			<description>Публикуется в рамках недели ненависти к С++ на хабре. Если вам все это еще не надоело, то добро пожаловать под кат. &lt;br&gt;Ранее уже были перечислены глупые причины для ненависти, поэтому я 
остановлюсь на реальных. Итак, за что же действительно стоит ненавидеть 
С++.&lt;br&gt;Причина первая, самая серьезная — грамматика. Язык С++ описывается 
контекстно зависимой грамматикой, следовательно алгоритм его разбора 
имеет высокую сложность. ...</description>
			<content:encoded>Публикуется в рамках недели ненависти к С++ на хабре. Если вам все это еще не надоело, то добро пожаловать под кат. &lt;br&gt;Ранее уже были перечислены глупые причины для ненависти, поэтому я 
остановлюсь на реальных. Итак, за что же действительно стоит ненавидеть 
С++.&lt;br&gt;Причина первая, самая серьезная — грамматика. Язык С++ описывается 
контекстно зависимой грамматикой, следовательно алгоритм его разбора 
имеет высокую сложность. $CUT$Все это выливается в конечном счете в долгие 
билды и в отсутствие нормальных инструментов для анализа кода. Написать 
парсер С++ задача весьма нетривиальная, написать инкрементальный парсер,
 по всей видимости вообще невозможно, именно поэтому, нормальные 
инструменты для анализа и рефакторинга С++ отсутствуют, а при малейшем 
изменении в файле, компилятор делает так много лишней работы. Вот 
простой пример красоты синтаксиса С++:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; // выбор типа на этапе компиляции&lt;br&gt;template&lt;bool Cond, typename A, typename B&amp;gt; struct Select;&lt;br&gt;...&lt;br&gt;Select&lt;SomeConst &amp;gt; 0, SomeType, SomeOtherType&amp;gt;::result x;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;Красиво, правда? :)&lt;br&gt;
&lt;br&gt;
Вторая причина, тоже очень неприятная — отсутствие модулей. С++ 
использует заголовочные файлы, так же как и Си, добро пожаловать в 
шестидесятые. Каждый заголовочный файл, анализируется столько раз, 
сколько раз он включается директивой include(upd: это не совсем верно, в
 комментариях меня поправили). В сочетании с первым пунктом, это 
выливается в неприлично долгие билды. Конечно, прекомпиляция 
заголовочных файлов решает эту проблему, но лишь от части. Во первых, 
настроить прекомпиляцию задача не простая, во вторых, она спасает от 
повторной обработки стандартных заголовочных файлов. Существуют еще и 
Ваши заголовочные файлы, которые нельзя прекомпилировать, так как они 
иногда меняются.&lt;br&gt;
&lt;br&gt;
Третья причина — шаблоны. Механизм шаблонов — основное преимущество С++.
 В этом языке нет кортежей, нет вариантных типов, нет лямбда 
выражений(точнее не было), но все это можно реализовать с помощью 
шаблонов, что и было сделано. Шаблоны, это полный по Тьюрингу, 
функциональный язык программирования, который выполняет вычисления над 
типами во время компиляции. Правда круто? Но есть один нюанс — 
компиляторы С++ — не очень хороши в выполнении сложных шаблонных 
мата-программ. Если нормальный код на С++ компилируется просто долго, то
 код на С++, использующий boost::lambda, boost::spirit или boost::mpl, 
компилируется целую вечность. Обычно, время компиляции напрямую зависит 
от количества типов, с которыми приходится работать компилятору. Когда 
вы пишете&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;&amp;nbsp;SomeType&lt;int&amp;gt; x;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;компилятор инстанциирует шаблон, создает новый тип. Когда вы пишете —&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;boost::tuple&lt;int, long, std::string&amp;gt; myTuple;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;— компилятор инстанциирует как минимум 4 новых типа, так как тип myTyple
 — это мета-программа, которая рекурсивно вычисляет тип кортежа. Сколько
 шаблонов инстанциируется в следующем примере, мне страшно представить:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;std::for_each(v.begin(), v.end(),&lt;br&gt; ( &lt;br&gt; switch_statement(&lt;br&gt; _1,&lt;br&gt; case_statement&lt;0&amp;gt;(std::cout &lt;&lt; constant(&quot;zero&quot;)),&lt;br&gt; case_statement&lt;1&amp;gt;(std::cout &lt;&lt; constant(&quot;one&quot;)),&lt;br&gt; default_statement(cout &lt;&lt; constant(&quot;other: &quot;) &lt;&lt; _1)&lt;br&gt; ), &lt;br&gt; cout &lt;&lt; constant(&quot;&amp;#92;n&quot;) &lt;br&gt; )&lt;br&gt;);&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;В недалеком будущем, благодаря проекту Clang, эта проблема может потерять актуальность.&lt;br&gt;
&lt;br&gt;
Причина четвертая — сообщения об ошибках. Если вы пишете обычный код, то
 все замечательно, вы будете получать понятные сообщения об ошибках. 
Если вы используете STL и (особенно)Boost, то сможете наблюдать 
сообщения об ошибках на несколько экранов. Это не фича компилятора, это 
фича языка, недостаток дизайна. Пример — есть несколько перегрузок одной
 функции, которые используют идиому SFINAE, для более точного управления
 перегрузкой. Скажем, мы хотели добиться того, что-бы определенная 
перегрузка вызывалась тогда, когда у типа параметра есть определенный 
typedef или еще что-нибудь в этом роде. В случае, если компилятор не 
найдет нужную перегрузку, мы получим сообщение о том, что такой функции 
вообще нет. Все из-за того, что тогда, когда происходит substitution 
falure(которое is not an error), компилятор выбрасывает функцию из 
списка перегрузок. &lt;br&gt;
Эту проблему хотели решить в C++ 0x с помощью концептов, но видимо не 
судьба. Поэтому, компилятор С++ всегда будет сообщать о том, что 
тип/функция отсутствует, вместо того, что-бы сообщить о том, что 
параметром шаблона может быть только определенный тип.&lt;br&gt;
&lt;br&gt;
В принципе, все эти проблемы можно так или иначе обойти, настроить 
прекомпиляцию, купить хорошую IDE, не использовать библиотеки интенсивно
 использующие метапрограммирование на основе шаблонов и не писать &lt;a href=&quot;http://habrahabr.ru/blogs/cpp/108196/&quot;&gt;такой&lt;/a&gt; код :)&lt;br&gt;
В качестве примера того, как нужно писать на С++, можно привести webkit.</content:encoded>
			<link>https://cpplus.my1.ru/news/obektivnye_nedostatki_s/2011-01-24-77</link>
			<category>Новости</category>
			<dc:creator>FazaNaka</dc:creator>
			<guid>https://cpplus.my1.ru/news/obektivnye_nedostatki_s/2011-01-24-77</guid>
			<pubDate>Mon, 24 Jan 2011 11:16:04 GMT</pubDate>
		</item>
		<item>
			<title>Многопоточный Observer на С++ (практика)</title>
			<description>Есть много вариаций на тему данного паттерна, но большинство примеров не подходит для многопоточных приложений.&lt;br&gt;&amp;nbsp;
В этой статье я хочу поделится опытом применения паттерна в
многопоточных приложениях и опишу основные проблемы, с которыми мне
приходилось сталкиваться....</description>
			<content:encoded>Есть много вариаций на тему данного паттерна, но большинство примеров не подходит для многопоточных приложений.&lt;br&gt;&amp;nbsp;
В этой статье я хочу поделится опытом применения паттерна в
многопоточных приложениях и опишу основные проблемы, с которыми мне
приходилось сталкиваться.$CUT$&lt;br&gt;&lt;br&gt;
Цель данной стати — обратить внимание разработчиков на проблемы, с
которыми можно столкнуться при создании многопоточных приложений.
Выявить подводные камни в реализации коммуникации между компонентами в
многопоточном приложении.&lt;br&gt;&lt;br&gt;
Если Вам необходимо готовое решение, обратите внимание на библиотеку Signals2, котрая включена в boost с &lt;a href=&quot;http://www.boost.org/users/history/version_1_39_0&quot;&gt;мая 2009-го года&lt;/a&gt;.&lt;br&gt;&amp;nbsp;
Я не пытаюсь предоставить решение, которое можно было бы использовать в
готовом виде. Но тем не менее, ознакомившись с материалом, можно
обойтись без использования сторонних библиотек, в тех проектах, в
которых они по каким-либо причинам не доступны или нежелательны
(драйвера, низкоуровневые приложения и т.п.).&lt;br&gt;
&lt;h4&gt;Предметная область&lt;/h4&gt;
&lt;h5&gt;Действующие лица&lt;/h5&gt;
NotificationSender — объект, рассылающий сообщения.&lt;br&gt;
Как правило это рабочий поток, извещающий об изменении своего
состояния, которое необходимо отобразить на пользовательском интерфейсе.&lt;br&gt;
NotificationListener — объект, реализующий обработку уведомлений.&lt;br&gt;
Как правило это объект, который управляет отображением части пользовательского интерфейса связанного с фоновой задачей.&lt;br&gt;
Таких объектов может быть множество, при этом они могут
подключаться/отключаться динамически (к примеру открытие далогового
окна, где показываются детали выполнения задачи)&lt;br&gt;NotificationDispatcher — объект, управляющий подписчиками и рассылкой сообщений.&lt;br&gt;
&lt;h5&gt;Взаимодействие между объектами&lt;/h5&gt;
Рассылка сообщений всем подписчикам.&lt;br&gt;
Процесс подписки/прекшащения подписки.&lt;br&gt;
Время жизни объектов.&lt;br&gt;&amp;nbsp;
В данной статье описан метод синхронной рассылки сообщений. Это
означает, что вызов функции SendMessage будет синхронным, и поток,
вызывающий этот метод будет ожидать завершения обработки сообщений
всеми подписчиками. В ряде случаев такой подход удобней ассинхронной
рассылки, но при этом в нем есть трудности с прекращением подписки.&lt;br&gt;
&lt;h4&gt;Простейшая реализация для однопоточной среды&lt;/h4&gt;
&lt;pre&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; typedef unsigned __int64 SubscriberId;&lt;br&gt;class CSubscriber&lt;br&gt;{&lt;br&gt;public:&lt;br&gt; virtual ~CSubscriber(){}&lt;br&gt; virtual void MessageHandler(void* pContext) = 0;&lt;br&gt; SubscriberId GetSubscriberId() {return (SubscriberId)this;}&lt;br&gt;};&lt;br&gt;&lt;br&gt;class CDispatcher&lt;br&gt;{&lt;br&gt;private:&lt;br&gt; typedef std::vector&lt;CSubscriber*&amp;gt; CSubscriberList;&lt;br&gt;public:&lt;br&gt; SubscriberId Subscribe(CSubscriber* pNewSubscriber)&lt;br&gt; {&lt;br&gt; for(size_t i = 0; i &lt; m_SubscriberList.size(); ++i)&lt;br&gt; {&lt;br&gt; if(m_SubscriberList[i]-&amp;gt;GetSubscriberId() == pNewSubscriber-&amp;gt;GetSubscriberId())&lt;br&gt; {&lt;br&gt; return 0;&lt;br&gt; }&lt;br&gt; }&lt;br&gt; m_SubscriberList.push_back(pNewSubscriber);&lt;br&gt; return pNewSubscriber-&amp;gt;GetSubscriberId();&lt;br&gt; }&lt;br&gt; bool Unsubscribe(SubscriberId id)&lt;br&gt; {&lt;br&gt; for(size_t i = 0; i &lt; m_SubscriberList.size(); ++i)&lt;br&gt; {&lt;br&gt; if(m_SubscriberList[i]-&amp;gt;GetSubscriberId() == id)&lt;br&gt; {&lt;br&gt; m_SubscriberList.erase(m_SubscriberList.begin() + i);&lt;br&gt; return true;&lt;br&gt; }&lt;br&gt; }&lt;br&gt; return false;&lt;br&gt; }&lt;br&gt; void SendMessage(void* pContext)&lt;br&gt; {&lt;br&gt; for(size_t i = 0; i &lt; m_SubscriberList.size(); ++i)&lt;br&gt; {&lt;br&gt; m_SubscriberList[i]-&amp;gt;MessageHandler(pContext);&lt;br&gt; }&lt;br&gt; }&lt;br&gt;private:&lt;br&gt; CSubscriberList m_SubscriberList;&lt;br&gt;};&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;Здесь уникальный идентификатор подписчика — адресс объекта подписчика,
функция GetSubscriberId возвращает всегда одинаковое значение для
одного объекта подписчика в не зависимости от преобразования типов.&lt;br&gt;
&lt;h5&gt;Пример использования&lt;/h5&gt;
&lt;pre&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; class CListener:&lt;br&gt; public CSubscriber&lt;br&gt;{&lt;br&gt; virtual void MessageHandler(void* pContext)&lt;br&gt; {&lt;br&gt; wprintf(L&quot;%d&amp;#92;n&quot;, *((int*)pContext));&lt;br&gt; }&lt;br&gt;};&lt;br&gt;int _tmain(int argc, _TCHAR* argv[])&lt;br&gt;{&lt;br&gt; CDispatcher Dispatcher;&lt;br&gt; CListener Listener1;&lt;br&gt; CListener Listener2;&lt;br&gt; Dispatcher.Subscribe(&amp;amp;Listener1);&lt;br&gt; Dispatcher.Subscribe(&amp;amp;Listener2);&lt;br&gt; for(int i = 0; i &lt; 5; ++i)&lt;br&gt; {&lt;br&gt; Dispatcher.SendMessage(&amp;amp;i);&lt;br&gt; }&lt;br&gt; Dispatcher.Unsubscribe(Listener2.GetSubscriberId());&lt;br&gt; Dispatcher.Unsubscribe(Listener1.GetSubscriberId());&lt;br&gt; return 0;&lt;br&gt;}&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;code class=&quot;cpp&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Отключение подписчика внутри обработчика сообщений&lt;/h4&gt;&amp;nbsp;
В примере есть проблема, не связанная с многопоточностью. Эта проблема
проявляется, когда мы пытаемся отписаться внутри обработчика
MessageHandler. Данная проблемма будет решена копированием списка
подписчиков перед вызовом MessageHandler.&lt;br&gt;
&lt;h4&gt;Переходим к многопоточной среде&lt;/h4&gt;
С одним потоком такой код будет работать довольно стабильно.&lt;br&gt;
Давайте посмотрим что будет при работе нескольких потоков.&lt;br&gt;
&lt;pre&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; CDispatcher g_Dispatcher;&lt;br&gt;DWORD WINAPI WorkingThread(PVOID pParam)&lt;br&gt;{&lt;br&gt; for(int i = 0;;++i)&lt;br&gt; {&lt;br&gt; g_Dispatcher.SendMessage(&amp;amp;i);&lt;br&gt; }&lt;br&gt;};&lt;br&gt;int _tmain(int argc, _TCHAR* argv[])&lt;br&gt;{&lt;br&gt; ::CreateThread(NULL, 0, WorkingThread, NULL, 0, NULL);&lt;br&gt; CListener Listener1;&lt;br&gt; CListener Listener2;&lt;br&gt; for(;;)&lt;br&gt; {&lt;br&gt; g_Dispatcher.Subscribe(&amp;amp;Listener1);&lt;br&gt; g_Dispatcher.Subscribe(&amp;amp;Listener2);&lt;br&gt;&lt;br&gt; g_Dispatcher.Unsubscribe(Listener1.GetSubscriberId());&lt;br&gt; g_Dispatcher.Unsubscribe(Listener2.GetSubscriberId());&lt;br&gt; }&lt;br&gt; return 0;&lt;br&gt;}&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;code class=&quot;cpp&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
Рано или позно произойдет креш.&lt;br&gt;
Проблема заключается в добавлении/удалении подписчиков и одновременной
рассылке уведомлений (многопоточный доступ к
CDispatcher::m_SubscriberList в нашем примере).&lt;br&gt;&amp;nbsp;
Здесь необходима синхронизация доступа к списку подписчиков.&lt;br&gt;
&lt;h4&gt;Синхронизация доступа к списку подписчиков&lt;/h4&gt;
&lt;pre&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; class CDispatcher&lt;br&gt;{&lt;br&gt;private:&lt;br&gt; typedef std::vector&lt;CSubscriber*&amp;gt; CSubscriberList;&lt;br&gt;public:&lt;br&gt; SubscriberId Subscribe(CSubscriber* pNewSubscriber)&lt;br&gt; {&lt;br&gt; CScopeLocker ScopeLocker(m_Lock);&lt;br&gt; for(size_t i = 0; i &lt; m_SubscriberList.size(); ++i)&lt;br&gt; {&lt;br&gt; if(m_SubscriberList[i]-&amp;gt;GetSubscriberId() == pNewSubscriber-&amp;gt;GetSubscriberId())&lt;br&gt; {&lt;br&gt; return 0;&lt;br&gt; }&lt;br&gt; }&lt;br&gt; m_SubscriberList.push_back(pNewSubscriber);&lt;br&gt; return pNewSubscriber-&amp;gt;GetSubscriberId();&lt;br&gt; }&lt;br&gt; bool Unsubscribe(SubscriberId id)&lt;br&gt; {&lt;br&gt; CScopeLocker ScopeLocker(m_Lock);&lt;br&gt; for(size_t i = 0; i &lt; m_SubscriberList.size(); ++i)&lt;br&gt; {&lt;br&gt; if(m_SubscriberList[i]-&amp;gt;GetSubscriberId() == id)&lt;br&gt; {&lt;br&gt; m_SubscriberList.erase(m_SubscriberList.begin() + i);&lt;br&gt; return true;&lt;br&gt; }&lt;br&gt; }&lt;br&gt; return false;&lt;br&gt; }&lt;br&gt; void SendMessage(void* pContext)&lt;br&gt; {&lt;br&gt; CScopeLocker ScopeLocker(m_Lock);&lt;br&gt; for(size_t i = 0; i &lt; m_SubscriberList.size(); ++i)&lt;br&gt; {&lt;br&gt; m_SubscriberList[i]-&amp;gt;MessageHandler(pContext);&lt;br&gt; }&lt;br&gt; }&lt;br&gt;private:&lt;br&gt; CSubscriberList m_SubscriberList;&lt;br&gt; CLock m_Lock;&lt;br&gt;};&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;code class=&quot;cpp&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
Синхронизация доступа была реализована при помощи объектов синхронизации (Critical section или Mutex).&lt;br&gt;
Для большей переносимости и для того, чтобы не отвлекаться от сути
происходящего, абстрагируемся от прямых вызовов платформенно-зависимых
функций, типа EnterCriticalSection. Для этого служит класс CLock.&lt;br&gt;
Для устойчивости к с++ исключениям удобно использовать технологию RAII,
а именно класс CScopeLocker, который в конструкторе захватывает объект
синхронизации, а в деструкторе освобождает его.&lt;br&gt;&amp;nbsp;
При такой реализации программа не будет падать, но нас поджидает еще одна неприятная ситуация.&lt;br&gt;
&lt;h4&gt;Борьба с взаимной блокировкой потоков (deadlock)&lt;/h4&gt;
Допустим у нас есть некий поток, выполняющий какую-то фоновую задачу и есть окно, где отображается ход выполнения этой задачи.&lt;br&gt;
Как правило, поток посылает уведомление классу окна, который в свою
очередь вызывает системную функцию SendMessage, которая инициирует
какие-то действия в контексте оконной процедуры.&lt;br&gt;
Системная функция SendMessage является блокирующей, она отсылает сообщение потоку окна и ждет пока тот его обработает.&lt;br&gt;
Если подключение/отключение объекта-слушателя будет происходить также в
контексте оконной процедуры (в потоке окна) возможна взаимная
блокировка потоков, так называемый deadlock.&lt;br&gt;
Такой deadlock может воспроизоводится крайне редко (в момент вызова
Subscribe/Unsubscribe и одновременном вызове MessageHandler в отдельном
потоке)&lt;br&gt;&amp;nbsp;
Следующий код эмулирует ситуацию с блокирующим вызовом системной ф-ции SendMessage.&lt;br&gt;
&lt;pre&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; CDispatcher g_Dispatcher;&lt;br&gt;CLock g_Lock;&lt;br&gt;class CListener:&lt;br&gt; public CSubscriber&lt;br&gt;{&lt;br&gt; virtual void MessageHandler(void* pContext)&lt;br&gt; {&lt;br&gt; //Эмулируем блокирующий вызов SendMessage&lt;br&gt; g_Lock.Lock();&lt;br&gt; wprintf(L&quot;%d&amp;#92;n&quot;, *((int*)pContext));&lt;br&gt; g_Lock.Unlock();&lt;br&gt; }&lt;br&gt;};&lt;br&gt;DWORD WINAPI WorkingThread(PVOID pParam)&lt;br&gt;{&lt;br&gt; for(int i = 0;;++i)&lt;br&gt; {&lt;br&gt; g_Dispatcher.SendMessage(&amp;amp;i);&lt;br&gt; }&lt;br&gt;};&lt;br&gt;int _tmain(int argc, _TCHAR* argv[])&lt;br&gt;{&lt;br&gt; ::CreateThread(NULL, 0, WorkingThread, NULL, 0, NULL);&lt;br&gt; CListener Listener1;&lt;br&gt; CListener Listener2;&lt;br&gt; for(;;)&lt;br&gt; {&lt;br&gt; //Эмулируем контекст оконной процедуры (обработчик оконного сообщения)&lt;br&gt; g_Lock.Lock();&lt;br&gt; g_Dispatcher.Subscribe(&amp;amp;Listener1);&lt;br&gt; g_Dispatcher.Subscribe(&amp;amp;Listener2);&lt;br&gt; g_Lock.Unlock();&lt;br&gt; Sleep(0);&lt;br&gt; g_Lock.Lock();&lt;br&gt; g_Dispatcher.Unsubscribe(Listener1.GetSubscriberId());&lt;br&gt; g_Dispatcher.Unsubscribe(Listener2.GetSubscriberId());&lt;br&gt; g_Lock.Unlock();&lt;br&gt; }&lt;br&gt; return 0;&lt;br&gt;}&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;code class=&quot;cpp&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
Проблема заключается в том, что главный поток захватывает глобальный
объект синхронизации g_Lock (при аналогии с оконной процедурой —
выполняется в контексте оконного потока), и затем вызывает метод
Subscribe/Unsubscribe, который внутри пытается захватить второй объект
синхронизации CDispatcher::m_Lock.&lt;br&gt;&amp;nbsp;
В этот момент рабочий поток посылает уведомление, захватив при этом
CDispatcher::m_Lock в функции CDispatcher::SendMessage, и затем
пытается захватить глобальный объект синхронизации g_Lock (при аналогии
с оконом — вызывает системную функцию SendMessage).&lt;br&gt;
&lt;pre&gt;Поток окна A -&amp;gt; B
Рабочий поток B -&amp;gt; A&lt;/pre&gt;
Это можно назвать класическим deadlock-ом.&lt;br&gt;
Проблема скрывается в функции CDispatcher::SendMessage().&lt;br&gt;
Здесь должно соблюдаться правило — нельзя вызывать callback-функцию захватив при этом какой-либо объект синхронизации.&lt;br&gt;&amp;nbsp;
Итак, убираем блокировку при рассылке уведомлений.&lt;br&gt;
&lt;pre&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; void SendMessage(void* pContext)&lt;br&gt;{&lt;br&gt; CSubscriberList SubscriberList;&lt;br&gt; {&lt;br&gt; CScopeLocker ScopeLocker(m_Lock);&lt;br&gt; SubscriberList = m_SubscriberList;&lt;br&gt; }&lt;br&gt; for(size_t i = 0; i &lt; SubscriberList.size(); ++i)&lt;br&gt; {&lt;br&gt; SubscriberList[i]-&amp;gt;MessageHandler(pContext);&lt;br&gt; }&lt;br&gt;}&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;code class=&quot;cpp&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Контроль времени жизни подписчиков&lt;/h4&gt;
После того, как мы убрали deadlock, появилась другая проблема — время жизни объектов-подписчиков.&lt;br&gt;
У нас больше нет гарантии, что метод MessageHandler не будет вызван
после вызова Unsubscribe, и по этому мы не можем удалять
объект-подписчик непосредственно после вызова Unsubscribe.&lt;br&gt;
В данной ситуации проще всего контролировать время жизни объектов-подписчиков с использованием счетчика ссылок.&lt;br&gt;
Для этого можно исползовать технологию COM — унаследовать интерфейс
CSubscriber от IUnknown и использовать ATL CComPtr для списка
объектов-подписчиков, тоесть заменить std::vector&lt;CSubscriber*&amp;gt;
на std::vector&lt;CComPtr&amp;gt;.&lt;br&gt;
Но такая реализация чревата дополнительными расходами на реализацию
классов-подписчиков, так как в каждом из них должны быть реализованы
методы AddRef/Release и ненужный QueryInterface, хотя если в проекте
активно используется COM, то такой подход может иметь приемущество.&lt;br&gt;&amp;nbsp;
Для контроля времени жизни объектов-подписчиков с исползованием счетчика ссылок хорошо подойдут умные указатели.&lt;br&gt;
&lt;h4&gt;Простая реализация для многопоточной среды&lt;/h4&gt;
&lt;pre&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; typedef unsigned __int64 SubscriberId;&lt;br&gt;class CSubscriber&lt;br&gt;{&lt;br&gt;public:&lt;br&gt; virtual ~CSubscriber(){}&lt;br&gt; virtual void MessageHandler(void* pContext) = 0;&lt;br&gt; SubscriberId GetSubscriberId() {return (SubscriberId)this;}&lt;br&gt;};&lt;br&gt;typedef boost::shared_ptr&lt;CSubscriber&amp;gt; CSubscriberPtr;&lt;br&gt;&lt;br&gt;class CDispatcher&lt;br&gt;{&lt;br&gt;private:&lt;br&gt; typedef std::vector&lt;CSubscriberPtr&amp;gt; CSubscriberList;&lt;br&gt;public:&lt;br&gt; SubscriberId Subscribe(CSubscriberPtr pNewSubscriber)&lt;br&gt; {&lt;br&gt; CScopeLocker ScopeLocker(m_Lock);&lt;br&gt; for(size_t i = 0; i &lt; m_SubscriberList.size(); ++i)&lt;br&gt; {&lt;br&gt; if(m_SubscriberList[i]-&amp;gt;GetSubscriberId() == pNewSubscriber-&amp;gt;GetSubscriberId())&lt;br&gt; {&lt;br&gt; return 0;&lt;br&gt; }&lt;br&gt; }&lt;br&gt; m_SubscriberList.push_back(pNewSubscriber);&lt;br&gt; return pNewSubscriber-&amp;gt;GetSubscriberId();&lt;br&gt; }&lt;br&gt; bool Unsubscribe(SubscriberId id)&lt;br&gt; {&lt;br&gt; CSubscriberPtr toRelease;&lt;br&gt; CScopeLocker ScopeLocker(m_Lock);&lt;br&gt; for(size_t i = 0; i &lt; m_SubscriberList.size(); ++i)&lt;br&gt; {&lt;br&gt; if(m_SubscriberList[i]-&amp;gt;GetSubscriberId() == id)&lt;br&gt; {&lt;br&gt; toRelease = m_SubscriberList[i];&lt;br&gt; m_SubscriberList.erase(m_SubscriberList.begin() + i);&lt;br&gt; return true;&lt;br&gt; }&lt;br&gt; }&lt;br&gt; return false;&lt;br&gt; }&lt;br&gt; void SendMessage(void* pContext)&lt;br&gt; {&lt;br&gt; CSubscriberList SubscriberList;&lt;br&gt; {&lt;br&gt; CScopeLocker ScopeLocker(m_Lock);&lt;br&gt; SubscriberList = m_SubscriberList;&lt;br&gt; }&lt;br&gt; for(size_t i = 0; i &lt; SubscriberList.size(); ++i)&lt;br&gt; {&lt;br&gt; SubscriberList[i]-&amp;gt;MessageHandler(pContext);&lt;br&gt; }&lt;br&gt; }&lt;br&gt;private:&lt;br&gt; CSubscriberList m_SubscriberList;&lt;br&gt; CLock m_Lock;&lt;br&gt;};&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;code class=&quot;cpp&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
В данной реализации я заменил «голый» указатель CSubscriber* на «умный»
указатель со счетчиком ссылок, такой оказался в библиотеке boost.&lt;br&gt;
Также в функцию Unsubscribe я добавил переменную toRelease для того,
чтобы вызвать деструктор объекта-подписчика уже после вызова Unlock
(нельзя вызывать callback-функцию, включая деструктор объекта
подписчика, захватив при этом какой-либо объект синхронизации).&lt;br&gt;&amp;nbsp;
Cтоит обратить внимание на то, что в функции SendMessage происходит
копирование списка умных указателей (после копирования все указатели
увеличивают свои счетчики ссылок, а при выходе из функции уменьшают,
что и контролирует время жизни объектов-подписчиков)&lt;br&gt;
&lt;h4&gt;Тестируем&lt;/h4&gt;
&lt;pre&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; CDispatcher g_Dispatcher;&lt;br&gt;CLock g_Lock;&lt;br&gt;class CListener:&lt;br&gt; public CSubscriber&lt;br&gt;{&lt;br&gt; virtual void MessageHandler(void* pContext)&lt;br&gt; {&lt;br&gt; //Эмулируем блокирующий вызов SendMessage&lt;br&gt; g_Lock.Lock();&lt;br&gt; wprintf(L&quot;%d&amp;#92;n&quot;, *((int*)pContext));&lt;br&gt; g_Lock.Unlock();&lt;br&gt; }&lt;br&gt;};&lt;br&gt;DWORD WINAPI WorkingThread(PVOID pParam)&lt;br&gt;{&lt;br&gt; for(int i = 0;;++i)&lt;br&gt; {&lt;br&gt; g_Dispatcher.SendMessage(&amp;amp;i);&lt;br&gt; }&lt;br&gt;};&lt;br&gt;int _tmain(int argc, _TCHAR* argv[])&lt;br&gt;{&lt;br&gt; ::CreateThread(NULL, 0, WorkingThread, NULL, 0, NULL);&lt;br&gt; for(;;)&lt;br&gt; {&lt;br&gt; boost::shared_ptr&lt;CListener&amp;gt; pListener1(new CListener);&lt;br&gt; boost::shared_ptr&lt;CListener&amp;gt; pListener2(new CListener);&lt;br&gt; //Эмулируем контекст оконной процедуры (обработчик оконного сообщения)&lt;br&gt; g_Lock.Lock();&lt;br&gt; g_Dispatcher.Subscribe(pListener1);&lt;br&gt; g_Dispatcher.Subscribe(pListener2);&lt;br&gt; g_Lock.Unlock();&lt;br&gt; Sleep(0);&lt;br&gt; g_Lock.Lock();&lt;br&gt; g_Dispatcher.Unsubscribe(pListener1-&amp;gt;GetSubscriberId());&lt;br&gt; g_Dispatcher.Unsubscribe(pListener2-&amp;gt;GetSubscriberId());&lt;br&gt; g_Lock.Unlock();&lt;br&gt; }&lt;br&gt; return 0;&lt;br&gt;}&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;code class=&quot;cpp&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Соптимизированная реализация для многопоточной среды&lt;/h4&gt;
Как правило вызов функции SendMessage будет происходить намного чаще
чем Subscribe/Unsubscribe. При большом количестве подписчиков узким
местом может стать копирование списка подписчиков внутри SendMessage.&lt;br&gt;
Копирование списка подписчиков можно перенести в функции
Subscribe/Unsubscribe. Это будет похоже на методику из lock-free
алгоритмов.&lt;br&gt;
Объект CDispatcher будет хранить список подписчиков не на прямую, а при
помощи умного указателя. Внутри функции SendMessage мы будем получать
указатель на текущий список подписчиков и работать с ним. В функциях
Subscribe/Unsubscribe мы будем каждый раз создавать новый список
подписчиков и перенаправлять указатель внутри объекта CDispatcher на
новый список подписчиков. Таким образом в то время, когда указатель на
список подписчиков в объекте CDispatcher будет указывать уже на новый
список подписчиков, ф-ция SendMessage по прежнему будет работать со
старым списком. Так как старый список подписчиков никто не изменяет, то
все будет работать стабильно в многопоточной среде.&lt;br&gt;
В принципе, можно несколько модифицировать функции
Subscribe/Unsubscribe и реализовать полностью lock-free алгоритм, но
это уже другая тема.&lt;br&gt;
Медот Unsubscribe является асинхронным и не гарантирует после своего
завершения полное прекращение рассылки, половинное решение — подписчик
получает уведомление о прекращении подписки при помощи ф-ции
UnsubscribeHandler. Для реализации этого поведения добавлен
промежуточный класс CSubscriberItem, который в своем деструкоторе
вызывает ф-цию UnsubscribeHandler.&lt;br&gt;
&lt;pre&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; namespace Observer&lt;br&gt;{&lt;br&gt; //////////////////////////&lt;br&gt; // Subscriber&lt;br&gt; //////////////////////////&lt;br&gt; typedef unsigned __int64 SubscriberId;&lt;br&gt; class CSubscriber&lt;br&gt; {&lt;br&gt; public:&lt;br&gt; virtual ~CSubscriber(){}&lt;br&gt; virtual void MessageHandler(void* pContext) = 0;&lt;br&gt; virtual void UnsubscribeHandler() = 0;&lt;br&gt; SubscriberId GetSubscriberId() {return (SubscriberId)this;}&lt;br&gt; };&lt;br&gt; typedef boost::shared_ptr&lt;CSubscriber&amp;gt; CSubscriberPtr;&lt;br&gt;&lt;br&gt; //////////////////////////////////////////////////////////////////////&lt;br&gt; // Dispatcher&lt;br&gt; ///////////////////////////////////&lt;br&gt; class CDispatcher&lt;br&gt; {&lt;br&gt; private:&lt;br&gt; class CSubscriberItem&lt;br&gt; {&lt;br&gt; public:&lt;br&gt; CSubscriberItem(CSubscriberPtr pSubscriber)&lt;br&gt; :m_pSubscriber(pSubscriber)&lt;br&gt; {&lt;br&gt; }&lt;br&gt; ~CSubscriberItem()&lt;br&gt; {&lt;br&gt; m_pSubscriber-&amp;gt;UnsubscribeHandler();&lt;br&gt; };&lt;br&gt; CSubscriberPtr Subscriber()const {return m_pSubscriber;}&lt;br&gt; private:&lt;br&gt; CSubscriberPtr m_pSubscriber;&lt;br&gt; };&lt;br&gt; typedef boost::shared_ptr&lt;CSubscriberItem&amp;gt; CSubscriberItemPtr;&lt;br&gt; typedef std::vector&lt;CSubscriberItemPtr&amp;gt; CSubscriberList;&lt;br&gt; typedef boost::shared_ptr&lt;CSubscriberList&amp;gt; CSubscriberListPtr;&lt;br&gt; public:&lt;br&gt; CDispatcher()&lt;br&gt; {&lt;br&gt; }&lt;br&gt; private:&lt;br&gt; CDispatcher(const CDispatcher&amp;amp;){}&lt;br&gt; CDispatcher&amp;amp; operator=(const CDispatcher&amp;amp;){return *this;}&lt;br&gt; public:&lt;br&gt; SubscriberId Subscribe(CSubscriberPtr pNewSubscriber)&lt;br&gt; {&lt;br&gt; //Declaration of the next shared pointer before ScopeLocker&lt;br&gt; //prevents release of subscribers from under lock&lt;br&gt; CSubscriberListPtr pNewSubscriberList(new CSubscriberList());&lt;br&gt; //Enter to locked section&lt;br&gt; CScopeLocker ScopeLocker(m_Lock);&lt;br&gt; if(m_pSubscriberList)&lt;br&gt; {&lt;br&gt; //Copy existing subscribers&lt;br&gt; pNewSubscriberList-&amp;gt;assign(m_pSubscriberList-&amp;gt;begin(), m_pSubscriberList-&amp;gt;end());&lt;br&gt; }&lt;br&gt; for(size_t i = 0; i &lt; pNewSubscriberList-&amp;gt;size(); ++i)&lt;br&gt; {&lt;br&gt; CSubscriberItemPtr pSubscriberItem = (*pNewSubscriberList)[i];&lt;br&gt; if(pSubscriberItem-&amp;gt;Subscriber()-&amp;gt;GetSubscriberId() == pNewSubscriber-&amp;gt;GetSubscriberId())&lt;br&gt; {&lt;br&gt; return 0;&lt;br&gt; }&lt;br&gt; }&lt;br&gt; //Add new subscriber to new subscriber list&lt;br&gt; pNewSubscriberList-&amp;gt;push_back(CSubscriberItemPtr(new CSubscriberItem(pNewSubscriber)));&lt;br&gt; //Exchange subscriber lists&lt;br&gt; m_pSubscriberList = pNewSubscriberList;&lt;br&gt; return pNewSubscriber-&amp;gt;GetSubscriberId();&lt;br&gt; }&lt;br&gt; bool Unsubscribe(SubscriberId id)&lt;br&gt; {&lt;br&gt; //Declaration of the next shared pointers before ScopeLocker&lt;br&gt; //prevents release of subscribers from under lock&lt;br&gt; CSubscriberItemPtr pSubscriberItemToRelease;&lt;br&gt; CSubscriberListPtr pNewSubscriberList;&lt;br&gt; //Enter to locked section&lt;br&gt; CScopeLocker ScopeLocker(m_Lock);&lt;br&gt; if(!m_pSubscriberList)&lt;br&gt; {&lt;br&gt; //No subscribers&lt;br&gt; return false;&lt;br&gt; }&lt;br&gt; pNewSubscriberList = CSubscriberListPtr(new CSubscriberList());&lt;br&gt; for(size_t i = 0; i &lt; m_pSubscriberList-&amp;gt;size(); ++i)&lt;br&gt; {&lt;br&gt; CSubscriberItemPtr pSubscriberItem = (*m_pSubscriberList)[i];&lt;br&gt; if(pSubscriberItem-&amp;gt;Subscriber()-&amp;gt;GetSubscriberId() == id)&lt;br&gt; {&lt;br&gt; pSubscriberItemToRelease = pSubscriberItem;&lt;br&gt; }&lt;br&gt; else&lt;br&gt; {&lt;br&gt; pNewSubscriberList-&amp;gt;push_back(pSubscriberItem);&lt;br&gt; }&lt;br&gt; }&lt;br&gt; //Exchange subscriber lists&lt;br&gt; m_pSubscriberList = pNewSubscriberList;&lt;br&gt; if(!pSubscriberItemToRelease.get())&lt;br&gt; {&lt;br&gt; return false;&lt;br&gt; }&lt;br&gt; return true;&lt;br&gt; }&lt;br&gt; void SendMessage(void* pContext)&lt;br&gt; {&lt;br&gt; CSubscriberListPtr pSubscriberList;&lt;br&gt; {&lt;br&gt; CScopeLocker ScopeLocker(m_Lock);&lt;br&gt; if(!m_pSubscriberList)&lt;br&gt; {&lt;br&gt; //No subscribers&lt;br&gt; return;&lt;br&gt; }&lt;br&gt; //Get shared pointer to an existing list of subscribers&lt;br&gt; pSubscriberList = m_pSubscriberList;&lt;br&gt; }&lt;br&gt; //pSubscriberList pointer to copy of subscribers&apos; list&lt;br&gt; for(size_t i = 0; i &lt; pSubscriberList-&amp;gt;size(); ++i)&lt;br&gt; {&lt;br&gt; (*pSubscriberList)[i]-&amp;gt;Subscriber()-&amp;gt;MessageHandler(pContext);&lt;br&gt; }&lt;br&gt; }&lt;br&gt; private:&lt;br&gt; CSubscriberListPtr m_pSubscriberList;&lt;br&gt; CLock m_Lock;&lt;br&gt; };&lt;br&gt;&lt;br&gt;}; //namespace Observer&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;code class=&quot;cpp&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Ссылки&lt;/h4&gt;
Библиотека boost::signals2 &lt;a href=&quot;http://www.rsdn.ru/article/cpp/signals2.xml&quot;&gt;статья&lt;/a&gt;&lt;br&gt;
Умные указатели &lt;a href=&quot;http://www.ozon.ru/context/detail/id/88891/&quot;&gt;Джефф Элджер&lt;/a&gt;&lt;br&gt;
Resource Acquisition Is Initialization (RAII) &lt;a href=&quot;http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization&quot;&gt;википедия&lt;/a&gt;&lt;br&gt;
Комментарии к первой версии этой статьи можно найти &lt;a href=&quot;http://rsdn.ru/forum/cpp/4053177.flat.aspx&quot;&gt;здесь&lt;/a&gt;</content:encoded>
			<link>https://cpplus.my1.ru/news/mnogopotochnyj_observer_na_s_praktika/2011-01-23-76</link>
			<category>Новости</category>
			<dc:creator>FazaNaka</dc:creator>
			<guid>https://cpplus.my1.ru/news/mnogopotochnyj_observer_na_s_praktika/2011-01-23-76</guid>
			<pubDate>Sun, 23 Jan 2011 06:35:25 GMT</pubDate>
		</item>
		<item>
			<title>Елена Сагалаева приняла участие в подкасте Радио Т</title>
			<description>На подкасте &lt;a href=&quot;http://radio-t.com/podcasts/radio-t-222/&quot;&gt;Радио Т номер 222&lt;/a&gt; поговорили о языках программирования вообще и о C++ в частности. &lt;br&gt;Размышления на тему судьбы языка С++ стали особенно актуальны после недели ненависти к С++ на Хабрахабре.</description>
			<content:encoded>На подкасте &lt;a href=&quot;http://radio-t.com/podcasts/radio-t-222/&quot;&gt;Радио Т номер 222&lt;/a&gt; поговорили о языках программирования вообще и о C++ в частности. &lt;br&gt;Размышления на тему судьбы языка С++ стали особенно актуальны после недели ненависти к С++ на Хабрахабре.</content:encoded>
			<link>https://cpplus.my1.ru/news/elena_sagalaeva_prinjala_uchastie_v_podkaste_radio_t/2011-01-19-75</link>
			<category>Новости</category>
			<dc:creator>FazaNaka</dc:creator>
			<guid>https://cpplus.my1.ru/news/elena_sagalaeva_prinjala_uchastie_v_podkaste_radio_t/2011-01-19-75</guid>
			<pubDate>Wed, 19 Jan 2011 16:28:05 GMT</pubDate>
		</item>
		<item>
			<title>Еще 5++ причин ненавидеть С++</title>
			<description>Признаюсь Вам — я ненавижу язык С++. Он просто выводит меня из себя. &lt;br&gt;
&lt;br&gt;Когда я встречаю эти три символа в заголовках статей на любимых
ресурсах — еще терпимо, когда вижу в подопытном проекте файлы с
расширением .cc и .cpp, .hh и .hpp (и еще .hxx и .cxx, у-ух, тысяча
чертей!) — уже злюсь, а уж когда приходится читать, или, еще хуже,
писать на нем — да я почти что истекаю ядом!...</description>
			<content:encoded>Признаюсь Вам — я ненавижу язык С++. Он просто выводит меня из себя. &lt;br&gt;
&lt;br&gt;Когда я встречаю эти три символа в заголовках статей на любимых
ресурсах — еще терпимо, когда вижу в подопытном проекте файлы с
расширением .cc и .cpp, .hh и .hpp (и еще .hxx и .cxx, у-ух, тысяча
чертей!) — уже злюсь, а уж когда приходится читать, или, еще хуже,
писать на нем — да я почти что истекаю ядом!$CUT$&lt;br&gt;
&lt;br&gt;Что ж, попробую заронить и в Вас искру священной ненависти.&lt;br&gt;
&lt;h4&gt;0. С — это Вам не С с плюсами!&lt;/h4&gt;&amp;nbsp;
Ненавижу, когда код на чистом С, который сами Патриархи Керниган и
Ритчи признали бы созвучным их Светлому Языку, кощунственно обзывают
«С++» и бездумно заключают в файлы с одним из вышеуказанных нечестивых
расширений, и, что самое страшное, и компилируют тоже как С++. Зачем?&lt;br&gt;
&lt;h4&gt;1. С++ — это Вам не С с плюсами!&lt;/h4&gt;
Это более чем весомая причина для ненависти. Многие авторы пособий, те
самые отступники, кто продвигал язык в 90-е, утверждали, мол, что С++ —
это «улучшенный» С, что можно, якобы, почти безболезненно перейти от С
к С++!&lt;br&gt;
&lt;br&gt;
Какой удивительный бред! Куда можно улучшить язык, который можно
вручную транслировать в ассемблер и обратно (да, да, каюсь, ну
приукрасил слегка)? Но им, похоже, удалось убедить в этом
программирующие массы. В результате — не надо далеко ходить за
примерами кода, который никоим образом не использует ни ООП, не
нуждается в киллер-фичах С++, но от скудоумия ответственных лиц пишется
на нем — а в сухом остатке мегатонны кода, которые замечательно бы
смотрелись на суровом своей чистотой ANSI C.&lt;br&gt;
&lt;br&gt;
Не стоит и говорить, что в таком же стиле любят воспитывать будущих
программистов высшие учебные заведения. Форумы переполнены вопросами от
нерадивых студентов, которые используют С++ для сугубо процедурного
кода только из-за того, что в нем можно объявлять переменные в любом
месте блока, а не в начале. Как это Вы собираетесь высекать свои мысли
в граните кода, если не знаете на блок вперед, какие переменные Вам
потребуются? &lt;br&gt;
&lt;br&gt;
«Новые» возможности С++ разлагают неокрепшие умы юных программистов. &lt;br&gt;
По тлетворному влиянию С++ может поспорить с Delphi/VB в 90-х и PHP в 2000-х. &lt;br&gt;
&lt;br&gt;
Что же касается перехода — да, возможно, но отнюдь не безболезненно,
тем более когда кажется, что остаешься чуть ли не в рам ках того же
языка.&lt;br&gt;
&lt;br&gt;
Лично я вижу три пути обучения программированию: &lt;br&gt;
1) академический — от основ Computer Science — к функциональному, логическому и метапрограммированию;&lt;br&gt;
2) инженерный — от аппаратных основ и Ассемблера к С и высокоуровневым языкам, параллельно изучаются мат. основы;&lt;br&gt;
3) ремесленный (т.н. «быдлокодинг») — no comment;&lt;br&gt;
&lt;br&gt;
Даже профессионал в силу мифологичности человеческого мышления обычно
идет по списку снизу вверх, потрогав Бейсик/Паскаль/(Лого) в детстве, в
отрочестве дойдя до понимания алгоритмов, работы с памятью, железом и
средствами ОС, а в институте изучив, скажем, Лисп и Пролог, уже
получает все средства, чтобы проникнуться, наконец, малым Дао на своем
пути. &lt;br&gt;
&lt;br&gt;
Но вот прожить весь путь с одним языком?&lt;br&gt;
&lt;br&gt;&amp;nbsp;
Главная проблема С++ в том, что этот язык напрасно замахивается на
первую и третью область сразу, когда корни его растут из второй. Для
первой области надо заново собрать международный комитет и полностью
переделать язык с нуля (и здесь тоже могла бы висеть картинка с
троллейбусом из батона), для третьей — небрежности не терпит, и кривая
обучения будет крутовата. Причем, взобравшись на очередное плато,
ученик не поймет, насколько он высоко залез, и бросится пробовать свои
силы, думая, что это поможет ему взять очередную высоту, и т.д. до
посинения и преждевременной смерти под кучей собственного кода.&lt;br&gt;
&lt;h4&gt;2. Вы все еще знаете, что под капотом у Вашего компилятора С++? Тогда мы идем к Вам!&lt;/h4&gt;
Язык С обязан был своей всемирной популярностью двум основным факторам: &lt;br&gt;
1) идеологическая и синтаксическая целостность;&lt;br&gt;
2) прямая связь с низкоуровневым программированием;&lt;br&gt;
&lt;br&gt;
Если Вы видели или даже трогали формальное описание грамматики языка С,
то Вы понимаете, о чем я. Если Вы видели ассемблерные листинги своих и
чужих программ на С, хоть при трансляции, хоть в дизассемблере и
отладчике, то Вы понимаете, о чем я. Если Вы, черт побери, прочитали
документацию к Вашему компилятору и компоновщику и способны хоть иногда
угадывать, какие оптимизации применяются к Вашему коду и как он лежит
внутри исполняемых модулей — Вы на коне! А не, извините, под ним. &lt;br&gt;
&lt;br&gt;
Шаблоны(!), позднее связывание, RTTI… Все это потребовало новый уровень
абстракции, еще больше отдалило код «на бумаге» от кода «в железе». Да
на счету одного только безобидного name mangling миллиарды сожженных
нейронов и тонны сточенного о клавиатуру эпителия!&lt;br&gt;
&lt;br&gt;
Отладка, реверсинг и ручная оптимизация «приплюснутого» кода требуют
стойкости в истинной вере и прямых рук, но где, где их найти, когда
юных «плюсовиков» развращают с малолетства? &lt;br&gt;
&lt;br&gt;
В результате готовы «постигать» С++ люди, которые не знают, в какую
сторону растет стек на их машине. В стародавние времена, когда
правоверные программисты на С были в почете, мне рассказывали, таких
казнили еще во младенчестве при помощи шнура от клавиатуры, а те, кто
выжил, либо укреплялись в вере, либо с позором изгонялись к
быдлокодерам, но и в изгнании сияли они отраженной искрой истинного
света.&lt;br&gt;
&lt;br&gt;А С++-то быдлокодинга совсем не терпит, но как манит, манит его адептов!&lt;br&gt;
&lt;h4&gt;3. Парадигма повержена? Да здравствует парадигма!&lt;/h4&gt;
То, что данный язык в принципе так позволяет себя расширять и до сих пор скорее жив, весьма достойно уважения. &lt;br&gt;
&lt;br&gt;
Но сама идея того, что можно из императивного языка сделать нестрогое
подобие объектно-ориентированного, а затем сторонними библиотеками
придать черты функционального, а потом использовать «это» как
мультипарадигменный язык, порочна и б-гомерзка. А как реализуются эти
нестройные расширения? Заглушки, и снаружи, и внутри. STL и его
собратья хоть и придают языку (и среде) необходимую «мощь», но никак не
целостность, и неотъемлемые теперь уже свойства языка реализованы и
оформлены совершенно различными способами. Тогда уж можно и на голом С
с препроцессором накрутить что угодно, что успешно и делается.&lt;br&gt;
&lt;br&gt;Кощунственно даже сравнивать С++ с метаязыками — его сила в гибкости, а не в изменчивости.&lt;br&gt;
&lt;h4&gt;4. Синтаксис С++ убог&lt;/h4&gt;
Все синтаксические изыски С++, по сравнению с С, меня лично всегда
заставляли мучительно подозревать за ними наличие маниакального набора
директив препроцессора. Хотя, времена, когда так и было (но очень
недолго, когда код С++ перед компиляцей преобразовывался в С),
безвозвратно прошли еще до распада СССР. &lt;br&gt;
&lt;br&gt;
Посмотрим правде в глаза — то, как синтаксис С++ выражает его
идеологию, сегодня вызывает ужас. Чтобы сделать код С++ читаемым, уже
недостаточно прямых рук автора, нужна подсветка синтаксиса! А чтобы
прочитать код, использующий STL или boost — буйство угловых скобок (про
стандарт C++0x лучше умолчим), удвоенных двоеточий и преисполненных
смысла слов — придется пожертвовать частью мозга, которая, при
использовании более разборчивых в друзьях языков, могла бы заняться и
более полезным делом. &lt;br&gt;
&lt;br&gt;Если бы С++ с самого начала родился бы без буквы С в своем названии, возможно, претензий к синтаксису ни у кого бы и не было.&lt;br&gt;
&lt;h4&gt;5. Хотите ООП — так возьмите его живым!&lt;/h4&gt;
А не довольствуйтесь метафизическим сношением с подобным Франкенштейном. &lt;br&gt;
&lt;br&gt;
No, srsly. Вы вчера-сегодня выбрали С++, именно чтобы снимать сливки с
использования ООП в своем проекте? ООП — это круто, Вы хотите найти
идеальный баланс между проектированием, кодированием и (о, б-же!)
сопровождением, начитались статей про модные в 90-х концепции?
Передумайте, пока не поздно. На дворе не 95-й год, когда это еще
прокатывало. &lt;br&gt;
&lt;br&gt;
Есть, есть ниша, где С++ идеален и замены ему пока нет — это функционально богатые, &lt;em&gt;монолитные&lt;/em&gt; проекты, требующие наивысшей производительности (и в которых ну совершенно &lt;em&gt;нет места быдлокодингу&lt;/em&gt;).&lt;br&gt;
&lt;br&gt;
Это топовые игры, браузеры, офисные и графические пакеты, некоторые
IDE. Там, где нельзя обойтись простыми интерфейсами между модулями
системы, где приходится постоянно работать не с сырыми данными, а
относительно сложными, нафаршированными логикой, структурированными
объектами, там, где можно &lt;em&gt;выделить&lt;/em&gt; ядро, требующее максимума производительности, но нельзя без потерь &lt;em&gt;отделить&lt;/em&gt; его от остальной системы. &lt;br&gt;
&lt;br&gt;
Но С++ уже не место в мэйнстриме!&lt;br&gt;
&lt;br&gt;&amp;nbsp;
Есть, к примеру, замечательный язык Java, который обладает и ясным
синтаксисом, и всеми необходимыми возможностями, да еще и транслируется
в байткод удобоваримого формата. Есть не менее замечательный язык C#,
который не отвечает за грехи своего предка, да и намного толерантнее,
чем С++, к небрежному коду. А для промышленного языка это более чем
критично.&amp;nbsp;&lt;br&gt;
&lt;br&gt;
С++ же обладает воистину удивительной особенностью — программист может
обходиться процедурным по сути кодом, но он вынужден выбирать С++
вместо С или любого другого языка, только чтобы использовать элементы
существующих industry-standard ООП-решений — например, обращаться через
С++ — интерфейсы к стороннему коду и библиотекам. &lt;br&gt;
&lt;br&gt;
За примерами далеко ходить не надо — в стеке технологий из Редмонда — DirectX и ATL/MFC. &lt;s&gt;Доколе?&lt;/s&gt;
Зачем? OpenGL и голое WinAPI отлично обходятся без плюсовых «наворотов»
(Я умолчу об именах серьезных проектов, которые используют С, включая
ядра всех популярных ОС и драйвера для них, популярные СУБД, полные
тулчейны для множества языков, графические оболочки и даже, Вы не
поверите, прикладное ПО!). &lt;br&gt;
&lt;br&gt;
Нет, действительно достойные ООП-библиотеки и фреймворки существуют,
взять тот же Qt, но в целом приятного мало — «ООП ради ООП» торжествует
среди библиотек и превращается в «С++ ради С++» при выборе технологии
для проекта, а хотелось бы думать, что мы — не те школьники, которых
насильно обучили Delphi, потому что на нем окошки рисовать проще, или
потому, что окошки можно рисовать только на нем.&lt;br&gt;
&lt;br&gt;&amp;nbsp;
Все эти годы, когда С++ доминировал на рынке, получается так, что он не
выбирался низами, а насаждался верхами, и не всегда оправданно.&lt;br&gt;
&lt;h4&gt;Вместо заключения&lt;/h4&gt;
Я встречал мнение, что сила С++ — в сочетании его гибкости и нестрогого
его соответствия канонам ООП, по сравнению с той же Java. Мол, не
хотите — пишите вроде бы как бы и на С, а где надо — «раззудись плечо»
и т.д. &lt;br&gt;
&lt;br&gt;
Не могу полностью согласиться с таким аргументом при выборе языка —
если действительно позволяет архитектура решения и действительно
получилась &lt;em&gt;гетерогенная система&lt;/em&gt;
— так и пусть в каждом модуле будет использоваться наиболее подходящий
язык и технологии, и будет поощряться хоть все что душе угодно, кроме
размножения сущностей и ковровых бомбардировок велосипедами по воробьям!&lt;br&gt;
&lt;br&gt;
То, что создатели С++ уже 20 с лишним лет пытаются отвечать желанию
трудящихся усидеть на трех стульях сразу и накрыв одним языком &lt;em&gt;всю&lt;/em&gt;&amp;nbsp;
предметную область, может быть поводом для уважения, но никак не должно
влиять на выбор идеального инструмента для решения конкретной задачи.&lt;br&gt;
&lt;br&gt;
Прошу не принимать данную заметку близко к сердцу, искать в ней личные
оскорбления или требовать сатисфакции — отнюдь не это было моей целью.&lt;br&gt;
&lt;br&gt;
Я всего лишь поделился с Вами тем, что тревожит меня при использовании
данного языка и продуктов, так или иначе с ним связанных. &lt;br&gt;
&lt;br&gt;
Если в наших с Вами силах своим выбором направлять движение всей отрасли, то пусть нами руководит разум, а не привычки и эмоции.</content:encoded>
			<link>https://cpplus.my1.ru/news/eshhe_5_prichin_nenavidet_s/2011-01-19-74</link>
			<category>Новости</category>
			<dc:creator>FazaNaka</dc:creator>
			<guid>https://cpplus.my1.ru/news/eshhe_5_prichin_nenavidet_s/2011-01-19-74</guid>
			<pubDate>Wed, 19 Jan 2011 08:52:54 GMT</pubDate>
		</item>
		<item>
			<title>Язык Mt: C для высоконагруженных серверов</title>
			<description>Я хочу предложить к обсуждению идеи о том, как упростить написание
серверных программ на C введением дополнительных языковых средств.
Полагаю, что эта тема может быть интересна всем разработчикам, которым
приходилось иметь дело с написанием многопоточного или асинхронного
кода.&lt;br&gt;
&lt;br&gt;&amp;nbsp;
На данный момент я практически завершил написание инструментария —
генератора парсеров, парсера C и частично C++, — который позволяет
приступить к написанию транслятора, поддерживающего языковые
расширения, о которых я здесь расскажу. Но перед тем, как продолжить
работу, хотелось бы посоветоваться с коллегами по цеху и найти
единомышленников....</description>
			<content:encoded>Я хочу предложить к обсуждению идеи о том, как упростить написание
серверных программ на C введением дополнительных языковых средств.
Полагаю, что эта тема может быть интересна всем разработчикам, которым
приходилось иметь дело с написанием многопоточного или асинхронного
кода.&lt;br&gt;
&lt;br&gt;&amp;nbsp;
На данный момент я практически завершил написание инструментария —
генератора парсеров, парсера C и частично C++, — который позволяет
приступить к написанию транслятора, поддерживающего языковые
расширения, о которых я здесь расскажу. Но перед тем, как продолжить
работу, хотелось бы посоветоваться с коллегами по цеху и найти
единомышленников.$CUT$&lt;br&gt;
&lt;br&gt;
Речь пойдёт о многотопоточных асинхронных серверах. Асинхронных — то
есть использующих логику обслуживания клиентов, основанную на событиях
(epoll/kqueue), при которой одним потоком одновременно обслуживается
множество клиентов. Многопоточных — значит готовых использовать
преимущества современных многоядерных процессоров, выполняя
параллельное обслуживание соединений несколькими потоками в рамках
одного процесса.&lt;br&gt;
&lt;br&gt;
Асинхронная модель обслуживания позволяет создавать серверы, наиболее
подготовленные к работе с по-настоящему высокой нагрузкой. Но
асинхронный (неблокирующийся) код писать сложнее, чем синхронный,
блокирующийся. Кроме того, существенно сложнее писать асинхронный
многопоточный код: слишком легко ошибиться при синхронизации доступа к
совместно используемым данным, и слишком сложно такие ошибки выявлять.
Это приводит к тому, что на практике зачастую разумнее полностью
отказываются от поддержки многопоточности.&lt;br&gt;
&lt;br&gt;
Накопив определённый опыт написания таких программ, я стал замечать,
что решение основных проблем синхронизации потоков решается типовыми
способами, применение которых можно было бы автоматизировать на уровне
языка, существенно облегчив задачу разработчику. Затем я увидел, что,
используя схожий подход, можно упростить написание кода асинхронной
обработки событий. Именно из-за видимой общности решений я предлагаю
единый набор языковых дополнений для этих не связанных напрямую задач.&lt;br&gt;
&lt;br&gt;
Идеи довольно простые, поэтому, чтобы не создавать ощущения сложности,
перечислю основные тезисы по дополнениям к C. Эти дополнения я буду
называть «языком Mt».&lt;br&gt;
&lt;br&gt;
Итак, Mt — это:&lt;ul&gt;
&lt;li&gt;Автоматическое управление мьютексами для синхронизации доступа к объектам;&lt;/li&gt;
&lt;li&gt;Асинхронные цепочки обработки, записываемые как обычные последовательные функции;&lt;/li&gt;
&lt;li&gt;Аннотирование — дополнительное описания элементов программы для статического анализа исходных текстов;&lt;/li&gt;
&lt;li&gt;Встроенный механизм подсчёта ссылок;&lt;/li&gt;
&lt;li&gt;Заимствование наиболее полезных возможностей из C++;&lt;/li&gt;
&lt;li&gt;Совместимость с C, жёсткая привязка к C ABI. Первоначальная реализация — транслятор из Mt в C.&lt;/li&gt;
&lt;/ul&gt;Поясню, почему в качестве базы выбран C, а не C++. Основная
причина — существенно более высокая сложность C++ с точки зрения
семантического анализа исходных текстов. На протяжении последнего года
я предпринял попытку написать парсер C++ и систему статического анализа
C++-кода. Результат — парсер C и разбор семантики в стиле C дались
довольно легко, а разбор C++ далёк от завершения: объём работы слишком
велик. Поэтому я решил, что можно добиться хороших практических
результатов, опираясь на чистый C и дополнив его теми возможностями
C++, которые достаточно просты в реализации.&lt;br&gt;
&lt;br&gt;
Далее расскажу о каждом дополнении подробнее. Некоторые примеры кода
привожу на C++, потому что так их легче соотносить с эквивалентным
кодом на Mt.&lt;br&gt;
&lt;br&gt;
&lt;h4&gt;1. Автоматическое управление мьютексами&lt;/h4&gt;&lt;br&gt;
Рассмотрим распространённую схему обработки запросов. Пусть у нас есть
некоторый объект класса MyFrontend, представляющий собой реализацию
нашего сервера. После получения и декодирования запроса от клиента мы
вызываем метод серверного объекта processRequest(). Этот метод
выполняет некоторые операции, вызывающие изменение внутреннего
состояния сервера. Положим для примера, что в результате обработки
запроса увеличивается счётчик кол-ва запросов num_requests:&lt;br&gt;
&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;class MyFrontend&lt;br&gt; {&lt;br&gt; private:&lt;br&gt; Mutex state_mutex;&lt;br&gt; unsigned num_requests;&lt;br&gt;&lt;br&gt; public:&lt;br&gt; void processRequest ()&lt;br&gt; {&lt;br&gt; state_mutex.lock ();&lt;br&gt; ++ num_requests;&lt;br&gt; state_mutex.unlock ();&lt;br&gt; }&lt;br&gt; };&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;
Mt освобождает нас от необходимости ручной синхронизации:&lt;br&gt;
&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; async object MyFrontend&lt;br&gt; {&lt;br&gt; private:&lt;br&gt; unsigned num_requests;&lt;br&gt;&lt;br&gt; public:&lt;br&gt; async void processRequest ()&lt;br&gt; {&lt;br&gt; ++ num_requests;&lt;br&gt; }&lt;br&gt; };&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;
async object объявляет класс объектов, безопасных с точки зрения
многопоточности. Такие объекты будем называть асинхронными. Приведённый
пример кода на Mt транслируется в код, эквивалентный приведённому
примеру на C++.&lt;br&gt;
&lt;br&gt;
Теперь допустим, что для обработки запроса MyFrontend должен вызвать
метод doSomething() объекта MyBackend, передав ему в качестве параметра
счётчик обращений к бэкэнду num_back. Кроме того, мы не хотим
задумываться о возможности взаимоблокировок и хотим, чтобы всё работало
с нерекурсивными мьютексами. Для этого нам нужно освобождать
state_mutex перед обращением к MyBackend:&lt;br&gt;
&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;class MyFrontend&lt;br&gt; {&lt;br&gt; private:&lt;br&gt; Mutex state_mutex;&lt;br&gt; unsigned num_requests;&lt;br&gt; unsigned num_back;&lt;br&gt; MyBackend *backend;&lt;br&gt;&lt;br&gt; public:&lt;br&gt; void processRequest ()&lt;br&gt; {&lt;br&gt; state_mutex.lock ();&lt;br&gt; ++ num_back;&lt;br&gt; unsigned tmp_num_back = tmp_num_back;&lt;br&gt; state_mutex.unlock ();&lt;br&gt;&lt;br&gt; backend-&amp;gt;doSomething (tmp_num_back);&lt;br&gt;&lt;br&gt; state_mutex.lock ();&lt;br&gt; ++ num_requests;&lt;br&gt; state_mutex.unlock ();&lt;br&gt; }&lt;br&gt; };&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;
То же самое на Mt:&lt;br&gt;
&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; async object MyFrontend&lt;br&gt; {&lt;br&gt; private:&lt;br&gt; unsigned num_requests;&lt;br&gt; unsigned num_back;&lt;br&gt; MyBackend *backend;&lt;br&gt;&lt;br&gt; public:&lt;br&gt; async void processRequest ()&lt;br&gt; {&lt;br&gt; call backend-&amp;gt;doSomething (++ num_back);&lt;br&gt; ++ num_requests;&lt;br&gt; }&lt;br&gt; };&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;
Транслятор Mt самостоятельно вставляет операции со state_mutex и вводит
временную переменную для передачи параметра. Оператор call введён для
того, чтобы подчеркнуть, что на время вызова асинхронного метода
другого объекта мы освобождаем блокировку state_mutex. Кроме того, это
позволяет нам явно запретить вызов асинхронных методов в выражениях.&lt;br&gt;
&lt;br&gt;
Помимо приведённых примеров автоматизируется ряд других случаев
использования блокировок: ожидание событий (события обычно используются
в паре с мьютексом, защищающим условие завершения ожидания), вызов
асинхронного метода объекта из другого метода того же объекта.&lt;br&gt;
&lt;br&gt;
Таким образом, мы сводим к минимуму риск ошибиться при управлении
простой блокировкой на состояние объекта. По моим наблюдениям, если
синхронизация доступа к объекту осуществляется с использованием не
одной, а нескольких блокировок, то скорее всего имеет место неудачное
проектирование: следует или разделить объект на несколько
самостоятельных объектов, или слить блокировки в одну. Поэтому,
автоматизируя простейший случай с одним мьютексом, мы решаем большую
часть проблем синхронизации. Возможность выполнять синхронизацию в
ручном режиме и с использованием других примитивов при этом&lt;br&gt;
сохраняется. &lt;br&gt;
&lt;br&gt;
&lt;h4&gt;2. Асинхронные цепочки обработки&lt;/h4&gt;&lt;br&gt;
На мой взгляд, это наиболее интересная и необычная идея. Объясню на примере, иллюстрирующем работу реального сервиса.&lt;br&gt;
&lt;br&gt;
Допустим, наш сервер — это фронтэнд фотохостига. Мы хотим реализовать
отдачу клиентам HTML-страниц с фотографиями пользователей. На странице
отображается информация из анкеты владельца фотографии и сама
фотография. Фотографии могут смотреть только друзья владельцев фото.
Таким образом, для того, чтобы ответить на HTTP-запрос, нужно собрать
нужную информацию, опросив ряд внутренних сервисов.&lt;br&gt;
&lt;br&gt;
Алгоритм обработки запроса:&lt;ol&gt;
&lt;li&gt;Отправить серверу проверки авторизации cookie пользователя, чтобы идентифицировать его (check_auth);&lt;/li&gt;
&lt;li&gt;После проверки авторизации отправить серверу, обслуживающему
социальный граф, запрос на проверку, являются ли пользователи друзьями
(check_friends);&lt;/li&gt;
&lt;li&gt;Отправить в хранилище анкетных данных запрос на владельца фотографии (get_userinfo);&lt;/li&gt;
&lt;li&gt;После получения ответов от check_friends и get_userinfo сформировать HTML-страницу и отправить её клиенту.&lt;/li&gt;
&lt;/ol&gt;Операция 2 может быть выполнена только после получения ответа на
запрос 1. Запрос 3 не зависит от запросов 1 и 2 и мы хотим, чтобы он
выполнялся параллельно с ними.&lt;br&gt;
&lt;br&gt;
Сделаем набросок реализации на C. В этот пример можно не вчитываться.
Его задача — показать, насколько громоздка ручная реализация даже такой
несложной асинхронной цепочки обработки:&lt;br&gt;
&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;// Структура состояния асинхронной цепочки.&lt;br&gt; typedef struct {&lt;br&gt; // &quot;Родительский&quot; класс, управляющий счётчиком ссылок.&lt;br&gt; MReferenced parent_referenced;&lt;br&gt; // Блокировка на доступ к элементам состояния.&lt;br&gt; MMutex state_mutex;&lt;br&gt;&lt;br&gt; uint64_t owner_id;&lt;br&gt; uint32_t photo_id;&lt;br&gt;&lt;br&gt; // Если &apos;true&apos;, то прерываем цепочку.&lt;br&gt; bool cancel;&lt;br&gt;&lt;br&gt; // Получили ответ от get_userinfo?&lt;br&gt; bool got_userinfo;&lt;br&gt; // Карма владельца фото (анкетные данные).&lt;br&gt; uint32_t owner_carma;&lt;br&gt;&lt;br&gt; // Получили ответ от check_friends?&lt;br&gt; bool got_friends;&lt;br&gt; } HttpPhotoData;&lt;br&gt;&lt;br&gt; static void http_photo_data_init (HttpPhotoData *data)&lt;br&gt; {&lt;br&gt; m_referenced_init ((MReferenced*) data);&lt;br&gt; m_mutex_init (&amp;amp;data-&amp;gt;state_mutex);&lt;br&gt;&lt;br&gt; data-&amp;gt;cancel = false;&lt;br&gt; data-&amp;gt;got_userinfo = false;&lt;br&gt; data-&amp;gt;got_friends = false;&lt;br&gt; }&lt;br&gt;&lt;br&gt; static void http_photo__get_userinfo_ret (uin32_t owner_carma,&lt;br&gt; void *_data);&lt;br&gt;&lt;br&gt; static void http_photo__check_auth_ret (uint64_t client_id,&lt;br&gt; void *_data);&lt;br&gt;&lt;br&gt; static void http_photo__end (HttpPhotoData *data);&lt;br&gt;&lt;br&gt; // Точка входа в асинхронную цепочку.&lt;br&gt; void http_photo (char *cookie_str,&lt;br&gt; uint64_t owner_id,&lt;br&gt; uint32_t photo_id)&lt;br&gt; {&lt;br&gt; HttpPhotoData *data = m_malloc (sizeof (HttpPhotoData));&lt;br&gt; http_photo_data_init (data);&lt;br&gt;&lt;br&gt; data-&amp;gt;owner_id = owner_id;&lt;br&gt; data-&amp;gt;photo_id = photo_id;&lt;br&gt;&lt;br&gt; {&lt;br&gt; m_referenced_ref ((MReferenced*) data);&lt;br&gt;&lt;br&gt; MCallbackDesc cb;&lt;br&gt; m_callback_desc_init_refdata (&amp;amp;cb, http_photod__get_userinfo_ret, data);&lt;br&gt;&lt;br&gt; get_userinfo (&amp;amp;cb, owner_id);&lt;br&gt; }&lt;br&gt;&lt;br&gt; {&lt;br&gt; m_referenced_ref ((MReferenced*) data);&lt;br&gt;&lt;br&gt; MCallbackDesc cb;&lt;br&gt; m_callback_desc_init_refdata (&amp;amp;cb, http_photod__check_auth_ret, data);&lt;br&gt;&lt;br&gt; check_auth (&amp;amp;cb, cookie_str);&lt;br&gt; }&lt;br&gt;&lt;br&gt; m_referenced_unref ((MReferenced*) data);&lt;br&gt; }&lt;br&gt;&lt;br&gt; // Ответ от get_userinfo.&lt;br&gt; static void http_photo__get_userinfo_ret (uint32_t owner_carma,&lt;br&gt; void *_data)&lt;br&gt; {&lt;br&gt; HttpPhotoData *data = (HttpPhotoData*) _data;&lt;br&gt;&lt;br&gt; m_mutex_lock (&amp;amp;data-&amp;gt;state_mutex);&lt;br&gt;&lt;br&gt; if (data-&amp;gt;cancel) {&lt;br&gt; m_mutex_unlock (&amp;amp;data-&amp;gt;state_mutex);&lt;br&gt; return;&lt;br&gt; }&lt;br&gt;&lt;br&gt; data-&amp;gt;owner_carma = owner_carma;&lt;br&gt; data-&amp;gt;got_userinfo = true;&lt;br&gt;&lt;br&gt; if (data-&amp;gt;got_friends) {&lt;br&gt; // Все данные для ответа собраны, отвечаем клиенту.&lt;br&gt; http_photo_end (data);&lt;br&gt; }&lt;br&gt;&lt;br&gt; m_mutex_unlock (&amp;amp;data-&amp;gt;state_mutex);&lt;br&gt; }&lt;br&gt;&lt;br&gt; // Ответ от check_auth.&lt;br&gt; static void http_photo__check_auth_ret (uint64_t client_id,&lt;br&gt; void *_data)&lt;br&gt; {&lt;br&gt; HttpPhotoData *data = (HttpPhotoData*) _data;&lt;br&gt;&lt;br&gt; m_referenced_ref ((MReferenced*) data);&lt;br&gt;&lt;br&gt; MCallbackDesc cb;&lt;br&gt; m_callback_desc_init_refdata (&amp;amp;cb, http_photo__check_friends_ret, data);&lt;br&gt;&lt;br&gt; check_friends (&amp;amp;cb, client_id, data-&amp;gt;owner_id);&lt;br&gt; }&lt;br&gt;&lt;br&gt; // Ответ от check_friends.&lt;br&gt; static void http_photo__check_friends_ret (bool friends,&lt;br&gt; void *_data)&lt;br&gt; {&lt;br&gt; HttpPhotoData *data = (HttpPhotoData*) _data;&lt;br&gt;&lt;br&gt; m_mutex_lock (&amp;amp;data-&amp;gt;state_mutex);&lt;br&gt; if (data-&amp;gt;cancel) {&lt;br&gt; m_mutex_unlock (&amp;amp;data-&amp;gt;state_mutex);&lt;br&gt; return;&lt;br&gt; }&lt;br&gt;&lt;br&gt; data-&amp;gt;got_friends = true;&lt;br&gt;&lt;br&gt; if (!friends) {&lt;br&gt; // Отказано в доступе. Прерываем цепочку.&lt;br&gt; data-&amp;gt;cancel = true;&lt;br&gt; m_mutex_unlock (&amp;amp;data-&amp;gt;state_mutex);&lt;br&gt; return;&lt;br&gt; }&lt;br&gt;&lt;br&gt; if (data-&amp;gt;got_userinfo) {&lt;br&gt; // Все данные для ответа собраны, отвечаем клиенту.&lt;br&gt; http_photo_end (data);&lt;br&gt; }&lt;br&gt;&lt;br&gt; m_mutex_unlock (data-&amp;gt;state_mutex);&lt;br&gt; }&lt;br&gt;&lt;br&gt; // Отправка ответа клиенту.&lt;br&gt; static void http_photo_end (HttpPhotoData *data)&lt;br&gt; {&lt;br&gt; photo_send_page (data-&amp;gt;owner_id, data-&amp;gt;photo_id, data-&amp;gt;owner_carma);&lt;br&gt; }&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;Получившийся код сложен, его трудно читать. Большая часть времени при
его написании была потрачена на рутинное обеспечение нужного порядка
выполнения операций. &lt;br&gt;
&lt;br&gt;
Эквивалентная реализация на Mt:&lt;br&gt;
&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; async chain void http_photo (char *cookie_str,&lt;br&gt; uint64_t owner_id,&lt;br&gt; uint32_t photo_id)&lt;br&gt; {&lt;br&gt; async call GetUserinfoCall get_userinfo (owner_id);&lt;br&gt;&lt;br&gt; async call check_auth (cookie);&lt;br&gt; gives uint64_t client_id;&lt;br&gt;&lt;br&gt; async call check_friends (client_id, owner_id)&lt;br&gt; gives bool friends;&lt;br&gt;&lt;br&gt; if (!friends) {&lt;br&gt; // Доступ запрещён&lt;br&gt; return;&lt;br&gt; }&lt;br&gt;&lt;br&gt; GetUserinfoCall gives uint32_t owner_carma;&lt;br&gt;&lt;br&gt; // Отправляем HTTP-ответ со страницей фото.&lt;br&gt; photo_send_page (owner_id, photo_id, owner_carma);&lt;br&gt; }&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;
Обратите внимание, что мы практически полностью освободились от
рутинной работы по организации нужного порядка исполнения. Кроме того,
сгенерированный C-код можно будет использовать в многопоточных
программах.&lt;br&gt;
&lt;br&gt;
В приведённом примере использованы два новых оператора:&lt;ul&gt;
&lt;li&gt;Оператор async call описывает асинхронный вызов. Функции, которые
можно использовать с этим оператором, должны иметь особую сигнатуру:
первый параметр — callback, который будет вызван по завершении вызова.
Callback завершения должен обязательно быть вызван при любом исходе,
будь то успешное выполнение вызова, внутренняя ошибка или таймаут.&lt;/li&gt;
&lt;li&gt;Оператор gives задаёт точку, в которой нужно дождаться завершения
указанного асинхронного вызова. gives вводит в область видимости список
возвращаемых вызовом значений. Если операторы async call и gives должны
быть разнесены, то в async call вызов именуется и в gives указывается
имя вызова (см. GetUserinfoCall в приведённом примере).&lt;/li&gt;
&lt;/ul&gt;Кроме продемонстрированных операторов, предусматривается
возможность ранней обработки результатов асинхронных вызовов, чтобы
можно было прервать цепочку при возникновении ошибки до того, как мы
дойдём до соответствующего неудавшемуся вызову оператора gives. Также
можно задавать дополнительный деструктор для данных, входящих в
структуру состояния цепочки.&lt;br&gt;
&lt;br&gt;
Основная работа, которую выполняет Mt при трансляции асинхронных цепочек в C, заключается в следующем:&lt;ul&gt;
&lt;li&gt;Определяет набор переменных, которые нужно хранить в структуре состояния цепочки (HttpPhotoData);&lt;/li&gt;
&lt;li&gt;Дробит функцию на более мелкие. Точками разрыва являются операторы async call;&lt;/li&gt;
&lt;li&gt;Обеспечивает нужный порядок выполнения, преобразуя операторы C,
нарушенные операторами async call и gives, в эквивалентные асинхронные
конструкции по фиксированным правилам.&lt;/li&gt;
&lt;/ul&gt;Можно показать, что каждая из этих операций хорошо поддаётся автоматизации.&lt;br&gt;
&lt;br&gt;
Параллельное выполнение сложных цепочек операций, непредставимых одной
функцией в Mt, можно реализовать, использовав несколько разных
асинхронных цепочек. То есть если ветвления в цепочке так сложны, что
их не удаётся описать одной функцией, то можно построить описание как
композицию нескольких функций.&lt;br&gt;
&lt;br&gt;
С помощью Mt мы можем описывать циклы и условные переходы, разрываемые
ожиданием ответов от асинхронных вызовов, в такой же простой и
привычной форме, как при написании обычного кода. Допустим, мы хотим
поискать значение в трёх независимых кэшах поочерёдно:&lt;br&gt;
&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;async chain void search (int key)&lt;br&gt; {&lt;br&gt; for (int i = 0; i &lt; 3; i++) {&lt;br&gt; async call search_cache (key, i /* cache id */)&lt;br&gt; gives int value;&lt;br&gt;&lt;br&gt; if (value &amp;gt; 0) {&lt;br&gt; printf (&quot;VALUE: %d&amp;#92;n&quot;, value);&lt;br&gt; break;&lt;br&gt; }&lt;br&gt; }&lt;br&gt; }&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;
Таким образом, для асинхронных цепочек обработки Mt может обеспечить
качественный скачок в выразительности и удобстве использования по
сравнению с C.&lt;br&gt;
&lt;br&gt;
&lt;h4&gt;3. Аннотирование&lt;/h4&gt;&lt;br&gt;
Аннотирование — это введение в код дополнительной вспомогательной
информации с помощью аннотирующих слов. Аннотирующие слова могут стоять
везде, где могут стоять квалификаторы const и volatile. Все
аннотирующие слова начинаются с символа &apos;$&apos;. Примеры:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt; int * $nonnull ptr; // указатель не может быть равен NULL&lt;br&gt; MyObject * $heap myobj; // объект должен быть выделен с помощью аллокатора&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;
Транслятор Mt является также основой для системы статической проверки
кода, которая использует аннотирование для дополнительных проверок.&lt;br&gt;
&lt;br&gt;
&lt;h4&gt;4. Заимствование возможностей C++&lt;/h4&gt;&lt;br&gt;
С точки зрения реализации транслятора, наиболее сложной особенностью
C++ по сравнению с C является зависимость семантического значения
языковых конструкций от типов данных аргументов. Это касается, в первую
очередь, правил перегрузки имён функций (включая перегрузку операторов)
и правил выбора шаблонов с учётом частичных специализаций. Такая
зависимость означает, что для того, чтобы определить, какая именно
функция или шаблон используется в варыжении, нужно вывести типы данных
всех аргументов функции или шаблона. Это, в свою очередь, требует
полной и точной реализации правил приведения типов. Всё это составляет
довольно значительный объём работы.&lt;br&gt;
&lt;br&gt;
В то же время, перегрузка имён — спорное свойство языка. Например, от неё отказались создатели языка Go, о чём есть &lt;a href=&quot;http://golang.org/doc/go_faq.html#overloading&quot;&gt;содержательная запись в FAQ&lt;/a&gt;.&lt;br&gt;
&lt;br&gt;
В Mt не будет перегрузки имён функций и перегрузки операторов,
множественного наследования, исключений, RTTI. Шаблоны без частичной
специализации реализовать не так сложно, и этого достаточно для того,
чтобы можно было создавать обобщённые контейнерные классы, поэтому
шаблоны целесообразно включить в язык. В отсутствие перегрузки имён
шаблоны станут гораздо более простым и лаконичным инструментом: таких
возможностей для метапрограммирования, как в C++, не будет.&lt;br&gt;
&lt;br&gt;
&lt;h4&gt;5. Ссылки и слабые ссылки&lt;/h4&gt;&lt;br&gt;
Отсутствие перегрузки операторов не даёт возможность реализовать в Mt
удобные аналоги «умных указателей», таких как std::auto_ptr и
boost::shared_ptr. Взамен на уровне языка предлагается механизм
подсчёта ссылок:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;int @a; // ссылка&lt;br&gt; int @@b; // слабая ссылка&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;
В базовом варианте используется простой подсчёт ссылок. Счётчики ссылок встроены&lt;br&gt;
в каждый синхронный или асинхронный объект Mt (object и async object).
При таком подходе к управлению ссылками программисту следует избегать
создания циклических ссылок, которые приводят к утечке памяти. Для
разрыва циклов используются слабые ссылки. В перспективе можно
предусмотреть возможность использования более сложного сборщика мусора,
но сейчас я не вижу в этом необходимости.&lt;br&gt;
&lt;br&gt;
В отсутствие перегрузки операторов нет необходимости поддерживать в Mt ссылки C++ (вида int &amp;amp;a).&lt;br&gt;
&lt;br&gt;
Отмечу, что реализация ссылок на уровне языка позволяет получить более
полный и удобный механизм, чем можно построить на шаблонах C++.&lt;br&gt;
&lt;br&gt;
&lt;h4&gt;Заключение&lt;/h4&gt;&lt;br&gt;
Я обозначил основные возможности, которые собираюсь включить в язык Mt.
Помимо них в проработке есть и другие идеи, направленные на упрощение
разработки и уменьшение ожидаемого количества ошибок в программах.&lt;br&gt;
&lt;br&gt;
Транслятор языка Mt — проект с открытым исходным кодом. В данный момент
он существует в виде парсера подмножества языка C++ и живёт в
svn-репозитории &lt;a href=&quot;https://mync.svn.sourceforge.net/svnroot/mync/trunk&quot;&gt;mync.svn.sourceforge.net/svnroot/mync/trunk&lt;/a&gt; под рабочим названием «scruffy».&lt;br&gt;
&lt;br&gt;
Если у вас есть мысли и идеи по добавлению или изменению определённых
свойств языков С/C++, то предлагаю их озвучить: возможно, пришло время
для их практической проверки.&lt;br&gt;
&lt;br&gt;
Приглашаю к участию в проекте. Если вы ищете тему для самостоятельной
работы, которая могла бы дать новые знания и опыт, а в случае успеха
превратиться в качественный и успешный продукт, то компилятор нового
языка может стать отличным выбором. Если же проблемы, на которые
нацелен Mt, для вас не очень актуальны, то, возможно, вас заинтересует
разработка парсера C++ и системы статического анализа программ на C/C++.&lt;br&gt;
&lt;br&gt;
Вместе мы сможем больше!&lt;br&gt;
&lt;br&gt;
Благодарю за внимание.</content:encoded>
			<link>https://cpplus.my1.ru/news/jazyk_mt_c_dlja_vysokonagruzhennykh_serverov/2011-01-17-73</link>
			<category>Новости</category>
			<dc:creator>FazaNaka</dc:creator>
			<guid>https://cpplus.my1.ru/news/jazyk_mt_c_dlja_vysokonagruzhennykh_serverov/2011-01-17-73</guid>
			<pubDate>Mon, 17 Jan 2011 08:44:53 GMT</pubDate>
		</item>
		<item>
			<title>Время выполнения операций в C++ под Linux</title>
			<description>Сегодня речь пойдёт об использовании clock_gettime в качестве секундомера для определения времени выполнения разных участков Вашего кода. Если Вы всё это уже знаете, или предпочитаете использовать gprof или другой профайлер, то смело можете пропускать данный топик, если же Вас это заинтересовало — добро пожаловать под хабракат....</description>
			<content:encoded>Сегодня речь пойдёт об использовании clock_gettime в качестве секундомера для определения времени выполнения разных участков Вашего кода. Если Вы всё это уже знаете, или предпочитаете использовать gprof или другой профайлер, то смело можете пропускать данный топик, если же Вас это заинтересовало — добро пожаловать под хабракат.$CUT$&lt;br&gt;&lt;br&gt;Возможно, многим из Вас эта тема будет не интересна в связи с тем, что Вы уже всё это давно знаете. А может просто предпочитаете профайлинг. Может Вы знаете более эффективный способ, и если так, то буду рад услышать Ваши комментарии.&lt;br&gt;&lt;br&gt;Однажды, во время написания очередного теста, понял что простого вывода времени этапа тестирования мне недостаточно и решил написать небольшой класс.&lt;br&gt;Начнём с теории.&lt;br&gt;&lt;strong&gt;&lt;br&gt;clock_gettime. Кто это и с чем его едят&lt;/strong&gt;&lt;br&gt;&lt;br&gt;Данная функция описана в &lt;time.h&amp;gt; и имеет следующий синтаксис:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;int clock_gettime(clockid_t clock_id, struct timespec *tp);&lt;/div&gt;&lt;/div&gt;&lt;br&gt;Функция изменяет значения полей в структуре tp, на текущие показатели часов. Каких часов, спросят наиболее искушённые? Тех чей тип был указан в clock_id.&lt;br&gt;По давней традиции функция возвращает 0 если выполнена успешно, и -1 если что-то привело к неудаче(соответственно в errno записывается код ошбки).&lt;br&gt;&lt;br&gt;Структура timespec состоит всего из 2х полей:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;struct timespec&lt;br&gt; {&lt;br&gt; __time_t tv_sec; /* Seconds. */&lt;br&gt; long int tv_nsec; /* Nanoseconds. */&lt;br&gt; };&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;в первое из которых записываются секунды, а во второе, как Вы могли уже догадаться из комментария, наносекунды.&lt;br&gt;&lt;br&gt;Из не разобранного остались лишь типы часов. Разберём и их. Описаны они в &lt;linux/time.h&amp;gt; и, думаю, могут у некоторых различаться:&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;#define CLOCK_REALTIME 0&lt;br&gt;#define CLOCK_MONOTONIC 1&lt;br&gt;#define CLOCK_PROCESS_CPUTIME_ID 2&lt;br&gt;#define CLOCK_THREAD_CPUTIME_ID 3&lt;br&gt;#define CLOCK_MONOTONIC_RAW 4&lt;br&gt;#define CLOCK_REALTIME_COARSE 5&lt;br&gt;#define CLOCK_MONOTONIC_COARSE 6&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;&lt;br&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;CLOCK_REALTIME&lt;/code&gt; — системные часы, со всеми их плюсами и &lt;br&gt; минусами. Могут быть переведены вперёд/назад, да и вставные секунды &lt;br&gt; иногда попадаются. Но иногда и их достаточно.&lt;/li&gt;&lt;li&gt;&lt;code&gt;CLOCK_MONOTONIC&lt;/code&gt; — сродни системным часам, но, в &lt;br&gt; отличии от них, представляют собой постоянно увеличивающийся счётчик, в &lt;br&gt; связи с чем, естественно, не могут быть переведены. Обычно равно &lt;br&gt; аптайму системы.&lt;/li&gt;&lt;li&gt;&lt;code&gt;CLOCK_PROCESS_CPUTIME_ID&lt;/code&gt; — возвращает время &lt;br&gt; затрачиваемое процессором относительно нашего приложения. В нашем &lt;br&gt; случае, используя эти часы мы получим время затраченое процессором на &lt;br&gt; работу с нашим приложением в независимости от других задач системы.&lt;/li&gt;&lt;li&gt;&lt;code&gt;CLOCK_THREAD_CPUTIME_ID&lt;/code&gt; — похоже на &lt;code&gt;CLOCK_PROCESS_CPUTIME_ID&lt;/code&gt; но более частный случай, так как следит за временем затрачиваемым на текущий поток.&lt;/li&gt;&lt;li&gt;&lt;code&gt;CLOCK_MONOTONIC_RAW&lt;/code&gt; — то же что и &lt;code&gt;CLOCK_MONOTONIC&lt;/code&gt;, но в отличии от первого не подвержен изменению через NTP.&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;Последние два я даже не смог протестировать ни на одной машине из доступных — везде они возвращали нули, но, по &lt;a href=&quot;http://lwn.net/Articles/347811/&quot;&gt;описанию автора,&lt;/a&gt; эти часы оперируют с тиками, и могут быть очень неточны в некоторых ситуациях, хотя в других дают беспрецедентную точность.&lt;br&gt; &lt;br&gt; Программная часть не несёт ничего гениального, а местами, и вовсе выглядит по индуски.&lt;br&gt;&lt;br&gt;&lt;strong&gt;Класс GetWorkTime&lt;/strong&gt;&lt;br&gt;&lt;br&gt;Для начала описал будущий класс. Думаю назначение переменных и и функций-членов понятны из названий.&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;GetWorkTime.h&lt;br&gt;&lt;br&gt;#include &lt;time.h&amp;gt;&lt;br&gt;&lt;br&gt;class GetWorkTime&lt;br&gt;{&lt;br&gt;private:&lt;br&gt; bool isCounterStarted;&lt;br&gt; bool isCounterWorkComplete;&lt;br&gt;&lt;br&gt; clockid_t clockID;&lt;br&gt; timespec startTime, endTime;&lt;br&gt;&lt;br&gt;public:&lt;br&gt; GetWorkTime(clockid_t clockID = CLOCK_REALTIME);&lt;br&gt; virtual ~GetWorkTime(){};&lt;br&gt;&lt;br&gt; void setClockID(clockid_t clockID);&lt;br&gt; void countingStart();&lt;br&gt; void countingEnd();&lt;br&gt;&lt;br&gt; unsigned long long getWorkTimeInNanoseconds();&lt;br&gt; unsigned long long getWorkTimeInSeconds();&lt;br&gt;};&lt;br&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;Реализация тоже не отличается гениальностью — всё ожидаемо.&lt;br&gt;&lt;br&gt;&lt;div class=&quot;bbCodeBlock&quot;&gt;&lt;div class=&quot;codeMessage&quot; style=&quot;border:1px inset;max-height:200px;overflow:auto;height:expression(this.scrollHeight&lt;5?this.style.height:scrollHeight&gt;200?&apos;200px&apos;:&apos;&apos;+(this.scrollHeight+5)+&apos;px&apos;);&quot;&gt;GetWorkTime.cpp&lt;br&gt;&lt;br&gt;#include &quot;GetWorkTime.h&quot;&lt;br&gt;&lt;br&gt;#include &lt;stdio.h&amp;gt;&lt;br&gt;#include &lt;unistd.h&amp;gt;&lt;br&gt;&lt;br&gt;GetWorkTime::GetWorkTime(clockid_t clockID)&lt;br&gt;{&lt;br&gt; isCounterStarted = false;&lt;br&gt; isCounterWorkComplete = false;&lt;br&gt;&lt;br&gt; setClockID(clockID);&lt;br&gt;}&lt;br&gt;&lt;br&gt;void GetWorkTime::setClockID(clockid_t clockID)&lt;br&gt;{&lt;br&gt; switch (clockID)&lt;br&gt; {&lt;br&gt;#ifdef _POSIX_MONOTONIC_CLOCK&lt;br&gt; case CLOCK_MONOTONIC:&lt;br&gt; case CLOCK_MONOTONIC_RAW:&lt;br&gt;#endif&lt;br&gt;#ifdef _POSIX_CPUTIME&lt;br&gt; case CLOCK_PROCESS_CPUTIME_ID:&lt;br&gt;#endif&lt;br&gt;#ifdef _POSIX_THREAD_CPUTIME&lt;br&gt; case CLOCK_THREAD_CPUTIME_ID:&lt;br&gt;#endif&lt;br&gt; case CLOCK_REALTIME_COARSE:&lt;br&gt; case CLOCK_MONOTONIC_COARSE:&lt;br&gt; this-&amp;gt;clockID = clockID;&lt;br&gt; break;&lt;br&gt;&lt;br&gt; default:&lt;br&gt; this-&amp;gt;clockID = CLOCK_REALTIME;&lt;br&gt; }&lt;br&gt;}&lt;br&gt;&lt;br&gt;void GetWorkTime::countingStart()&lt;br&gt;{&lt;br&gt; if (!isCounterStarted)&lt;br&gt; {&lt;br&gt; isCounterStarted = true;&lt;br&gt; isCounterWorkComplete = false;&lt;br&gt;&lt;br&gt; clock_gettime(clockID, &amp;amp;startTime);&lt;br&gt; }&lt;br&gt; else&lt;br&gt; perror(&quot;WARNING: Счётчик уже запущен&amp;#92;n&quot;);&lt;br&gt;}&lt;br&gt;&lt;br&gt;void GetWorkTime::countingEnd()&lt;br&gt;{&lt;br&gt; if (isCounterStarted)&lt;br&gt; {&lt;br&gt; clock_gettime(clockID, &amp;amp;endTime);&lt;br&gt;&lt;br&gt; isCounterStarted = false;&lt;br&gt; isCounterWorkComplete = true;&lt;br&gt; }&lt;br&gt; else&lt;br&gt; perror(&quot;WARNING: Невозможно остановить не запущен&amp;#92;n&quot;);&lt;br&gt;}&lt;br&gt;&lt;br&gt;unsigned long long GetWorkTime::getRealNanosecondsCount(timespec time)&lt;br&gt;{&lt;br&gt; return (unsigned long long)time.tv_sec * 1000000000 + time.tv_nsec;&lt;br&gt;}&lt;br&gt;&lt;br&gt;unsigned long long GetWorkTime::getCurrentTimeInNanoseconds()&lt;br&gt;{&lt;br&gt; clock_gettime(clockID, &amp;amp;currentTime);&lt;br&gt;&lt;br&gt; return getRealNanosecondsCount(currentTime);&lt;br&gt;}&lt;br&gt;&lt;br&gt;unsigned long GetWorkTime::getCurrentTimeInSeconds()&lt;br&gt;{&lt;br&gt; clock_gettime(clockID, &amp;amp;currentTime);&lt;br&gt;&lt;br&gt; return (unsigned long)currentTime.tv_sec;&lt;br&gt;}&lt;br&gt;&lt;br&gt;unsigned long long GetWorkTime::getWorkTimeInNanoseconds()&lt;br&gt;{&lt;br&gt; if (isCounterWorkComplete)&lt;br&gt; return getRealNanosecondsCount(endTime) - getRealNanosecondsCount(startTime);&lt;br&gt; else&lt;br&gt; perror(&quot;ERROR: Невозможно узнать время выполнения не запустив, а затем остановив, счётчик.&amp;#92;n&quot;);&lt;br&gt;&lt;br&gt; return 0;&lt;br&gt;}&lt;br&gt;&lt;br&gt;unsigned long GetWorkTime::getWorkTimeInSeconds()&lt;br&gt;{&lt;br&gt; if (isCounterWorkComplete)&lt;br&gt; return (unsigned long)endTime.tv_sec - startTime.tv_sec;&lt;br&gt; else&lt;br&gt; perror(&quot;ERROR: Невозможно узнать время выполнения не запустив, а затем остановив, счётчик.&amp;#92;n&quot;);&lt;br&gt;&lt;br&gt; return 0;&lt;br&gt;}&lt;/div&gt;&lt;/div&gt;&lt;br&gt;&lt;strong&gt;Тесты&lt;/strong&gt;&lt;br&gt;&lt;br&gt;Для демонстрации работы написал небольшую обёртку вызывающую счётчик для разных задач и с разными часами.&lt;br&gt;&lt;br&gt;Функция testTime последовательно пытается определить чистое нулевое время замера(интервал между началом отсчёта и окончанием без использования класса-обёртки), после чего замеряет тот же интервал, но уже с использованием класса.&lt;br&gt;Далее считается сколько времени займёт создание класса, ресурсоёмкая задача и sleep(0).&lt;br&gt;&lt;br&gt;Результаты работы на моей машине приведены ниже.&lt;br&gt;&lt;br&gt;CLOCK_REALTIME&lt;br&gt;Текущее время: 1294839189719051200нс, 1294839189с&lt;br&gt;clock_gettime(clockID, &amp;amp;startTime);clock_gettime(clockID, &amp;amp;endTime); выполнилось за: 439нс, 0с&lt;br&gt;counter.countingStart();counter.countingEnd(); выполнилось за: 465нс, 0с&lt;br&gt;Создание и удаление объекта GetWorkTime заняло: 71454нс, 0с&lt;br&gt;Выполнение for (int i = 0; i &lt; 1000000000; i++) testDouble = testDouble / M_PI;: 7397920416нс, 8с&lt;br&gt;Выполнение sleep(0) заняло: 11040нс, 0с&lt;br&gt;&lt;br&gt;CLOCK_MONOTONIC&lt;br&gt;Текущее время: 533621337167908нс, 533621с&lt;br&gt;clock_gettime(clockID, &amp;amp;startTime);clock_gettime(clockID, &amp;amp;endTime); выполнилось за: 450нс, 0с&lt;br&gt;counter.countingStart();counter.countingEnd(); выполнилось за: 465нс, 0с&lt;br&gt;Создание и удаление объекта GetWorkTime заняло: 1943нс, 0с&lt;br&gt;Выполнение for (int i = 0; i &lt; 1000000000; i++) testDouble = testDouble / M_PI;: 6847570003нс, 7с&lt;br&gt;Выполнение sleep(0) заняло: 993нс, 0с&lt;br&gt;&lt;br&gt;CLOCK_MONOTONIC_RAW&lt;br&gt;Текущее время: 533628051394051нс, 533628с&lt;br&gt;clock_gettime(clockID, &amp;amp;startTime);clock_gettime(clockID, &amp;amp;endTime); выполнилось за: 442нс, 0с&lt;br&gt;counter.countingStart();counter.countingEnd(); выполнилось за: 450нс, 0с&lt;br&gt;Создание и удаление объекта GetWorkTime заняло: 2043нс, 0с&lt;br&gt;Выполнение for (int i = 0; i &lt; 1000000000; i++) testDouble = testDouble / M_PI;: 6810908498нс, 6с&lt;br&gt;Выполнение sleep(0) заняло: 979нс, 0с&lt;br&gt;&lt;br&gt;CLOCK_PROCESS_CPUTIME_ID&lt;br&gt;Текущее время: 19781932013нс, 19с&lt;br&gt;clock_gettime(clockID, &amp;amp;startTime);clock_gettime(clockID, &amp;amp;endTime); выполнилось за: 667нс, 0с&lt;br&gt;counter.countingStart();counter.countingEnd(); выполнилось за: 693нс, 0с&lt;br&gt;Создание и удаление объекта GetWorkTime заняло: 2353нс, 0с&lt;br&gt;Выполнение for (int i = 0; i &lt; 1000000000; i++) testDouble = testDouble / M_PI;: 6579920922нс, 7с&lt;br&gt;Выполнение sleep(0) заняло: 1292нс, 0с&lt;br&gt;&lt;br&gt;CLOCK_THREAD_CPUTIME_ID&lt;br&gt;Текущее время: 26361905552нс, 26с&lt;br&gt;clock_gettime(clockID, &amp;amp;startTime);clock_gettime(clockID, &amp;amp;endTime); выполнилось за: 622нс, 0с&lt;br&gt;counter.countingStart();counter.countingEnd(); выполнилось за: 641нс, 0с&lt;br&gt;Создание и удаление объекта GetWorkTime заняло: 2281нс, 0с&lt;br&gt;Выполнение for (int i = 0; i &lt; 1000000000; i++) testDouble = testDouble / M_PI;: 6587131651нс, 6с&lt;br&gt;Выполнение sleep(0) заняло: 1210нс, 0с&lt;br&gt;&lt;br&gt;rdtsc()&lt;br&gt;startTime = rdtsc(); endTime = rdtsc();выполнилось за: 91 тактов&lt;br&gt;Создание и удаление объекта GetWorkTime заняло: 3353 тактов&lt;br&gt;Выполнение for (int i = 0; i &lt; 1000000000; i++) testDouble = testDouble / M_PI;: 14028721707 тактов&lt;br&gt;Выполнение sleep(0) заняло: 1113 тактов&lt;br&gt;&lt;br&gt;Ах да, чуть не забыл, добавьте Вашему gcc ключик -lrt для того что бы clock_gettime заработал.&lt;br&gt;&lt;br&gt;Добавлены тесты, и результаты этих самых тестов, с использованием rdtsc, в связи с чем несколько изменены исходные коды.&lt;br&gt;Прикрутил к велосипеду по определению времени в неаносекундах новые педали, должно быть получше, но есть идея как сделать ещё лучше.&lt;br&gt;Думаю, после праздников добавлю.&lt;br&gt;</content:encoded>
			<link>https://cpplus.my1.ru/news/vremja_vypolnenija_operacij_v_c_pod_linux/2011-01-13-72</link>
			<category>Новости</category>
			<dc:creator>FazaNaka</dc:creator>
			<guid>https://cpplus.my1.ru/news/vremja_vypolnenija_operacij_v_c_pod_linux/2011-01-13-72</guid>
			<pubDate>Thu, 13 Jan 2011 17:08:24 GMT</pubDate>
		</item>
		<item>
			<title>О ненависти к С++</title>
			<description>&lt;nobr&gt;С++&lt;/nobr&gt; or not &lt;nobr&gt;C++&lt;/nobr&gt;, &lt;nobr&gt;C++&lt;/nobr&gt; или
Java/Python/Ruby? Как часто вы задаёте или слышите подобные вопросы? Не
хотелось бы поднимать очередной холивар — по моему мнению, умные люди
давно должны были бы прийти к выводу, что при выборе языка нет той
серебряной пули, которая бы поставила окончательную точку, — у каждого
языка есть свои плюсы и минусы и чаще всего проблемы в прокладке между
клавиатурой и стулом....</description>
			<content:encoded>&lt;nobr&gt;С++&lt;/nobr&gt; or not &lt;nobr&gt;C++&lt;/nobr&gt;, &lt;nobr&gt;C++&lt;/nobr&gt; или
Java/Python/Ruby? Как часто вы задаёте или слышите подобные вопросы? Не
хотелось бы поднимать очередной холивар — по моему мнению, умные люди
давно должны были бы прийти к выводу, что при выборе языка нет той
серебряной пули, которая бы поставила окончательную точку, — у каждого
языка есть свои плюсы и минусы и чаще всего проблемы в прокладке между
клавиатурой и стулом.$CUT$&lt;br&gt;
&lt;br&gt;
Я сам программирую на &lt;nobr&gt;С++&lt;/nobr&gt;, поскольку решаемые задачи
требуют достаточно хорошей скорости обработки данных. На одном из
тематических блогов встретил эмоциональную статью в защиту &lt;nobr&gt;С++&lt;/nobr&gt;. Там больше даже понравились комментарии, чем статья — в них собран весь тот букет аргументов против &lt;nobr&gt;С++&lt;/nobr&gt;, который редко где можно встретить в одном месте. И главное, что автор пытается парировать выпады — прямо дуэль &lt;nobr&gt;какая-то&lt;/nobr&gt;. Все что там написано может использоваться как евангелистам &lt;nobr&gt;C++&lt;/nobr&gt;, так и его противникам языка.&lt;br&gt;
&lt;br&gt;
Взял на себя смелость перевести статью и пару комментариев к ней.&lt;br&gt;
&lt;br&gt;
Но прежде чем читать, посмотрите, пожалуйста, на &lt;a href=&quot;http://alenacpp.blogspot.com/2009/06/c.html&quot;&gt;прекрасную карту языка С++&lt;/a&gt; (The C++ Lands). Особая благодарность за карту Алёне Сагалаевой:&lt;br&gt;&lt;br&gt;
&lt;a href=&quot;http://3.bp.blogspot.com/_VUQ3DQEhjsM/SiulAQFExOI/AAAAAAAAAXQ/UHIhgH4l-54/s1600-h/cpplands1.png&quot;&gt;&lt;img src=&quot;http://lh4.ggpht.com/_sE1M5AiKmYk/TSPaLxddV7I/AAAAAAAAC_0/GSa-c6_15lU/s640/cpplands1.png&quot;&gt;&lt;/a&gt;&lt;br&gt;
&lt;hr&gt;&lt;br&gt;
&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;http://goo.gl/n9sAv&quot;&gt;Оригинал статьи &lt;nobr&gt;«C++&lt;/nobr&gt; Hating»&lt;/a&gt; (автор Dean Michael Berris)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://goo.gl/U5Pu0&quot;&gt;Статья о закате &lt;nobr&gt;С++&lt;/nobr&gt; «A skeptic’s history of &lt;nobr&gt;C++»&lt;/nobr&gt;&lt;/a&gt; (автор Chad Perrin)&lt;/li&gt;
&lt;/ul&gt;&lt;br&gt;
&lt;hr&gt;&lt;br&gt;
У меня представился случай прочитать на сайте TechRepublic интересную.
хорошо написанную, но совершенно неточную и провокационную статью. Эта
писанина одна из тех, что пытаются предсказать смерть &lt;nobr&gt;С++&lt;/nobr&gt; и победу других, не столь мощных, языков. Я, как &lt;nobr&gt;С++&lt;/nobr&gt;
разработчик, который любит этот язык со всеми его скелетами в чулане,
могу заявить, что появление подобных статей — это плохой знак и признак
невежественности технических блогеров.&lt;br&gt;
&lt;br&gt;
Не буду вдаваться в детали — если вам интересно, то оригинал можно найти по &lt;a href=&quot;http://goo.gl/U5Pu0&quot;&gt;ссылке&lt;/a&gt;. Разберу только отрывок, на который не могу не отреагировать.&lt;br&gt;
&lt;br&gt;
&lt;blockquote&gt;Можно считать, что дни &lt;nobr&gt;С++&lt;/nobr&gt; сочтены. Альтернатива, которая наиболее подходит для решения схожих задач — это &lt;nobr&gt;Objective-C&lt;/nobr&gt;. Другой вариант — Objective Caml, язык который регулярно приводится в качестве примера для &lt;nobr&gt;высоко-производительных&lt;/nobr&gt; задач и по некоторым показателям часто опережает &lt;nobr&gt;С++&lt;/nobr&gt;.
На нем можно писать кратко и структурировано, предоставляет
разработчикам более понятные и интересные модели, которые порой
отсутствуют в языках того же уровня. Язык D также можно назвать
конкурентом, хотя его проприетарные корни могут помешать его широкому
применению. В языке Go от Google много спорных компромиссов, но без
сомнения его возможности дают большое преимущество при создании
некоторого типа программного обеспечения, включая параллелизм.&lt;br&gt;
&lt;br&gt;
Однако, исходя из уроков истории, &lt;nobr&gt;С++&lt;/nobr&gt; будет еще долгое время занимать свою нишу. Он годами использовался при разработке операционок. Без спору, &lt;nobr&gt;С++&lt;/nobr&gt;&amp;nbsp;
дает некоторые преимущества перед С для некоторых видов систем, в
которых критична производительность и уже существуют наработки. Но сила
подобных наработок лежит в сопровождении их разработчиками, которые
игнорируют альтернативы, и эта та особенность, которую не легко можно
обойти потенциальным конкурентам.&lt;br&gt;потенциальными конкурентами.&lt;br&gt;
&lt;/blockquote&gt;Так же как существует жёлтый журнализм, то я бы назвал эту статью
хорошим примером жёлтого блогизма. Материал в ней сенсационен и
эмоционален, но не раскрываются факты, на которых они основаны. Если вы
полностью прочтите статью, то увидите, что она завалена анекдотами, на
основе которых пытаются сделать вывод о кончине &lt;nobr&gt;С++&lt;/nobr&gt;. Но в итоге, автор фактически сдаётся и признает, что &lt;nobr&gt;C++&lt;/nobr&gt; &lt;nobr&gt;все-равно&lt;/nobr&gt; будет использоваться &lt;nobr&gt;какое-то&lt;/nobr&gt; время.&lt;br&gt;
&lt;br&gt;Давайте посмотрим, что он на самом деле говорится в статье и попробуем прочитать между строк.&lt;br&gt;
&lt;blockquote&gt;Можно считать, что дни &lt;nobr&gt;С++&lt;/nobr&gt; сочтены. Альтернатива, которая наиболее подходит для решения схожих задач — это &lt;nobr&gt;Objective-C&lt;/nobr&gt;.&lt;br&gt;
&lt;/blockquote&gt;К сожалению, автор не указал какие задачи имеются в виду. Он привёл пример браузеров. &lt;nobr&gt;Так-что&lt;/nobr&gt; давайте познакомимся с этим поближе.&lt;br&gt;
&lt;br&gt;
Cказано, что существует множество альтернатив &lt;nobr&gt;С++&lt;/nobr&gt;
для разработки браузеров, высокопроизводительных программ, настольных
приложения и высоконагруженных серверных систем. Давайте, перечислим
несколько успешных продуктов на рынке, которые написаны на &lt;nobr&gt;C++&lt;/nobr&gt; и объясним для чего был выбран именно &lt;nobr&gt;С++&lt;/nobr&gt;.&lt;br&gt;
&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Google Chrome&lt;/strong&gt; — кроссплатформенный браузер, первый
по скорости и производительности. Если вы захотите заменить язык
программирования, на котором он написан, то придется использовать тот
язык, который портируется на многие платформы. Я говорю не только о Mac
OSX, Windows или Linux — также имею в виду Android (проверьте, Google
TV — это Android с портированным Chrome).&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adobe Creative Suite&lt;/strong&gt; — системы, работающие с
видео, аудио и графикой очень требовательны к производительности. Если
вы захотите портировать продукты Adobe, то вам придется использовать
язык, при помощи которого можно создавать программы использующие
наименьшее количество ресурсов компьютера при выполнения простых задач
создания/редактирования, и при этом оставляют достаточно ресурсов для
выполнения других задач системы. Ох, и конечно же необходимо &lt;nobr&gt;что-бы&lt;/nobr&gt; программа работала как на OS X, так и на Windows.&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Microsoft Office&lt;/strong&gt; — широко распространенная,
расширяемая, встраиваемая и самая нашпигованная фичами система. При
портировании Microsoft Office, нужно чтобы в результате не получился
монстр и программа быстро реагировала на ваши действия. Вы должны быть
уверены, что можно будет написать мощные расширения, которые смотрелись
как одно целое с программой.&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;World of Warcraft&lt;/strong&gt; — и здесь я рассматриваю не
только десктопного клиента, который перерабатывает огромное количество
событий, следует куче правил, отрисовывает графику и делает еще кучу
всего связанного с игрой. Подумайте также о серверной части. Нужны ли
еще &lt;nobr&gt;какие-либо&lt;/nobr&gt; слова об этой прекрасной программе?&lt;br&gt;
&lt;/li&gt;
&lt;/ul&gt;Я даю вам возможность самим назвать хотя бы один другой язык
программирования, который позволяет написать программы подобные этим и
предоставить тот же уровень функциональности, стабильности,
расширяемости, портабельности. Прежде, чем вы назовёте С, хочу
напомнить, что С — это подмножество &lt;nobr&gt;С++&lt;/nobr&gt;,
и в случае если подразумеваете чистый С, то удачи вам и пожелание не
сойти с ума, в случае если не используете абстракции, которые
предоставляет &lt;nobr&gt;С++&lt;/nobr&gt;.&lt;br&gt;
&lt;blockquote&gt;Другой вариант — Objective Caml, язык который регулярно приводится в качестве примера для &lt;nobr&gt;высоко-производительных&lt;/nobr&gt; задач и по некоторым показателям часто опережает &lt;nobr&gt;С++&lt;/nobr&gt;.
На нем можно писать кратко и структурировано, предоставляет
разработчикам более понятные и интересные модели, которые порой
отсутствуют в языках того же уровня.&lt;/blockquote&gt;
Вы действительно думаете, что &lt;nobr&gt;C++&lt;/nobr&gt; это только &lt;nobr&gt;объектно-ориентированый&lt;/nobr&gt; язык и ничего более? Сила &lt;nobr&gt;С++&lt;/nobr&gt; не только в поддержке &lt;nobr&gt;объктно-ориентированого&lt;/nobr&gt; программирования — фактом является то, что в &lt;nobr&gt;С++&lt;/nobr&gt; можно использовать много разных парадигм, которые делают язык достаточно мощным. Теперь о тестах. Каждый должен понимать, что &lt;nobr&gt;микро-тесты&lt;/nobr&gt;
предназначены для тестирования только одного из аспектов работы
системы. Настоящие тесты — это те, что испытывают систему в реальных
условиях.&lt;br&gt;
&lt;br&gt;
Если говорить о высокопроизводительном коде, то здесь большую роль
играет используемый компилятор, а не используемый язык высокого уровня.
Это верно для всех компилируемых языков. Легко можно сказать, что один
язык лучше другого, если инструменты первого лучше, чем у второго, но в
реальности производительность продукта в большей степени зависит от
нескольких вещей — железа, компиляторов и кода. Одна из основных причин
низкой производительности некоторых программ — это плохой код.
Например, когда выбирается алгоритм без учёта реальной ситуации,
входных данных,… Если сравнивать пузырьковую сортировку в &lt;nobr&gt;C++&lt;/nobr&gt; и поразрядную сортировку в OCaml, то, возможно, программа на OCaml будет более быстрой.&lt;br&gt;
&lt;br&gt;
Говоря об эстетике, то это дело вкуса. Поэма, написанная на
французском, не будет для меня прекрасна, поскольку я не
читаю/говорю/пишу на французском — и тут уже не важно насколько она
прекрасна для другого человека. То же самое и для &lt;nobr&gt;C++&lt;/nobr&gt; — я видел (и надеюсь, что писал) прекрасный код на &lt;nobr&gt;C++&lt;/nobr&gt;, но программист на Java или С будет, возможно, иного мнения, поскольку они не знают язык так же как я.&lt;br&gt;
&lt;br&gt;
Большая часть вопросов об эффективности и профессиональности связана не
только с техническими особенностями, но и теми знаниями и их обмен
между разработчиками. Вы не можете сказать, что вы профессионал в &lt;nobr&gt;C++&lt;/nobr&gt;, если это не подтвердят коллеги по цеху.&lt;br&gt;
&lt;br&gt;
У некоторых может возникнуть мнение, что прочитанная книга о &lt;nobr&gt;С++&lt;/nobr&gt;,
даст вам право считать себя профессионалом, но также, как и в случае с
другими языками, профессионализм и эффективность приходят только с
опытом.&lt;br&gt;
&lt;blockquote&gt;Язык D также может составить конкуренцию, хотя его
проприетарность может помешать широкому применению. Язык Go от Google,
содержит много спорных компромиссов, но без сомнения его конструкции
дают большое преимущество при создании некоторого типа программного
обеспечения, включая параллелизм.&lt;/blockquote&gt;
Цитируя Джона Дворака (John Dvorak) — «ерунда все это».&lt;br&gt;
&lt;br&gt;
Если считаете, что проприетарность может мешать широкому применению, то
взгляните на Java, который стал новым Cobol’ом в мире бизнеса.
Вспомните об ассемблере x86 — он тоже проприетарен. Существует много
закрытых языков программирования, которые широко применяются,
проприетарность не помеха: что реально имеет значение — это техническая
сторона. Хакеры которые выбирают только те инструменты, которыми будут
пользоваться. Если вы хотите продать мне мышеловку, то и она должна
быть достаточно хороша, чтобы я решил потратить на неё деньги.&lt;br&gt;
&lt;br&gt;
По поводу параллельности, то реальная проблема в том, что основная
часть программистов не знает как писать эффективные параллельные
программы. Вот где загвоздка. И не имеет значения, какой язык
используется. Недостаток знания в предмете, машинных архитектурах,
шаблонах разнообразного доступа к данным, в совокупности с
ограничениями на ресурсы — вот, что делает параллельное
программирование «сложным». Даже когда упоминается Erlang или Go, то &lt;nobr&gt;все-равно&lt;/nobr&gt; не эффективно писать приложения, когда вы делаете это неправильно и не используете идиомы и шаблоны для параллельных программ.&lt;br&gt;
&lt;blockquote&gt;Однако, исходя из уроков истории, &lt;nobr&gt;С++&lt;/nobr&gt; будет еще долгое время занимать свою нишу. Он годами использовался при разработке операционок. Без спору, &lt;nobr&gt;С++&lt;/nobr&gt;
дает некоторые преимущества перед С для некоторых видов систем, в
которых критична производительность и уже существуют наработки. Но сила
подобных наработок лежит в сопровождении их разработчиками, которые
игнорируют альтернативы, и эта та особенность, которую не легко можно
обойти потенциальным конкурентам.&lt;br&gt;потенциальными конкурентами.&lt;/blockquote&gt;
B cнова — &lt;strong&gt; фигня все это&lt;/strong&gt;.&lt;br&gt;
&lt;br&gt;
Разработчики, использующие &lt;nobr&gt;С++&lt;/nobr&gt; каждый день, не живут
в вакууме. Мы знаем о Ruby, Python, Haskell, Lisp, JavaScript, Java и
даже такие новомодные языки как Erlang, Go, Lua &lt;nobr&gt;и т. д.&lt;/nobr&gt; Причиной, по которым организации используют &lt;nobr&gt;С++&lt;/nobr&gt; для создания хороших &lt;nobr&gt;С++&lt;/nobr&gt; приложений, — является то, что &lt;nobr&gt;С++&lt;/nobr&gt; работает и позволяет делать вещи, которые могут делать и другие небольшие языки. Я буду первым, кто признает, что &lt;nobr&gt;C++&lt;/nobr&gt;
не столь продуктивен, когда необходимо работать с задачами для которых
предназначен Lisp, но чтобы вы знали, при использовании современных фич
&lt;nobr&gt;С++&lt;/nobr&gt;, таких как метапрограммирование на шаблонах, можно решать и эти задачи.&lt;br&gt;
&lt;br&gt;
Кроме того, Lisp не предназначен для создания высокопроизводительных
программ, по крайней мере до тех пор, пока не будете настраивать эти
программы под нагрузку, с которой вы возможно столкнётесь.&lt;br&gt;
&lt;br&gt;
&lt;strong&gt;Заключение&lt;/strong&gt;&lt;br&gt;
&lt;br&gt;
Я смотрю на количество слов в статье — их около 2000. Этот вид ненависти к &lt;nobr&gt;С++&lt;/nobr&gt; разжигает меня и заставляет защищаться, но давайте я взгляну в будущее. Причина, по которой считаю &lt;nobr&gt;С++&lt;/nobr&gt;
мощным инструментов — это не только та, что я потратил на его изучении
и использовании много времени. Это далеко не правда, есть другая
причина — я очень многого достиг, начав применять &lt;nobr&gt;С++&lt;/nobr&gt;.&lt;br&gt;
&lt;br&gt;
Я слышал много критики в адрес &lt;nobr&gt;С++&lt;/nobr&gt; от разных людей. CEO, HR менеджеров, разработчиков, бизнес партнёров и даже &lt;nobr&gt;С++&lt;/nobr&gt; программистов. Но причина, по которой &lt;nobr&gt;С++&lt;/nobr&gt;
стандартизирован ISO, причина по которой процветает рынок приложений на
этом языке — это его не ограниченность дизайном — так случилось, что
язык был спроектирован с учетом лучших и случайно попавших плохих
частей прошлых языков для представления многогранного инструмента для
создания хороших приложений. Много что можно сделать &lt;nobr&gt;с++&lt;/nobr&gt;, намного больше, что можно сделать на других языках.&lt;br&gt;
&lt;br&gt;
Если и есть одна критика в сторону &lt;nobr&gt;C++&lt;/nobr&gt;, то это его
неправильное понимание. Если каждый потратит на его изучение столько же
времени как на Ruby/Python/JavaScript, то я уверен, что это поменяет
представление о языке. Существует много хороших книг о &lt;nobr&gt;С++&lt;/nobr&gt; — одна из них &lt;a href=&quot;http://goo.gl/3HFbt&quot;&gt;Programming: Principles and Practice using &lt;nobr&gt;C++&lt;/nobr&gt;&lt;/a&gt;. Прежде, чем стучать на &lt;nobr&gt;C++&lt;/nobr&gt; снова, прочитайте эту книгу и расскажите о слабостях &lt;nobr&gt;C++&lt;/nobr&gt; — и будет нормальным, если вы поблагодарите меня потом.&lt;br&gt;
&lt;br&gt;
&lt;strong&gt;Об авторе Dean Michael Berris:&lt;/strong&gt;&lt;br&gt;
&lt;br&gt;
Я программист по профессии, специализирующийся на разработке высокопроизводительных сетевых приложений на &lt;nobr&gt;С++&lt;/nobr&gt;.
Много читаю, пишу и дискутирую о том, что мне близко. Особенно
интересуюсь разработками в параллельном программировании и
высокопроизводительными системами, в особенности построенных на языке &lt;nobr&gt;С++&lt;/nobr&gt;.&lt;br&gt;
&lt;br&gt;
&lt;hr&gt;&lt;strong&gt;Пара критических комментариев к статье и ответы на них.&lt;/strong&gt;&lt;br&gt;
&lt;blockquote&gt;Программируя на &lt;nobr&gt;С++&lt;/nobr&gt; очень легко попасть в
ловушки. На нем сложно программировать, поскольку многие вещи просто
скрыты от разработчика компилятором (например, компилятор создает по
умолчанию конструктор копирования, и если он не был явно создан, то это
ведет к побайтовому копированию указателей и может привести в аварии в
программе. Даже программа, которая большую часть времени работает
нормально, может нести в себе ошибку неправильного использования
удаленного указателя. Очень сложно программировать на &lt;nobr&gt;С++&lt;/nobr&gt;, когда поджимают сроки и, особенно, когда в команде присутствуют новички.&lt;br&gt;
&lt;br&gt;
У &lt;nobr&gt;С++&lt;/nobr&gt; намного меньше поддерживаемых библиотек, по
сравнению например, в Java. Это означает, что каждый раз приходится
реализовывать простые вещи, которые уже есть в других языках.&lt;br&gt;
&lt;br&gt;
Очень большая платформозависимость. Нужно приложить много усилий для портирования с Unix на Windows и обратно.&lt;br&gt;
&lt;br&gt;
&lt;nobr&gt;С++&lt;/nobr&gt; можно применять для академических проектов, в которых
важна гибкость языка и есть достаточно времени, чтобы привести
программу к идеальному виду.&lt;br&gt;
&lt;br&gt;
&lt;/blockquote&gt;Это все равно, что называть молоток хорошим, но опасным инструментом, когда вы не умеете им пользоваться.&lt;br&gt;
&lt;br&gt;
Но посмотрите на &lt;nobr&gt;С++&lt;/nobr&gt; с той стороны, где он напрямую
работает с железом. Ещё, вы говорите о «базовых вещах», но не каждый
язык имеет четкое представление, что есть «базовая вещь». В &lt;nobr&gt;С++&lt;/nobr&gt;
базовым является выделение памяти, средства работы с памятью
(указатели), типы (классы, структуры, примитивные типы), объекты этих
типов (объекты первого класса), управляющие структуры и функции, такие
синтаксические конструкции как namespace’ы, модификаторы типов, и все
те инструменты для создания модулей и библиотек.&lt;br&gt;
&lt;br&gt;
Если вы не любите работать с низкоуровневыми инструментами, то учите
STL, используйте Boost, изучайте Loki или ACE. Если вы не понимаете,
что такое указатели, то не сможете применять даже С в реальных
программах и ничего не добьётесь от &lt;nobr&gt;С++&lt;/nobr&gt;.&lt;br&gt;
Для &lt;nobr&gt;С++&lt;/nobr&gt; существует множество библиотек для разных вещей. Вы можете также использовать уже написанные С библиотеки.&lt;br&gt;
&lt;br&gt;
Я не уверен, что вы используете &lt;nobr&gt;C++&lt;/nobr&gt; в рабочих проектах и обидно слушать от вас нападки тем, кто использует &lt;nobr&gt;С++&lt;/nobr&gt; уже продолжительное время. Но предполагаю, что коль вы считаете, что для &lt;nobr&gt;С++&lt;/nobr&gt; имеется меньше библиотек, чем для Java, то не удивлён вашим словам о том, что &lt;nobr&gt;C++&lt;/nobr&gt; «полезный инструмент для академических разработок».&lt;br&gt;
&lt;blockquote&gt;Если вы сравниваете &lt;nobr&gt;С++&lt;/nobr&gt; c молотком, то
существует по крайней мере 5 разных приспособлений и насадок к нему и
непонятно какую из них выбрать. Выбрав неверный, вы отобьете себе руки.
Да, если выберите правильный, то сможете работать быстрее, но какова
вероятность выбора правильного молотка?&lt;br&gt;
&lt;br&gt;
&lt;nobr&gt;C++&lt;/nobr&gt; был одним из моих инструментов более 10 лет. Я
участвовал в больших проектах и некоторые из них все еще используются.
Однако, было много проектов, которые останавливалиcь еще до своего
завершения — выходили за рамки отпущенного времени и бюджета. После
этого команда java программистов реализовывала ту же функциональность в
течение 2 недель и без существенных багов. Даже производительность java
версии была выше (&lt;nobr&gt;с++&lt;/nobr&gt; код не был оптимизирован).&lt;br&gt;
&lt;br&gt;
Я видел опытных программистов делающих глупые ошибки, которые так легко совершить, но трудно найти в &lt;nobr&gt;С++&lt;/nobr&gt;.&lt;br&gt;
&lt;br&gt;
Портирование кода с unix на windows — это полный кошмар. Причудливые
конструкции с шаблонами имеют большой шанс не быть скомпилированными на
другой платформе.&lt;br&gt;
&lt;br&gt;
Портирование с windows на unix тоже не сахар.&lt;br&gt;
&lt;br&gt;
STL хороша и делает программирование проще и библиотека поддерживается
всеми компиляторам. Она предоставляет много «базовых вещей», такие как
контейнеры, необходимые нам для реализации бизнес логики. Но помимо
хорошего дизайна и удобных штучек, ее сообщения об ошибках просто
ужасны. STL может немного различаться на разных платформах, что может
привести к проблемам при портировании.&lt;br&gt;
Мы используем ACE/TAO фрейморк, но и с ним возникали проблемы на
windows платформе, хотя заявляется, что библиотека платформонезависимая.&lt;br&gt;
&lt;br&gt;
Мы тестируем, как Boost может использоваться в наших проектах.&lt;br&gt;
&lt;br&gt;
Проблема заключается в том, что мы уже имеем 3 разный фреймворка (2 из
них внутренние разработки), которые уже используются и не могут быть
просто заменены другим без переписывания большого количества кода — нам
это не позволяет бюджет. Написание 4го приведет еще к большей путанице
(проблема cвязана не &lt;nobr&gt;с++&lt;/nobr&gt;, а с непредсказуемой сложностью добавления фунциональности и растущем временем на рефакторинг кода)&lt;br&gt;
&lt;br&gt;
Каждый язык имеет свои хорошие и плохие стороны. Очень важно выбрать правильный для проекта.&lt;br&gt;
&lt;br&gt;
Хотя &lt;nobr&gt;C++&lt;/nobr&gt; предоставляет достаточную гибкость по
сравнению с другими языками, но это не тот инструмент для задач, где
нужна скорость разработки.&lt;br&gt;
&lt;/blockquote&gt;Извините, но инструмент, который вы описали не имеет ничего общего с молотком.&lt;br&gt;
О том, что разработка на &lt;nobr&gt;С++&lt;/nobr&gt;
более дорога, говорит больше о команде, чем о языке программирования,
не правда ли? Я не уверен о каком тип программ вы говорите, но если
речь идет о сетевом приложении (я это понял по использованию ACE/TAO),
то не вижу где могут возникнуть проблемы. Зная ACE и профессионально
используя его ранее, я уверен, что вы быстро реализуете требуемую
функциональность, поскольку это уже готовый фреймворк.&lt;br&gt;
&lt;br&gt;
Когда вы говорите о глупых ошибках, то на самом деле это зависит от
человека пишущего код. Я написал клиента к memcache с нуля за 1 неделю,
используя современный &lt;nobr&gt;C++&lt;/nobr&gt;
стиль, и библиотека все еще используется в том месте, где я ее написал
(подсказка: ищите по ключевому слову memcache++). Переписал сервер с
нуля за два месяца, 1 месяц использовал для тестирования и улучшений, и
это при том, что работал один — сервер может обрабатывать около 5k
запросов в секунду (RPS), при этом используется не более 60% CPU и
средняя нагрузка 2:1 для СPU. И мне трудно понять, почему многие
испытывают сложно &lt;nobr&gt;с++&lt;/nobr&gt;, когда мне удается делать подобные вещи.&lt;br&gt;
&lt;br&gt;
Уверен, что не я один имею такой же положительный опыт.&lt;br&gt;
&lt;br&gt;
Портирование на другую платформу — это только вопрос использования
стандартных языковых конструкций. Это также подразумевает использование
абстракций, которые скрывают от программиста платформозависимые вещи.
Пишите библиотеки и при этом думайте о том как она будет поддерживаться.&lt;br&gt;
&lt;br&gt;
Если говорить о сообщениях об ошибках, то это обязанность компилятора.
Cогласен, GCC ужасен в этом плане. Но другие компиляторы могут выводить
диагностику лучше, чем GCC — я имею в виду clang — он должен выстрелить
в скором времени. MSVC 10 тоже может давать хорошие сообщения.&lt;br&gt;
&lt;br&gt;
О вашем проекте я должен сказать, что вы должны нанять эксперта,
который знает что делать в подобной ситуации. Наймите BoostPro и
обучите вашу команду использовать современные &lt;nobr&gt;С++&lt;/nobr&gt; подходы, а не те, что были десятилетие назад.&lt;br&gt;
&lt;br&gt;
Я знаю хорошие и плохие стороны &lt;nobr&gt;C++&lt;/nobr&gt;, и они не имеют
ничего общего с проблемами человека, который программирует на нем. Если
вам нужна быстрая разработка, то вы должны улучшить ваши процессы и
изучить используемые инструменты. Я искренне сомневаюсь, что существует
язык программирования, который может помешать &lt;nobr&gt;кому-либо&lt;/nobr&gt; быстро прототипировать.</content:encoded>
			<link>https://cpplus.my1.ru/news/o_nenavisti_k_s/2011-01-12-71</link>
			<category>Новости</category>
			<dc:creator>FazaNaka</dc:creator>
			<guid>https://cpplus.my1.ru/news/o_nenavisti_k_s/2011-01-12-71</guid>
			<pubDate>Wed, 12 Jan 2011 09:44:48 GMT</pubDate>
		</item>
	</channel>
</rss>