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

Template keyword

Как вы думаете, что нужно написать, чтобы вызвать шаблонную фукнцию шаблонного класса из шаблонной фукнции?

Чтобы было понятно о чем идет речь, рассмотрим конкретный пример:
template<typename T> struct A {
template<int I> void f() {}
};

template<typename TT> void g()
{
A<TT> a;
a.f<3>(); // error is here
}
Код выглядит корректным, однако, строка, где указана ошибка, рассматривается компилятором как выражение со знаком меньше. Интересно, что Visual C++ 2008 догадывается, что там на самом деле (другие компиляторы не настолько догадливы). Однако стандарт С++ говорит о том, что нужно явно указывать компилятору на наличие вызова шаблонной функции там. Об этом говорит пункт 14.2/4. Правильная запись показана ниже:
template<typename TT> void g()
{
A<TT> a;
a.template f<3>(); // correct
}
Слово template выглядит довольно неожиданно в этом месте, что приводит в замешательство не только новичков. Тем более, что Visual C++ игнорирует стандарт по непонятной причине. В будущем стандарте требование ключевого слова остается.

C++'03 Standard 14.2/4:
When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

вторник, 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 некорректно определяются компилятором. Комментарий разработчкиков компилятора можно посмотреть тут (внизу).

А что если мы хотим использовать мультибайтовые последовательности? В этом случае тоже все хорошо — Visual Studio поддерживает директиву execution_character_set начиная с Visual Studio 2010. Для VS2008 существует хот-фикс. Для корректной работы достаточно добавить следующий код в каждый файл:
#pragma execution_character_set("utf-8")

четверг, 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 окт добавлена проверка на то, что тип является полностью определенным.

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

Portable way to shut up compiler warning

Совсем недавно Герб Саттер (Herb Sutter) привел пример портируемого способа запретить вывод предупреждений компилятора о неиспользуемых переменных. Широко используемый способ — определить макрос следующего вида:
#define UNUSED(x) x
Однако, этот способ не работает в некоторых компиляторах (в том смысле, что не убирает предупреждение). Саттер предлагает определить простой шаблон:
template<class T> void ignore( T& ) { }
Можно это определение поместить в неймспейс для избежания конфликтов, чего нельзя сделать с макросом. Следует отметить, что параметр функции ignore<T> следует оставить без имени, чтобы не получить предупреждение теперь уже в другом месте.

Использовать шаблонную функцию довольно просто:
static void Constraints(D* p)
{
  B* pb = p;
  ignore(pb); // portably suppresses the warning
}
От себя хотел бы дополнить, что лучше писать так:
template<class T> void ignore( const T& ) { }
Это необходимо по причинам, которые обсуждались на stackoverflow.com.
UPD:Саттер услышал замечание насчет const и обновил свое сообщение.

воскресенье, 18 октября 2009 г.

Do not use & for arrays

Представьте, что вам попался код следующего вида:
typedef unsigned char Str255[257];
Str255 Blah;

void f( Str255 s )
{
  memcpy(&Blah, &s, sizeof(Str255)); // error here
}

int main()
{
  Str255 x("Blah");
  f( x );
}
Вопрос в том, почему данный код будет работать неверно, т.е. в Blah будет скопировано не содержимое x, а непонятный мусор? Ошибка тут в том, что в memcpy не нужно использовать &, а писать memcpy(Blah, s, sizeof(Str255));. Потому что s и &s — это разные указатели, и они даже имеют разный тип: unsigned char * and unsigned char (*)[257] соответсвенно. Обычно указатель на первый элемент массива и указатель на массив — это одно и тоже, но в нашем случае это не так. Посмотри как выглядит функция f, если не использовать typedef:
void f( unsigned char* s )
{
  memcpy(&Blah, &s, sizeof(Str255)); // error here
}
При вызове этой функции, в соответсвии с пунктом 4.2 стандарта C++, массив будет неявно преобразован в указатель и мы получим, что s указывает на массив с текстом, но &s будет указывать на указатель, который хранится на стеке, т.е. при копировании мы получим содержимое стека в Blah. Чтобы избежать такого недоразумения лучше использовать std::string в C++, ну а в C просто не использовать typedef для строк и понимать что происходит.

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

Best font for programming

Наверное, многие уже слышали о шрифте Consolas. Его особенность в том, что он был разработан с нуля для использования всех преимуществ ClearType. Другие моноширинные шрифты сглаживаются операционной системой и, как правило, смотрятся размыленно со включенным ClearType. Шрифт совершенно бесплатный и может быть скачан тут.

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

Consolas, 10pt:

Lucida Console, 9pt:


Шкриншоты позаимствованы с www.codinghorror.com.

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

Determining 32 vs 64 bit in C++

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

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

template<int> void DoMyOperation();

template<> void DoMyOperation<4>() 
{
  // do 32-bits operations
}

template<> void DoMyOperation<8>() 
{
  // do 64-bits operations
}

int main()
{
  // appropriate function will be selected at compile time 
  DoMyOperation<sizeof(size_t)>(); 

  return 0;
}
В примере выше использован тип size_t, но стандарт говорит только о том, что этот тип должен уметь хранить максимальный размер блока памяти в текущей реализации системы аллоцирования. Строго говоря, это только косвенно говорит о том скольки битная платформа используется, но сложно представить себе реализацию С++, где это будет неприменимо.

среда, 30 сентября 2009 г.

Microsoft Innovation Day осень 2009

Вчера прошел традиционный форум компаний разработчиков Microsoft Innovation Day. На этот раз веб-разработчиков выделили в отдельную секцию, поэтому секций было три. Я посетил техническую.

На технической секции немного рассказали о том, что Windows 7 выходит, но так как об этом и так все знают, то особенно на это не задерживались. Упомянули только, что есть Platform Update для Windows XP и Windows Vista, который добавляет некоторые возможности новой ОС (подробнее...).

Очень поверхностно рассказали о новых продуктах семейства Office 2010, а также SQL Server 2008 R2. В Office ожидается веб варианты компонентов офиса как аналог Google Docs, но доступны они будут через корпоративный Share Point сервер, что и понятно — все-таки они не бесплатные. Это видится как очередной шаг к сервисам в облаке.

В SQL Server 2008 R2 интерес представляет новая технология StreamInsight. Но о ней рассказали не очень подробно и понятно (я так и не понял, можно ли будет хранить видео в БД), однако есть первоисточник где можно почитать об этом подробнее.

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

В конце показали ролик с представлением Microsoft о будущем Office 2019:



Судя по видео у нас в 2019 году будут не whiteboards, а transparentboards, что не очень удобно на мой взгляд :)

пятница, 18 сентября 2009 г.

integer types

Стандарт C'99 ISO подразумевает наличие файла stdint.h с определением типов, которые полезны для портируемых программ или описания форматов сетевых пакетов (типы вроде uint8_t и int64_t и так далее...). Поскольку Visual C++ 2008 не поддерживает полностью стандарт С99, то там этого файла нет. Если же пользоваться этими типами хочется, то можно использовать портируемую версию этого хедера — pstdint.h, которая распространяется под BSD лицензией, т.е. даром.

пятница, 28 августа 2009 г.

boost 1.40

Тихо и незаметно вчера вышла очередная версия библиотеки boost. Список изменений небольшой, в основном затронута библиотека Asio.

Changing wallpaper on Linux programmatically

Для тех, кто хочет начать работать с GNOME, но не знает с чего начать, вот задачка — поменять обои рабочего стола. Это я так пытаюсь подойти к проблеме поддержки разных обоев для двух мониторов (точнее, монитора и HD телевизора в моем случае). В KDE с этим проблем нет, но в GNOME такой настройки нет. Оказалось, что сделать это несложно. Проблемой я собирался заняться давно, но преодолеть лень и написать код меня сподвигнул этот вопрос.

Нам понадобятся: Ubuntu, любимый текстовый редактор, пакет build-essentials, libglib-2.0-dev, libgconf2-dev и консоль. Думаю, что ставить пакеты все умеют (кто не умеет — читать тут).

В текстовом редакторе создаем файл следующего содержания:
// bkgmanage.c
#include <glib.h>
#include <gconf/gconf-client.h>
#include <stdio.h>

typedef enum {
  WALLPAPER_ALIGN_TILED     = 0,
  WALLPAPER_ALIGN_CENTERED  = 1,
  WALLPAPER_ALIGN_STRETCHED = 2,
  WALLPAPER_ALIGN_SCALED    = 3,
  WALLPAPER_NONE            = 4
} WallpaperAlign;

gboolean set_as_wallpaper( const gchar *image_path, WallpaperAlign align )
{
  GConfClient *client;
  char        *options = "none";

  client = gconf_client_get_default();

  // TODO: проверить, что image_path является файлом и вообще существует
  if ( image_path == NULL ) options = "none";
  else {
    gconf_client_set_string( client, 
      "/desktop/gnome/background/picture_filename",
      image_path,
      NULL );
      
    switch ( align ) {
      case WALLPAPER_ALIGN_TILED: options = "wallpaper"; break;
      case WALLPAPER_ALIGN_CENTERED: options = "centered"; break;
      case WALLPAPER_ALIGN_STRETCHED: options = "stretched"; break;
      case WALLPAPER_ALIGN_SCALED: options = "scaled"; break;
      case WALLPAPER_NONE: options = "none"; break;
    }
  }

  gboolean result = gconf_client_set_string( client, 
    "/desktop/gnome/background/picture_options", 
    options,
    NULL);
  g_object_unref( G_OBJECT(client) );

  return result;
}

int main(int argc, const char* argv[])
{
  if ( argc > 1 ) {
    printf( "Setting %s as wallpaper... ", argv[1] );
    if ( set_as_wallpaper( argv[1], WALLPAPER_ALIGN_STRETCHED ) ) printf( "Ok\n" );
    else printf( "Failed\n" );
  } else printf( "Usage: ./bkgmanage <filename>\n" );

  return 0;
}
gconf — это библиотека для управлением аналогом реестра в GNOME. С помощью неё и исправляется параметр /desktop/gnome/background/picture_filename для замены картинки, а также /desktop/gnome/background/picture_options для определения параметров отображения этой картинки. Изменения вступают в силу мгновенно.

Сборка указанного выше файла тоже не составляет труда. Достаточно в консоли ввести:
gcc -Wall -g `pkg-config --libs --cflags glib-2.0 gconf-2.0` bkgmanage.c -o bkgmanage
Тут pkg-config возвращает все необходимые пути к include-файлам и библиотеки, необходимые для линковки, что очень удобно. В результате получается исполняемый файл bkgmanage.

Далее, остается сделать GUI для удобного выбора картинок для каждого монитора с последующей склейкой в один файл. Пока не решил какой фреймворк для этого выбрать — основная идея, что хочется писать на С++, но Qt имеет слишком много зависимостей, которые в GNOME по умолчанию отсутствуют...

четверг, 6 августа 2009 г.

Trigraph sequences in C++

C++ стандарт в части 2.3/1 утверждает, что перед началом любой обработки кода все вхождения трех символов, указанных в таблице, будут заменены на один соответствующий символ.
----------------------------------------------------------------------------
| trigraph | replacement | trigraph | replacement | trigraph | replacement |
----------------------------------------------------------------------------
| ??=      | #           | ??(      | [           | ??<      | {           |
| ??/      | \           | ??)      | ]           | ??>      | }           |
| ??’      | ˆ           | ??!      | |           | ??-      | ˜           |
----------------------------------------------------------------------------
На практике это означает, что если даже в строковой константе встретится такая последовательность, то последствия могут оказаться неожиданными. Например, такой код:
printf( "What??!\n" )
Выдаст пользователю такой текст:
What|
Помимо этого есть такая же таблица для пар символов, которая делает следующий код вполне корректной программой на С++:
%:include <stdio.h>

%:ifndef BUFSIZE
%:define BUFSIZE  512
%:endif

void copy(char d<::>, const char s<::>, int len)
<%
while (len-- >= 0)
<%
d<:len:> = s<:len:>;
%>
%>
Стандарт поддерживает эти символы для совместимости, т.к. на очень старом железе в Европе не было части символов. Интересно, что в будущем стандарте С+x0 было предложено отказаться от поддержки триграфов, однако, этого видимо не произойдет. Подробнее тут.

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

понедельник, 13 июля 2009 г.

работаем с Windows Registry

Для восстановления веток реестра в Windows существует функция RegRestoreKey. До Windows Vista ей можно было свободно пользоваться для восстановления, например, настроек по умолчанию программы. В Windows Vista это стало требовать поднятия уровня процесса(elevation) с появлением назойливого диалога UAC. Конечно, понятно, что большинство читающих эту статью живут с отключенным UAC, но подавляющее большинство обычных пользователей(по данным Microsoft) UAC не выключают. У RegRestoreKey есть ещё один недостаток — она работает с файлами в бинарном формате, который отличается для каждой новой версии ОС.

Однако, существует в Windows и программа reg.exe, которая имеет встроенный парсер REG-фалов. Да, именно тех, которые описывают ключи в обычном тектовом формате. Команды import и export этой утилиты не требуют каких либо повышенных прав. Конечно, здесь речь идет о разделах HKCU. Почему эти функции не выделили в отдельный API остается загадкой.

Ниже приведен пример использования этой утилиты в программе:
// Получаем указатель на REG файл прилинковынный в ресурсы
HRSRC hRsrc = ::FindResource( NULL, MAKEINTRESOURCE(IDR_DEFSETTINGS), L"REG" ); // IDR_DEFSETTINGS - идентификатор ресурса с файлом. Может быть другим.
HGLOBAL hGlobal = ::LoadResource( NULL, hRsrc );
LPVOID lpReg = ::LockResource( hGlobal );

// Сохраняем во временный файл
wchar_t path_buf[MAX_PATH];
SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, &path_buf[0] );
const wstring temp_filename = path_buf+L"\\temp_reg.reg";
ofstream os( temp_filename.c_str(), ios_base::binary );
os.write( static_cast<char*>(lpReg), ::SizeofResource( NULL, hRsrc ) );
os.close();

// Готовим параметры reg.exe.
wstring params( L"import \"");
params += temp_filename;
params += L"\""; // Путь к файлу должен быть в кавычках на случай пробелов

// Запускаем reg.exe
SHELLEXECUTEINFO se = {0};
se.cbSize = sizeof SHELLEXECUTEINFO;
se.fMask = SEE_MASK_NOCLOSEPROCESS|SEE_MASK_UNICODE;
se.hwnd = GetSafeHwnd();
se.lpFile = L"reg.exe"; // находится в system32, поэтому путь указывать необязательно
se.lpParameters = params.c_str();
se.nShow = SW_HIDE; // Смотреть на консольку пользователю ни к чему
if ( ShellExecuteEx( &se ) ) {
WaitForSingleObject( se.hProcess, INFINITE ); // хотя тут и написано INFINITE, но в реальной жизни лучше указать таймаут
CloseHandle( se.hProcess );
} else {
// Ошибка запуска. Как-то обрабатываем эту ситуацию.
}

// Удаляем временный файл
_wremove( temp_filename.c_str() );

вторник, 7 июля 2009 г.

Windows Intenals 5-th


Наконец-то вышло пятое издание Windows Internals и уже почтальон везет его ко мне :) Книга потолстела на 250 страниц, как пишет Марк Руссинович(Mark Russinovich) у себя в блоге. Были добавлены главы посвященные Hyper-V, Kernel Transaction Manager, Code Integrity, Thread Pools, Mandatory Integrity Controls, Windows Driver Framework и ещё немного об утилитах SysInternals. До конца года обещается 6-е издание, которое пополнеет ещё на 100 страниц и будет содержать информацию специфичную для Windows 7 и Windows Server 2008 R2.

четверг, 2 июля 2009 г.

hex to long

Как думаете, чем конвертировать шестнадцатиричную строку в 32-х битное знаковое целое? Порывшись в стандарной библиотеке можно найти функцию strtol. И все работает обычно... до тех пор пока входные данные беззнаковые. Попробуйте конвертировать -1 (0xFFFFFFFF) и ничего не выйдет. strtol решит, что было переполнение и вернет MAX_LONG. Чтобы обойти эту особенность, следует использовать функцию strtoul. Она конвертит строку в беззнаковое целое, а уж потом можно его приравнять к long и старший бит укажет знак.

Пример:
#include <cstdlib>
#include <iostream>
using namespace std;

int main() {
string s = "FFFFFFFF";
char * p;
long n = strtoul( s.c_str(), &p, 16 );
// overflow when using long n = strtol( s.c_str(), &p, 16 );
if ( * p != 0 ) {
cout << "not a number" << endl;
} else {
cout << n << endl; // prints -1
}
}

четверг, 25 июня 2009 г.

7 (Seven)

Был сегодня в Microsoft'е. Из интересного — сертификация под Windows 7 теперь бесплатна. Как получить лого за один день(при должном упорстве) можно посмотреть тут.

Еще выдали интересные книжки «Обеспечение совместимости приложений с Windows 7 — пособие для разработчиков» (PDF версия) и «Обеспечение совместимости приложений в Microsoft Windows Vista и Windows 7 — Руководство для IT-специалистов» (PDF версия).

По мелочам: VS2010 Beta 2 выйдет до конца года, а Windows 7 уже проходит сертификацию в силовых ведомствах.

среда, 24 июня 2009 г.

Немного ссылок

Не все программисты умеют рисовать. Для них существует www.iconfinder.net. Большинство иконок там используют лицензии GPL, LGPL, CC или Free. Можно задать поиск по ключевым словам и размерам.

Ещё можно искать картинки похожие на те, что где-то увидели. На сайте tineye.com можно загрузить свою картинку и найти похожие с лучшим качеством. Похоже, что поиск работает.

суббота, 20 июня 2009 г.

Семинар Microsoft

Microsoft проводит последний(до выхода Windows 7) однодневный семинар посвященный обсуждению вопросов обеспечения совместимости приложений. Семинар пройдет в Москве 25-го июня. По ссылке можно ещё успеть зарегистрироваться.

x or X and stackoverflow.com

Некоторое время назад я писал о том как можно проверить наличие той или иной переменной в структуре или классе. Оказалось, что оба описанных метода некорректны с точки зрения стандарта. Решение было получено на страницах сайта stackoverflow.com, который был организован Джоелом Спольски(Joel Spolsky) и Джефом Атвудом(Jeff Atwood). Само решение можно посмотреть тут.

Рекомендую обращаться к страницам указанного сайта тем, кто этого ещё не делает. Там бывают интересные вопросы и ответы.

вторник, 9 июня 2009 г.

Хозяйке на заметку

Следующий метод отладки имеет весьма узкое применение и в большинстве случаев может оказатся неподходящим. Описанное решение пришло в результате поиска проблемы в чужом коде, написанном в ужасном стиле (смесь С++ и С). Речь пойдет о защите памяти с помощью функции VirtualProtect. Эта функция позволяет изменить режим доступа к страницам памяти: только чтение, копирование по записи, полное отсутсвие доступа и далее. Полный список флагов можно найти в документации.

Для целей отладки интересен флаг PAGE_NOACCESS. Он позволяет ловить запись или, что более важно, чтение за пределами некоторой валидной области памяти. Для удобного применения VirtualProtect был создан следующий класс:
struct protect_mem_t {
    protect_mem_t(void* addr, size_t size) : addr(addr), size(size), is_protected(FALSE) {
        protect();
    }
    ~protect_mem_t() { release(); }
    BOOL protect() {
        if (!is_protected) {
            is_protected = VirtualProtect(addr, size, PAGE_NOACCESS, &old_protect);
        }
        return is_protected;
    }
    BOOL release() {
        if (is_protected)
            is_protected = !VirtualProtect(addr, size, old_protect, &old_protect);
        return !is_protected;
    }

protected:
    void*   addr;
    size_t  size;
    BOOL    is_protected;
    DWORD   old_protect;
};

Применять его можно следующим образом:
const size_t PAGE_SIZE = 4096;

int main()
{
    const size_t var_size = 1024 * 96; // 1024*96 - некоторый размер. Конкретное значение здесь только в качестве примера.
    const size_t guard_size = PAGE_SIZE * 2 - var_size%PAGE_SIZE;
    __declspec(align(4096)) char some_var[var_size + guard_size];
    protect_mem_t guard(&some_var[var_size], PAGE_SIZE);

    some_var[5] = 20;   // that's ok
    char x = some_var[5];   // that's ok

    char y = some_var[1024 * 96 + 1024]; // access violation

    return 0;
}

В приведенном примере, как и в реальной программе, была добавлена защитная область памяти прямо на стеке. Удачной отладки.

пятница, 29 мая 2009 г.

Бывет и такое...

Казалось бы проблемы из-за сравнения знаковых и беззнаковых значений давно известны и обходятся опытными разработчиками. Однако, разработчики компилятора Microsoft C++, который входит в состав Visual Studio 2008 SP1 сделали такую ошибку, которая и была (не без удивления) обнаружена у нас в компании. Код, который позволяет воспроизвести эту ошибку приведен ниже:
#include <iostream>
using namespace std;

int main()
{
for ( int k = -4; k < -1; k++ ) {
unsigned long kkk = (k+4)*8+1;
bool jjj = kkk < 256;
if ( jjj == false ) cout << "Impossible!!!" << endl;
}
}
Если скомпилировать такой код с ключом /O2(cl /EHsc /O2 main.cpp), то будет выведен текст, который не должен быть выведен. Если без ключа оптимизации(cl /EHsc main.cpp), то текст не будет выведен.

Проблема тут в том, что компилятор оптимизурет выражение (k+4)*8+1 < 256 до k < 28, забывая что k может принимать отрицательные значения и результат сравнения проверяет командой, которая знак не учитывает.

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

пятница, 22 мая 2009 г.

ordered_unique vs hashed_unique

Контейнер multi_index библиотеки boost поддерживает несколько типов индексов. Среди них есть два очень похожих — ordered index и hashed index. Разница ясна из названия, hashed index не гарантирует какого-либо порядка сортировки, что, по идее, делает его несколько быстрее. Именно это и утверждается в документации: «they provide much faster lookup of elements, at the expense of losing sorting information». Что означает «much faster» далее не раскрывается.

Для того, чтобы выяснить эту разницу в скорости, можно создать следующую структуру, которая будет содержать несколько полей. Эти поля в дальнейшем будут использованы в индексации:
struct container_t {
complex_elem_t      hashed_index;
complex_elem_t      ordered_index;

explicit container_t() : hashed_index(), ordered_index() {};
};

Тип complex_elem_t введен для простой генерации уникальных элементов. Этот класс состоит из 5-ти полей типа short int, что позволило сгенерировать 10 млн. элементов без коллизий. В нем присутствуют операторы == и <. Также пришлось создать функцию hash_value, т.к. boost не умеет считать хэш для таких сложных структур.

Тип контейнера имеет следующий вид:
typedef multi_index_container<
container_t,
indexed_by<
hashed_unique< member<container_t, complex_elem_t, &container_t::hashed_index> >,
ordered_unique< member<container_t, complex_elem_t, &container_t::ordered_index> > >
> containers_t;

В своих тестах я заполнял контейнеры элементами количеством от 1 до 10млн. штук. Для каждого производилось несколько поисков и результат усреднялся. Для замеров времени использовалась инструкция процессора rdtsc. Полностью тестовую программу можно скачать тут. Для построения графиков использовался OpenOffice.


На графиках видно, что поиск в ordered index происходит за логарифм, а в hashed index за константу. По X показан размер контейнера, по Y — нормированное количество тактов процессора (измерения проводились на Core2Duo E6420).

Так что, если какой-либо порядок элементов не требуется, то имеет смысл использовать hashed индекс, вместо ordered.

UPD: В первый раз не учитывались коллизии при вставке элементов и, поэтому, старый график был верен только до отметки в ~180 элементов из-за парадокса дней рождения.

воскресенье, 17 мая 2009 г.

Мир IT кардинально меняется?

В ответ на вопрос Олега о том, что "Мир IT кардинально меняется."

Мы разрабатываем системы безопасности. На первый взгляд - это, как правило, изолированное решение на промышленных серверах и никаких "облаков". Однако, мы уже рассматривали возможность работы в Windows Azure, поэтому далее речь об облаках от Microsoft.

Уже сейчас можно отметить несколько моментов:
1. Дата центров в России с такой ОС пока нет. Постройка самого дата центра стоит половину миллиарда долларов. Всего дата центров у Микрософт около 20 и новых они вроде пока не планируют строить. А при повсеместном переходе "в облака" дата центров нужно будет очень много. Все это займет явно больше 5 лет.
2. Цена интернета должна быть сопоставима с ценой электричества. Ну все к этому идет, правда не только со стороны уменьшения цены интернета.
3. Цена аренды сервиса должна быть очень низкой. Объявленная Микрософтом оказалось предварительно вполне приемлемой.

Первый пункт самый нереалистичный. Несмотря не него, мы уже движемся в сторону "облачных" вычислений и, когда они будут реальны, мы будем готовы. Имеется ввиду, что вся предварительная обработка первичных данных будет идти на специализированном железе, которое работает без ОС или с Embedded версиями. А пост обработка может производится "в облаке", что позволит сократить траты на обслуживание и расширение вычислительных центров. И поддержка готового продукта будет проще без сомнений. Аналогичная схема подойдет для большинства IT-проектов. Распространение "облаков" для IT-компаний видится вовсе не плохим трендом, кроме тех, которые продают вторичные услуги.

Так, опасения писателей антивирусов можно сравнить с опасением автотехцентров из-за того, что автомобили перестанут ломаться. Антивирусы вторичный продукт. Пользователи покупают компьютеры, не для того, чтобы вирусы ловить, также, как и автомобили не для того, чтобы ездить в сервис центры. Поэтому панику не стоит разводить :)

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

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

А с C++ ничего плохого не случится. Уже сейчас Microsoft продолжила развитие MFC и планирует развивать его в Windows 7. И в Azure он тоже никуда не денется. Т.е. от идеи повсеместного перехода на C# отказались. Ну и сам С++ не стоит на месте. А разработчика остается развиваться вместе с технологиями и все будет в порядке.

среда, 6 мая 2009 г.

Другие стороны boost::format

Библиотека boost::format предоставляет удобный и безопасный(по сравнению с printf) способ форматирования строк. Возможен как классический printf-стиль:
cout << format("writing %s,  x=%s : %d-th step \n") % "toto" % 40.23 % 50;
Так и другие, более гибкие способы форматирования, как, например, такой:
cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style.
Самая очевидная проблема с printf — это то, что мы должны точно указать размер каждого аргумента. Например, для time_t в одних случаях писать %I64d, а в других %d. У time_t разный размер может быть и на 32-х битной платформе в зависимости от компилятора. И это только один пример. С boost::format такие проблемы исключаются.
Подробнее по всем возможностям можно посмотреть в документации. Но речь сейчас не о возможностях boost::format , а о том как их применить у себя в программе. Мне кажется, что очень удобно было бы, например, писать информацию в лог файл без предварительного форматирования строки на стеке. Т.е. писать вместо:
string msg = str( boost::format("Debug message %s") % some_msg_string );
log( msg );
вот такую строку:
log("Debug message %s") % some_msg_string;
Добиться этого не сложно. Нужно, всего лишь, перегрузить оператор %. Для этого создадим класс formatted_log_t. А ещё функцию log, которая возвращает экземпляр этого класса. Класс будет содержать в себе экземпляр boost::format.
#include <sstream>
#include <boost/format.hpp>
#include <iostream>
using namespace std;

class formatted_log_t {
public:
  formatted_log_t(const wchar_t* msg ) : fmt(msg) {}
  ~formatted_log_t() { wcout << fmt << endl; }

  template <typename T>
  formatted_log_t& operator %(T value) {
    fmt % value;
    return *this;
  }

protected:
  boost::wformat                fmt;
};
Оператор % должен быть шаблонным так как мы не знаем типы форматируемых аргументов. Функция log будет иметь следующий вид:
formatted_log_t log(const wchar_t* msg)
{
  return formatted_log_t( msg );
}
На этом месте реализация функции уже является вполне работоспособной. Далее можно добавлять какие-то другие фичи. Например, фильтр по уровню сообщений. Для этого сделаем функцию и класс шаблонными. Вот так:
#include <sstream>
#include <boost/format.hpp>
#include <iostream>
using namespace std;

enum log_level_t {
  LOG_NOTHING,
  LOG_CRITICAL,
  LOG_ERROR,
  LOG_WARNING,
  LOG_INFO,
  LOG_DEBUG
};

template<int level>
class formatted_log_t {
public:
  formatted_log_t(const wchar_t* msg ) : fmt(msg) {}
  ~formatted_log_t() {
  // GLOBAL_LEVEL is a global variable and could be changed at runtime
  if ( level <= GLOBAL_LEVEL ) wcout << fmt << endl;
}

template <typename T>
formatted_log_t& operator %(T value) {
  fmt % value;
  return *this;
}

protected:
  boost::wformat                fmt;
};

template <int level>
formatted_log_t<level> log(const wchar_t* msg)
{
  return formatted_log_t<level>( msg );
}
Применять этот код можно так:
int main ()
{
  log<LOG_DEBUG>(L"TEST %3% %2% %1%") % 5 % 10 % L"privet";
  return 0;
}
Работа с логом, наверное, самое очевидно, но только одно из возможных применений синтаксиса boost::format и далее можно развивать эту идею как вам больше нравится.

суббота, 25 апреля 2009 г.

Доставка из Китая

Искал аккумулятор для ноутбука в Москве. Оказалось, что цена его в районе $120. Ну плюс доставка около $20. Нашел аналог в США. Цена $45 с доставкой. Доставляют сразу из Китая, т.к. оттуда ближе, чем из США пересылать. Доставка в этой сумме составила около $5. Это получается, что из Китая доставка в 4 раза дешевле, чем из Москвы в Подмосковье...

понедельник, 20 апреля 2009 г.

Tracepoints

В Visual Studio 2008 есть такая удобная штука — «tracepoint». Из названия ясно, что в определенной точке программы можно вывести какое-либо сообщение. А удобство в том, что не нужно менять код, чтобы добавить отладочные трейсы. Создается такая точка также как breakpoint, через меню по правой кнопке:

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

Можно выводить значения переменных и прочую информацию(возможностей достаточно) не меняя код программы и не перекомпилируя.

У меня возникла только одна проблема: обрезаются очень длинные текстовые переменные. Пока идея только в том, чтобы в точке вызывать макрос и в нем выводить текст — не знаю сработает или нет.

UPD: В макросе тоже текст обрезался до длины в 200 символов. Но в Text Visualizer отладчика показывает полностью, значит способ есть...

пятница, 17 апреля 2009 г.

Окно на нужном мониторе

У меня два монитора.

У других разработчиков, вероятно, был только один. И поэтому мне, как пользователю приходится страдать. Проблема вот в чем: если разработчик не позаботился о нервах пользователя, то все всплывающие окна будут показываться на основном мониторе. Если окно программы находится на втором(третьем и т.д.) мониторах, то курсору мыши приходится преодолевать огромные расстояния, чтобы нажать какую-либо кнопку на всплывающем окне. Ну и головой вертеть тоже неприятно.
При этом избежать проблемы очень просто. Нужно создавать окна на том мониторе, где находится курсор мыши. Следующая функция поможет вывести окно по центру монитора, на котором пользователь оставил курсор:
RECT WherePlaceWindow( long win_width, long win_height )
{
    POINT point = { 0 };
    MONITORINFO mi = { sizeof(MONITORINFO), 0 };
    HMONITOR hMonitor = 0;
    RECT rcArea = { 0 };

    ::GetCursorPos( &point );
    hMonitor = ::MonitorFromPoint( point, MONITOR_DEFAULTTONEAREST );
    if ( ::GetMonitorInfo( hMonitor, &mi ) ) { // тут GetMonitorInfo сработало
        rcArea.left = ( mi.rcMonitor.right + mi.rcMonitor.left - win_width ) /2;
        rcArea.top = ( mi.rcMonitor.top + mi.rcMonitor.bottom - win_height ) /2;
    } else { // если не сработало, то придумает что-то другое
        ::SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcArea, NULL);
        rcArea.left = (rcArea.right + rcArea.left - win_width)/2;
        rcArea.top = (rcArea.top + rcArea.bottom - win_height)/2;
    }  

    // теперь rcArea.left и rcArea.right содержат координаты левого верхнего угла окна
    return rcArea;
}
Основная идея в том, чтобы найти монитор с помощью функции MonitorFromPoint, а затем получить его координаты на десктопе. Для тех мониторов, которые левее и/или выше основного, координаты будут отрицательными, поэтому нужно быть готовым к отрицательным координатам окна везде в программе.

пятница, 10 апреля 2009 г.

reintegrate merge и сборка Subversion на Ubuntu Hardy

В Subversion с версии 1.5 есть функция reintegrate, которая позволяет влить изменения бранча в основную ветку. У нас репозитарий находится на сервере под управлением Ubuntu 8.04 LTS, а там Subversion версии только 1.4.
Выяснилось, что в backports есть версия 1.5, но установка её не привела к работе указанной функции, поэтому пришлось собрать Subversion из сорсов. Сделать это просто, но не тривиально.

Первым делом нужно скачать сорсы. Находятся они на subversion.tigris.org. Далее нужно установить все необходимое для сборки. Проще всего это сделать через apt-get. Но для начала нужно включить в репозитории сорсов через Synaptic, как на рисунке ниже:


Или поправив файл sources.list, добавив туда строки deb-src, например так:
deb-src http://ru.archive.ubuntu.com/ubuntu/ hardy restricted main
deb-src http://ru.archive.ubuntu.com/ubuntu/ hardy-updates restricted main
deb-src http://ru.archive.ubuntu.com/ubuntu/ hardy-security restricted main

После этого можно устанавливать зависимости следующей командой:
apt-get build-dep subversion

Дальше переходим в каталог сорсов и пытаемся выполнить ./configure, но не все так просто, если хочется использовать Berkeley DB. В arp-utils есть баг, который не дает apu-config выдать с какой версией Berkeley DB она слинкована. Решается это созданием скрипта apu-config в папке с configure следующего содержания:
if [ "$1" == "--libs" ]; then
echo -ldb-4.6
exit
else
apu-config $1
fi

Файл нужно сделать исполняемым командой chmod +x apu_config. Затем можно запускать configure командой:
./configure --with-apr-util=. --prefix=/usr

Дальше make install и получим нужно версию Subversion. Ещё необходимо выполнить svnadmin upgrade на каталоге с репозитарием. После этого можно использовать все новые функции Subversion (не забыв обновить клиентскую часть, конечно).

пятница, 3 апреля 2009 г.

Google street view

Какое-то время назад у Google появилась возможность на карте спустится до уровня улицы и смотреть по сторонам как будто вы стоите на земле. Хотя разработка этой фичи ведется с 2007 года, я заметил её совсем недавно. Функция Street View позволяет прогуляться вдоль домов или полей. Карта Америки снята очень подробно, можно побывать практически везде. Ещё они поездили по Австралии, Японии и некоторым странам Европы. В России они ещё не побывали. Очень захватывающе надо сказать...

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



UPD: Кстати, вот ссылка на набережную в Ницце.

понедельник, 23 марта 2009 г.

Qt Creator 1.0.0

Некоторое время назад вышла версия 1.0.0 среды разработки Qt Creator IDE. Раз уж разработчики объявили такую версию, то уже стоит попробовать. Пробовать будем на Ubuntu. Ставит продукт собственная инсталяшка в директорию /opt по умолчанию, поэтому без админских прав не обойтись. Ставим так:
sudo ./qt-sdk-linux-x86-opensource-2009.01.bin --installer-language ru
Если язык не указать, то установка будет проходить на английском языке. Сразу же предлагается выбрать директорию назначения и ещё можно выбрать ставить ли дополнительные библиотеки. Я все оставил по умолчанию и процесс пошел...


Ставится сравнительно недолго, в конце предлагая сразу и запустить. Я отказался, помня что установка работает с админскими правами. На десктопе появляется ярлык, по нему и запускаем установленную среду от фирмы Nokia (о чем сообщает нам экран приветствия):


Попробуем создать проект. Выбор предлагается небольшой, но оно и понятно — среда предназначается для разработки Qt приложений, а не чего попало. Выбираем Qt4 GUI, отвечаем на несложные вопросы, и заготовка проекта готова к сборке:


Только подумал, что все неплохо работает, но тут же сборка завершилась с ошибкой. Оказывается нужно ставить библиотеки дополнительно, хотя Qt4-dev и шли в составе SDK. Мне понадобилось доустановить следующие пакеты:
sudo apt-get install libfreetype6-dev libglib2.0-dev libsm-dev libxrender-dev libfontconfig1-dev
Уже после этого сборка прошла до конца и можно запустить программу. Пустой диалог появился, что и требовалось для начала.

Далее попробовал что-то за пределами визарда. Заменяем код в main на следующий:
int main(int argc, char *argv[])
{
std::wstring buf( L"Test string" );
buf = L"Строка на русском";

return buf.size();
}
Отладчик почему-то не захотел показывать переменную buf. Хотя позже выяснилось, что std::string показывается нормально и содержимое его тоже. И даже содержимое сonst wchar_t* показывается, но только позорными числами, а не как строка.

Это очень плохо и уже на этом этапе можно сделать вывод, что пользоваться этим средством будет очень трудно. Кстати, и документация поставляется в комплекте только по Qt, но не по С++. Эта проблема, возможно, решается в коммерческой версии продукта (а я ставил ту, что дается даром), но об этом мне ничего неизвестно. Если даже простейший эксперимент вскрывает такие проблемы, то страшно подумать что будет, если копнуть глубже.

Подытоживая сказанное можно заявить, что пока ещё рано использовать это средсво для чего-то более серьезного, чем «Hello world» программы. Ждем следующюю версию...

четверг, 19 марта 2009 г.

Format day of week as string

Неожиданно столкнулся с проблемой, что в Windows нет(или просто я не нашел) тривиального способа получить список всех дней недели в определенной локали. Первым делом, конечно, выбор пал на функцию strtime. Код получился вот такой:
 setlocale( LC_ALL, ".1251" ); 
struct tm tm_temp = {0};
wchar_t day_buf[50];
for ( int n = 0; n < 7; ++n ) {
tm_temp.tm_wday = n;
wcsftime( day_buf, 50, L"%A", &tm_temp );
wcout << day_buf << endl;
}
Тут вроде все как нужно за тем исключением, что setlocale принимает не LCID. В Windows есть свои NLS функции. Попробуем их — получается что-то такое:
 // LCID langid где-то определен
SYSTEMTIME sys_time = {0};
wchar_t day_buf[100] = {0};
for ( int n = 0; n < 7; ++n ) {
sys_time.wDayOfWeek = n;
GetDateFormat( langid, 0, &sys_time, L"dddd", day_buf, 100 );
wcout << day_buf << endl;
}
Однако, GetDateFormat не смотрит на параметр wDayOfWeek. Пришлось взять дату, в которую было воскресенье и от неё выводить дни. Как-то так:
 // LCID langid где-то определен
SYSTEMTIME sys_time = { 2009, 3, 0, 15, 0, 0, 0, 0 };
wchar_t day_buf[100] = {0};
for ( int n = 0; n < 7; ++n ) {
int res = GetDateFormat( langid, 0, &sys_time, L"dddd", day_buf, 100 );
wcout << day_buf << endl;
sys_time.wDay++;
}
Таким образом можно получить список дней недели на нужном языке. Может есть способ попроще?

суббота, 14 марта 2009 г.

Прошел Microsoft Innovation Day в Москве

Закончился Microsoft Innovation Day в Москве. Целый день представители Microsoft рассказывали о программах партнерства, сертификации продуктов под Windows Vista, Server 2008 и Windows 7. Из интересной информации по операционкам стоит отметить, что Windows Server 2008 R2 с ядром Windows 7 будет выпускаться только в варианте 64-бит. Нам дают однозначно понять, что эра 32-бит программ подходит к концу. Что касается партнерских программ и сертификации, то нам весь день напоминали, что вся основная информация по этим темам собрана на сайте InnovateOn.com, в том числе на русском языке.

Вся техническая сессия проходила в рассказах о новый фичах Windows 7. Основная «фича» — это планомерный отказ от форматов PostScript и PDF в сторону XPS и ребята из Microsoft пообещали, что PDF со временем уйдет в прошлое. Уже ведутся переговоры с производителями принтеров и прочей техники для обеспечения аппаратной поддержки XPS.

Про остальные новинки Windows 7 уже много писали и все они, в общем, были в очереной раз показаны в выступлениях докладчиков. А выступали на технической сессии Алексей Федоров, Георгий Баркан и Антон Шепитько. Все презентации показывались с нетбука под управлением новой ОС. Хотел бы отметить новую функцию поиска, Federated Search, которая позволит искать файлы в сети таким же способом как и на своем компьютере, и будет реализована на стандарте Open Search 1.1. А также, что предлагается новое API на замену GDI и GDI+ — Direct2D и DirectWrite. Это вместо когда-то заявленного аппаратного ускорения GDI+, которое так и не было реализовано, будет ускорение вывода картинок и сглаженного текста уже на Direct2D и DirectWrite.

Много говорилось о Cloud Computing и немного об ОС Windows Azure, которая будет доступна только в датацентрах Microsoft. А в России такой строить не собраются, т.к. стоит он 500 млн. долларов, а оборот Microsoft в России только недавно перевалил за 1 миллиард тех самых долларов. Возникает вопрос — зачем было про неё рассказывать? А затем, что докладчики упомянули, как бы неофициально, что возможно будет организовавыть датацентры в рамках предприятия и на них запускать Azure.

Весь день доклады шли точно по часам, но под конец сессия вопросов-ответов задержались более, чем на час, а народ не спешил уходить. Вопросов задавали не много, что не мешало докладчикам много отвечать о совместимости Windows 7 с прошлыми операционками. Отметили интересный факт, что 54% проблем совместимости связано с проверкой разработчиками версии ОС на строгое равенство пяти и отказом запускаться под любой другой версией (даже под более новой). Поэтому, в Windows 7, на всякий случай, не стали менять major версию ОС, а оставили равной 6. Ещё рассказали о том, как Windows 7 справляется сама с проблемами совместимости, но это скорее полезная информация для пользователей, чем для разработчиков, т.к. разработчики должны писать код, который и так совместим.

В качестве заключения: читателям, наверное, интересно будет узнать, что Windows 7 все-таки выйдет уже осенью, по крайней мере в 32-битном варианте. И хотя официально это и не заявлялось, но постоянно намекалось.

вторник, 10 марта 2009 г.

Windows Genuine Advantage в Firefox

Качал что-то с сайта MSDN и заметил, что проверка подлинности ОС прошла вполне себе нормально с установкой плагина, при том что браузер был Firefox. Зашел в плагины и увидел там вот что:

Получается, что теперь в Microsoft признают, что пользователи Windows могут браузить интернет не только из IE, и сами пишут плагины к Firefox...

четверг, 5 марта 2009 г.

Ночные сборки

В отличии от Visual Studio 2005, в версии 2008 появилась возможность собирать sln файлы из командной строки не загружая IDE. Ранее, конечно, можно было собрать солюшен из командной строки, но IDE все равно запускалось хотя и было скрыто. Но Wine не обманешь простым сокрытием окна — запуск студии не удавался.

В Visual Studio 2008 такое удобное средство как MSBuild наконец смогло собирать солюшены(sln файлы) для С++ проектов. Это позволяет организовать так называемые «nightly builds» на сервере под управлением Ubuntu Server. Это у меня такой сервер и сборка там была в VirtualBox, а теперь, в связи с открытием новых возможностей, в планах есть пункт сделать под Wine. Предполагается, что такой способ будет потреблять меньше ресурсов сервера, чем вторая операционка... А тем временем, под Windows сборка производится одним кликом по файлу rebuild_all.cmd:
@call "C:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" x86
@msbuild All90.sln /t:Rebuild /p:Configuration="Unicode Release"
Думаю, что все параметры понятны без дополнительных объяснений.

У нас сборка, помимо самой сборки, включает в себя автоматизированное сливание с SVN стабильной версии(ну т.е. разработчики думают, что там что-то стабильное и помечают такие ревизии меткой STABLE). Затем производится обновление всех rc и rc2 файлов проектов, а их много, поэтому тоже автоматизированно. В версиях проекта проставляется последняя цифра — это как раз номер ревизии проекта(по сути ревизия каталога, где лежит vcproj файл), и полностью меняется версия продукта, а также всякие копирайты и т.п.

После сборки все dll, exe и pdb заливаются на Symbol Server(что позволяет позже автоматически разбирать дампы программ), после чего продукт доступ для тестирования в сыром виде.

вторник, 3 марта 2009 г.

Microsoft Innovation Day

Пришло приглашение из Microsoft посетить их Форум компаний-разработчиков. Пройдет он 13 марта 2009 г. в Москве по адресу: гостиница «Radisson SAS Славянская» (пл. Европы, д. 2, ст. м. «Киевская»).

Форум традиционно будет разделен на две части: техническую и бизнес-секцию. На технической части будут освещены новые возможности Windows 7, вопросы совместимости решений компаний-разработчиков с клиентскими операционными системами Microsoft, а также будет рассмотрена web-платформа Microsoft(Silverlight?); на бизнес-секции — новая информация о партнерских программах для компаний-разработчиков и возможностях по созданию и продвижению совместных продуктов с Microsoft.

Участие в мероприятии бесплатно. Зарегистрироваться можно по ссылке:
http://www.microsoft.com/rus/events/detail.mspx?eventid=1032405960

Я уже зарегистрировался и буду на технической части.

понедельник, 2 марта 2009 г.

Давно про компилятор не писал

Давно про компилятор не писал, который в состав Visual Studio входит. Но там все идет как и раньше. Я уже не обращаю внимания на то, что компилится код вроде такого:
#include <stdio.h>
int main()
{
int x = (enum Fff)1;
enum Fff { dx, dy };
return 0;
}
В GCC выдется ошибка, но мистер Кейвс (Jonathan Caves) из Visual C++ Compiler Team весьма нервно отнесся к моим аппеляциям к GCC при демонстрации того, что в VC++ ошибка и как все должно работать, поэтому я уже не отсылаю такого рода репорты.

А вот код вроде того, что приведен ниже, уже вызывает интерес, хотя формально и не имеет ошибок:
class MyClass
{
public:
MyClass() {};
MyClass( int ) {};
};

int main()
{
MyClass (x);
MyClass (0);
return 0;
}
Получилось что-то вроде этого в результате опечатки — хотелось объявить переменную x и инициализировать её чем-то. Почему это скомпилировалось вообще? Коллега предположил, что должен вызываться конструктор MyClass и инициализироваться x-ом, однако, это не так. Это все то же объявление переменной x. Мне тут хотелось бы получать предупреждение на этапе компиляции, потому что я так и не придумал зачем может понадобиться объявлять переменные в таком синтаксисе.

А настоящая программа скомпилировалась и упала в рантайме, ошибка была сразу найдена, но лучше до рантайма не доводить imho.

Кстати, как вызвать конструктор MyClass, создав объект на стеке? Во втором случае так и будет, но имеется ввиду без константы. Ну все так-же как обычно — MyClass (x)(y).

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

Антикризисное средство поиска утечек памяти

При современном подходе к программированию на языке C++ довольно просто избежать всех проблем связанных с утечками памяти. Аллокаторы, в общем, неплохо заботятся о пямяти — тут вам и автоудаление, и слежение за фрагментацией (например, boost::pool_alloc), и прочие радости. Однако, иногда попадается код, где выделение памяти организовано плохо(с ошибками, а значит плохо).

В составе Debugging Tools for Windows присутствует утилита umdh (user-mode dump heap). Пользоваться этой тулзой довольно просто. Сначала делается пара дампов состояния памяти с помощью команд:
umdh.exe -p:3804 -f:p:\result1.log
umdh.exe -p:3804 -f:p:\result2.log
, где 3804 — идентификатор процесса (его можно посмотреть в task manager). Потом можно сравнить эти два состояния следующей командой:
umdh.exe p:\result1.log p:\result2.log -f:p:\final.log
Самое интересное начинается при сравнении. umdh умеет подгружать pdb файлы из путей, определенный переменной _NT_SYMBOL_PATH (как и все прочие тулзы в Debugging Tools).

Результат сравнения позволяет посмотреть стек и количество выделенных байт. Выглядит это так:
// Each log entry has the following syntax:                                 
//                                                                          
// + BYTES_DELTA (NEW_BYTES - OLD_BYTES) NEW_COUNT allocs BackTrace TRACEID 
// + COUNT_DELTA (NEW_COUNT - OLD_COUNT) BackTrace TRACEID allocations      
//     ... stack trace ...                                                  
//                                                                          
// where:                                                                   
//                                                                          
//     BYTES_DELTA - increase in bytes between before and after log         
//     NEW_BYTES - bytes in after log                                       
//     OLD_BYTES - bytes in before log                                      
//     COUNT_DELTA - increase in allocations between before and after log   
//     NEW_COUNT - number of allocations in after log                       
//     OLD_COUNT - number of allocations in before log                      
//     TRACEID - decimal index of the stack trace in the trace database     
//         (can be used to search for allocation instances in the original  
//         UMDH logs).                                                      

+     2f0 (   2f0 -     0)      1 allocs BackTraceA39
+       1 (     1 -     0) BackTraceA39 allocations

ntdll!RtlpAllocateHeap+000000C6
ntdll!RtlAllocateHeap+000001E3
gdiplus!GpMalloc+00000016
gdiplus!GdipCreateBitmapFromGdiDib+00000090
DkClient2!Gdiplus::Bitmap::Bitmap+00000069 (c:\program files\microsoft sdks\windows\v6.0a\include\gdiplusbitmap.h, 653)
DkClient2!Gdiplus::Bitmap::FromBITMAPINFO+00000064 (c:\program files\microsoft sdks\windows\v6.0a\include\gdiplusbitmap.h, 736)
DkClient2!CAlarmZone::OnFrame+00000095 (p:\svn\orwell\head\dkclient\trunk\alarmzone.cpp, 361)

Total decrease ==   25ea requested +    41e overhead =   2a08

По такому отчету уже вполне можно определить место утечки и разбираться дальше. Конечно, есть дорогие средства с красивым интерфейсом вроде BoundsChecker'a. Однако, не все могут позволить себе покупку таких средств, а umdh совершенно бесплатна.

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

пятница, 27 февраля 2009 г.

Время затягивать пояса?

Прочитал статью «Время затягивать пояса». Не могу сказать о всем рынке в целом, но в IT ожидания зарплат однозначно были сильно завышены на мой взгляд. Когда к нам в компанию приходили студенты старших курсов без опыта работы с требованием в 50000руб. (это было около $2000 весной), то мы их не брали. При этом, если хотите в долларах, в 98—99-х студенты соглашались и на $200—300.
Мы сопоставляли зарплаты не со средним уровнем по Москве, а с другими странами. Оказалось, что набирать сотрудников в Германии и Индии оказывается выгоднее при таких же ценах как в Москве, поэтому мы предлагали соискателям уровень зарплат указанных стран и США. При таком подходе к этому вопросу мы смогли набрать профессионалов на разумную зарплату, что позволило не уменьшать выплаты сотрудникам и никто до сих пор не уволен.
На сколько мне известно, в других компаниях либо сокращают персонал, либо урезают зарплаты за счет «премиальной» части.

Что касается других областей... Мне кажется, что зарплата охранникам в московских школах 30000руб. за просиживание штанов тоже были сильно завышены. Гораздо сильнее, чем в IT :), поэтому и падать должны сильнее. Наверное эти рассуждения можно продолжить и для других направлений. Поэтому в плане того, что теперь все будут получать в соответствии с производительностью труда, то я согласен с автором статьи.

вторник, 17 февраля 2009 г.

Какого цвета прозрачность

В комплекте с Visual Studio 2008 можно скачать совершенно бесплатно немного урезанную версию Axialis IconWorkshop. Эта программка позволяет редактировать bmp с альфа каналом. Так я и попробовал сделать, но оказалось, что на некоторых машинах с Windows XP эта прозрачность рисовалась абсолютно чёрной, на других - нормально. На ATI и NVidia наблюдалось одинаково отсутствие закономерности.

Решение было найдено случайно. Как-то я купил программу ArtIconsPro и в ней то и раскрылась тайна черного цвета — Axialis IconWorkshop считает прозрачным (ну т.е. когда сам его рисует) цвет 0x00000000. В программе, где используются эти картинки, другое представление о цвете прозрачности и ей больше нравится 0x00FFFFFF. Соответственно, замена 0x00000000 на 0x00FFFFFF решила проблему.

Кстати, интересный факт — художники(может только у нас?) впадают в ступор когда слышат о том, что полностью прозрачный фон может быть белым или черным. Они считают, что он бесцветный. И это правильно, конечно, но суть компонентов(ARGB) цвета нужно знать и уметь сделать прозрачный фон нужного цвета.

четверг, 15 января 2009 г.

Идеальный монитор для разработчика

То, что для разработки нужно иметь оборудованные по высшему разряду рабочие места, писал неоднократно известный программист и писатель Спольски. К счастью у нас в компании вопрос с рабочими станциями решается, на мой взгляд, неплохо. Разработка ведется на машинах с процессорами Core 2 Quad или Core 2 Duo не самых младших моделей, чтобы за время компиляции разработчики не успевали отвлекаться на новости в интернете. Настало время апгрейда мониторов...

Мой опыт разработки с widescreen монитором показал, что соотношение 16:10 гораздо удобнее классического 4:3. При отладке можно смотреть на сорсы и кучу дополнительных окон, а при написании кода можно видеть h и cpp одновременно. На мой взгляд эффективность труда повышается без всякого сомнения, что подтверждается различными исследованиями, на которые приходилось натыкаться в сети.

В связи с этим мы планируем обновить разработчикам мониторы до 24" с разрешением 1920×1200. Хочется надеяться, что разработка пойдет быстрее...

А как в других компаниях с этим? Может, где-то уже во всю используют 32", а мы живем в прошлом веке?

P.S. Если классные разработчики на C++ хотят работать на таких мониторах, то заполняйте анкету и присылайте на [email protected].

вторник, 13 января 2009 г.

Зачем нужны атомные станции

Наверное многие уже слышали обвинения в адрес Google (например, здесь) в том, что поиск потребляет слишком много энергии и каждый ваш запрос «добавит в земную атмосферу 15 грамм углекислого газа».

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

Интересно, сколько чайников можно вскипятить при поски в Yandex или MSN?