Архивы по Категориям: Work

Проклятье C++ программиста

Это незабываемый DllMain. С присущим ему списком «чего делать нельзя». Вообще, лучше в DllMain не делать вообще ничего.

Опять подставили подножку. Опять сделали то, чего делать нельзя было, и в результате четыре версии и пять релизов спустя в полнолуние были отстрелены ноги всем соседним модулям. Чудом не задело оператора. Что характерно, все в принципе работает, но треш и угар начинаются в самые неподходящие моменты. Труп (собрать дамп сходу даже не получилось) мне передали дрожащим руками, вссхлиывая «мы ничего, а оно вот и потом бум».

Запомните, дети — даже если создавать тред из DllMain в принципе можно, хотя и не рекомендуется, причем не рекомендуется совершенно по делу, ждать его завершения там же по DLL_PROCESS_DETACH нельзя ни при каких условиях. Даже если вам показалось, что оно работает. Ловить зомби будете всей конторой.

Время кидаться пальцами

Вышла новая версия буста. Теперь могу обоснованно заявлять, что в бусте присутствует и мой contribution в том числе.

 

Докатился…

Отправил баг-репорт в boost.

Будут молчать, придется пойти на совсем немыслимое — отправить им патч.

Однако, первый блин..

Нарисовал тут аппликуху для ондроида.


Get it on Google Play

Включает хотспот адным кнопом, да.

Программерское

Вчера скачал 2012 студию. Официально, с MSDN. Уже 10 минут жду первого запуска.

А ещё сегодня гугол заявил, что ридер закрывают. Пичалько. И ведь ясно, что бизнес оппортьюнити и все такое, да подъем аналога, который не будет являться очередным УГ, под силу разве что компании с сопоставимым количеством дензнаков.

Шаблонная магия

Нечаянно немножко шаблонно наметопрограммировал в текущем проекте. Чтоп компилятор за меня мою работу делал, да.

Теперь главное — не оставить домашнего адреса, когда увольняться буду.

Это они серьезно?

На фоне якобы запуска якобы Самсунгом новой версии Tizen, на КЫВТе состоялось обсуждение API. Я тоже побыл соучаснегом.

All two-phase construction classes have the Construct() method, which must be called once right after the class is instantiated.

Two-phase construction is used to let the caller know about an exception raised in class constructors. Because Tizen does not use the standard C++ exception mechanism in the platform layer, it is impossible to let the caller know about exceptions raised in the class constructors

Это пипец.

Error handling in Tizen works differently compared to standard C++. Tizen uses error results instead of C++ exceptions, due to historical reasons.

All exceptions in Tizen are caught as the result return type. The E_SUCCESS result indicates a method succeeded, while all other result values indicate an error.

А это треш и угар. Due to historical reasons, ага. В 2013 году. Они ее во времена первых версий DOS разрабатывать начали, что ли?

Весь хелп здесь. Примеры использования доставляют до состояния «дайте мне ЭТО развидеть».

Зато авторы ЭТОГО, надеюсь, в сортировках разбираются и гномиков в боингах взвешивать умеют.

Warning C4103 in Visual Studio 2010 is broken?

В продолжение поста.

Есть такой хедер

// dummyHeader.h
#pragma  pack(push, 1)
			struct Dummy
			{
				int a;
				char b;
				short c;
			};
// oops, #pragma pack(pop) is missing..

И есть такой cpp файл, его включающий

#include "DummyHeader.h"

#include "SomeOtherHeader.h"

При компиляции такого исходника студия честно жалуется

warning C4103: ‘test.cpp’ : alignment changed after including header, may be due to missing #pragma pack(pop)

Теперь следим за руками:

#include "SomeOtherHeader.h"
#include "DummyHeader.h"

Поменяли хедеры местами.

Компилирует — аж фуфайка заворачивается. Никаких ворнингов.

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

Это ж лютый песец, йащетаю.

ЗЫ:
Microsoft Visual Studio 2010
Version 10.0.30319.1 RTMRel

ЗЫЫ:
Проблема не наблюдается в SP1Rel. Там предупреждение выдается в любом случае

Что pragma pack в себе таит

Сегодня коллега порвал три бубна. Искал страшный и ужасный баг, который приводил к тому, что в отладочной сборке срабатаывал run-time check на heap corruption. Порвав последний бубен, коллега воззвал к нашему гуру отладки, одним взглядом исцеляющему stack overflow и access violation. Гуру пришел со своими бубнами. Когда и у него бубны кончились, они зачем-то стали просить бубен взаймы у меня.

Вкратце, происходило следующее. Есть примерно такой класс

class Response
{
#pragma pack(push, 1)
struct Header
{
int blah;
unsighed short blahblah;
unsigned char vesrion;
}
#pragma pack(pop)
public:
 Response();
 void firstMethod();
 virtual void anotherMethod();
private:
 Header m_header;
 std::vector<char> m_data1;
 std::vector<char> m_data2;
}

Это часть парсера одного абстрактного протокола в вакууме, что объясняет использование #pragma pack(1).

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

Ребята раскопали, как работает подобная проверка повреждения кучи. Всем известно, что отладочный рантайм MSVC инициализирует выделенную, но еще неинициализированную память значениями 0xCD. Но вот мне было интересно узнать, что в дополнении к этому, рантайм также расставляет заборы. То есть при выделении памяти под объект размера X рантайм размещает значение 0xFD по смещению X от начала объекта или, другими словами, сразу «за» объектом (тут подробнее). При удалении объекта рантайм проверяет, цел ли забор, то есть сохранилось ли значение 0xFD сразу за удаляемым объектом. Сообщение, выдаваемое в случае, если за объектом обнаружено нечто неожиданное, но не 0xFD, выглядит настолько устрашающе, что при его виде плачут даже лично знакомые с Александреску.

cdcdcdcd cdcdcdcd fdcdcdcd   //Allocated, not initialized
00abed01 6fa91001 fdcdcdcd   // Correctly initialized
00abed01 6fa91001 00cdcdcd   // BANG!

Первая строка наверху — память выделена, но не инициализирована (конструктор объекта еще не выполнился). Следующая строка — память инициализирована, объект сконструирован, замечаний нет. На третьей линии случился какой-то катаклизм и забор (0xFD) оказался продырявлен. Такое может произойти, к примеру, при buffer overrun. При удалении такого объекта отладочный рантайм поднимет вой.

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

Вернемся к нашим баранам. Buffer overrun мы отмели ввиду отсутствия такового (буфера). Для случайного расстрела памяти мина больно прицельно попадала в одну и ту же штакетину забора, поэтому вариант снайпера в соседнем потоке тоже был отметен. Осталось смотреть на создаваемый объект под отладчиком.

После бесчисленных экспериментов удалось установить, что несмотря на то, что sizeof(Response), вызванная в месте создания экземпляра объекта, выдавала 59 байтов, при пошаговой отладке код конструктора объекта ожидал, что объект имеет размер в 60 байтов. Как результат — последний член класса «выезжал» за пределы отведенной под отбъект памяти ровно на 1 байт, чем и рушил забор.

По правде сказать, хорошо поднаторевший в поисках багов Шрёдингера Штирлиц должен был бы насторожиться при виде оператора sizeof, возвращающего 59. Но в нашем случае причина чертовщины оказалась в том, что в разных единицах трансляции размер класса оказался разным! В итоге в месте использования код думал, что имеет дело с объектом размером в 59 байт, в то время как код конструктора полагал, что объект выровнен на границу 4 байт и посему имеет размер 60 байт.

Причина такого поведения оказалась проста. В одном из заголовков #pragma pack(push, 1) перед структурой поставили, но #pragma pack(pop) — забыли. Этот заголовок оказался включен в месте использования, и как результат — в этом *.cpp все структуры оказались выровнены на границу 1 байта. Багфикс заключался в поиске непарной прагмы и восстановлении статус-кво.

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

В продолжение — universal references

В продолжение вчерашней темы - universal references in C++ by Scott Meyers.

Ох и крови мне в свое время попили эти ссылки на ссылки в шаблонных функциях!

Короче, теперь вот это

const someData& getData();

template 
void DoSomething(const T&& _what)
{

}

DoSomething(getData());   // C++03: Shit!

должно компилироваться нормально, слава reference collapsing!

Это, конечно, далего не главная фича reference collapsing, но ИМХО уже одного этого достаточно, чтобы знать и любить. Главное — помнить, что collapsing работает только в случае, когда тип выводится компилятором

7 посетителей онлайн
1 гостей, 6 bots, 0 зарегистрированных
Максимум сегодня:: 41 в 01:32 am UTC
В этом месяце: 41 в 12-17-2014 12:14 pm UTC
В этом году: 130 в 10-22-2014 11:16 pm UTC
За все время: 130 в 10-22-2014 11:16 pm UTC