вторник, 8 декабря 2009 г.

Delayed writes

Наверное, уже многие знают, что результат выражения i++ + ++i является неопределенным. Тут вроде все ясно: в operator+ передаются два аргумента и порядок вычисления аргументов может быть любым, что и дает неопределенность. Однако, в стандарте есть гораздо более интересные примеры. Утверждается (С++'03 5/4), что:

i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9

i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented
Рассмотрим предпоследнее выражение. Допустим, изначально i=0. Интересно, что интуитивно выражение i = ++i + 1 абсолютно определено и непонятно как создатели компилятора могут выдать что-то кроме 2. Однако, стандарт позволяет в реализации делать отложенную запись вычислений. Т. е. в этом последнем примере может быть такая реализация:
  1. Посчитали значение ++i и запомнили в регистре (в переменную i результат запишем позднее).
  2. Считаем результат ++i + 1, взяв результат п. 1.
  3. Записываем в i значение, которое посчитали в п. 2.
  4. О, чуть не забыли. Записываем результат п. 1 в переменную i.
Итого, получили в i значение 1. Такое поведение допускается стандартом в пункте 5 абзац 4:
Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.
В отладочном режиме такого, скорее всего не будет, но при оптимизации очень может быть. Для разработчиков это означает, что, если в выражении переменная меняется более одного раза, то это скорее всего приведет к нежелательным последствиям и трудноуловимым багам.

Ссылки по теме:
Why is `i = ++i + 1` unspecified behavior?
Точки следования (sequence points)

четверг, 3 декабря 2009 г.

How to tame the Windows headers


При подключении windows.h определяется огромное количество макросов, что бывает совсем не полезно. Для борьбы с этим в Microsoft придумали дефайны, которые позволяют немного ограничить объем подключаемой информации. В MSDN эта тема обходится стороной, поэтому ниже приведен фрагмент windows.h:

/*  If defined, the following flags inhibit definition
* of the indicated items.
*
* NOGDICAPMASKS - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
* NOVIRTUALKEYCODES - VK_*
* NOWINMESSAGES - WM_*, EM_*, LB_*, CB_*
* NOWINSTYLES - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
* NOSYSMETRICS - SM_*
* NOMENUS - MF_*
* NOICONS - IDI_*
* NOKEYSTATES - MK_*
* NOSYSCOMMANDS - SC_*
* NORASTEROPS - Binary and Tertiary raster ops
* NOSHOWWINDOW - SW_*
* OEMRESOURCE - OEM Resource values
* NOATOM - Atom Manager routines
* NOCLIPBOARD - Clipboard routines
* NOCOLOR - Screen colors
* NOCTLMGR - Control and Dialog routines
* NODRAWTEXT - DrawText() and DT_*
* NOGDI - All GDI defines and routines
* NOKERNEL - All KERNEL defines and routines
* NOUSER - All USER defines and routines
* NONLS - All NLS defines and routines
* NOMB - MB_* and MessageBox()
* NOMEMMGR - GMEM_*, LMEM_*, GHND, LHND, associated routines
* NOMETAFILE - typedef METAFILEPICT
* NOMINMAX - Macros min(a,b) and max(a,b)
* NOMSG - typedef MSG and associated routines
* NOOPENFILE - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
* NOSCROLL - SB_* and scrolling routines
* NOSERVICE - All Service Controller routines, SERVICE_ equates, etc.
* NOSOUND - Sound driver routines
* NOTEXTMETRIC - typedef TEXTMETRIC and associated routines
* NOWH - SetWindowsHook and WH_*
* NOWINOFFSETS - GWL_*, GCL_*, associated routines
* NOCOMM - COMM driver routines
* NOKANJI - Kanji support stuff.
* NOHELP - Help engine interface.
* NOPROFILER - Profiler interface.
* NODEFERWINDOWPOS - DeferWindowPos routines
* NOMCX - Modem Configuration Extensions
*/

понедельник, 30 ноября 2009 г.

How to stop VC++ stepping into certain files

При отладке приходится сталкиваться с ситуациями вроде следующей:

int x1 = somefunc( a.c_str(), b.c_str(), somefunc2( c.c_str() ) );
При нажатии F11(StepInto) отладчик попадает с функцию c_str для первого аргумента, потом второго и так далее. И только потом в SomeFuct. Visual Studio позволяет в каждом случе войти в жалаемую функцию с помощью контекстного меню, как показано на рисунке:


Однако, не всегда удобно каждый раз пользоваться меню. На этот случай в IDE присутсвует недокументированная возможность задать правило перехода по функциям. Конфигурация NoStepInto хранится в реестре в разделе HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\NativeDE\StepOver. Там можно создать новые строки, которые описывают выражения, куда "не нужно заходить". Примеры:

Не заходить в функции класса CString:
1         \scope:CString.*\:\:.*=NoStepInto
Не входить в перегруженные операторы:
10        \scope:operator\oper:=NoStepInto
Не заходить в ATL:: кроме функций CComBSTR,
которые не являются операторами:
20        ATL\:\:CComBSTR::\funct:=StepInto
10 ATL\:\:.*=NoStepInto
Не заходить в шаблоны, кроме случаев когда это
шаблонные функции обычного класса:
20        \scope:\funct:=StepInto
10 .*[\<\>].NoStepInto


Дополнительную информацию по этой теме можно найти тут.

вторник, 17 ноября 2009 г.

Visual Studio & UTF8

Знаете ли вы, что компилятор Visual C++ поддерживает сорс файлы в кодировке UTF8? Для пользователя это означает, что можно писать комментарии и строковые констаны на различных языках одновременно.

wstring a=L"grüßen";
wstring b=L"שלום עולם!"; // привет мир!
wstring c=L"中文";
Единственное, что требуется — сохранить cpp-файл в кодировке UTF8 с BOM. Для этого нужно в меню File->Advances Save Options... выбрать Unicode(UTF-8 with signature) - Codepage 65001 в пункте Encoding.



Что интересно, файлы без BOM некорректно определяются компилятором. Комментарий разработчкиков компилятора можно посмотреть тут (внизу).

четверг, 5 ноября 2009 г.

Ubuntu 9.10 fast review

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


Окно микшера сделали симпатичнее, чем было, но посмотрел я туда не из любопытства, а потому что не заработал звук через S/PDIF->HDMI. Пока не ясно как это чинить.


Зачем-то установился Network Manager, который написал, что не может управлять моими сетями. Оно и к лучшему — сеть работала отлично, как и раньше.


Попробовал новый официальный клиент обмена сообщениями Empathy. Аккаунт импортировался из Pidgin без проблем, но сообщения приходили в неправильной кодировке. Разбираться стало лень, поэтому пока остается Pidgin.

Ожидаемо порадовало наличие gcc версии 4.4.1. На этом пока все и закончилось, т.к. остальное работает без замечаний и без заметных нововведений.

пятница, 23 октября 2009 г.

Deleting incomplete type

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

Дело в том, что стандарт в части 5.3.5/5 разрешает удаление объектов неполностью определенных типов:

If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.
Продемонстрировать проблему можно на следующем простом примере:
struct X; // неполностью определенный тип (определяется позднее)

int main()
{
X* x = 0;
delete x; // стандарт разрешает это

return 0;
}

struct X
{
private:
~X() {}; // private destructor
};
Этот код компилируется несмотря на приватный деструктор, и, очевидно, этот деструктор не вызывается. В этом и есть опасность. Приличные компиляторы выдают предупреждение в этом случае, но ошибка была бы полезнее. И функция SafeDelete обеспечивает выдачу такой ошибки.

Useful templates

В дополнение к предыдущему сообщению ещё пара полезных шаблонов:

//Безопасное удаление
template<typename T>
inline void SafeDelete( T*& p )
{
// Проверка, что тип полностью определен
typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
(void) sizeof(type_must_be_complete);
// проверять p на равенство нулю не нужно, т.к. стандарт
// гарантирует корректную работу delete
delete p;
p = NULL;
}
Если нет запрета на использование boost, то лучше использовать boost::checked_delete.

// Безопасное удаление через Release
template<typename T, typename D>
inline void SafeRelease( T*& p, D d )
{
if ( p != NULL ) {
(p->*d)();
p = NULL;
}
}
template<typename T>
inline void SafeRelease( T*& p)
{
return SafeRelease( p, &T::Release );
}


UPD:22окт добавлена проверка на то, что тип является полностью определенным.