26 июля 2007

Screen DPI in Vista

В Windows есть замечательная возможность - можно вручную указать DPI экрана. Приложения автоматически будут учитывать этот параметр, если используют "правильный" mapping mode, или учитывают этот параметр вручную через вызов GetDeviceCaps(ScreenDC, LOGPIXELSX / LOGPIXELSY) для режима MM_TEXT.

Видимо далеко не все разработчики учитывали DPI в режиме MM_TEXT (а этот режим используется по умолчанию), поэтому в Висте появился новый режим работы при увеличенном DPI (старый назвали XP style DPI scaling):



В этом режиме (когда указанная галочка снята) Виста полагает, что если приложение явно не сообщило, что оно умеет работать с разными dpi, то значит оно масштабировать нифига не умеет. И Виста будет делать это масштабирование насильно.

В моем текущем проекте весь GUI самописный и в нем с самого начала заложена поддержка различных dpi, т.к. значительная часть пользователей сидит на "крупных шрифтах" (они же 120dpi). Поэтому мне стало очень интересно, как будет выглядеть наше приложение в этом новом режиме. Так как никаких вызовов SetProcessDPIAware() и пометок в манифесте по поводу dpiAware нет, то Виста должна принять нас за лохов и применить добавочное масштабирование, что в сумме с заложенной в нашем GUI логикой в итоге должно дать двойное масштабирование. Однако этого не произошло - все выглядит как в XP. Довольно странно...

Полезная статья на эту тему: DPI-aware applications in Windows Vista. В частности, там указывается, что при собственноручной отрисовке иконок можно выбрать наиболее подходящий размер иконки из доступных:
// images available in sizes 16x16, 20x20, and 24x24
int nToolbarImageSize = (16*fScale+0.5f) >= 24 ? 24 : ((16*fScale+0.5f) >= 20 ? 20 : 16);
Идея очень правильная, так как при точном масштабировании иконки получаются довольно кривыми, поэтому достаточно выбрать наиболее близкую по размеру и отрисовать ее 1:1. Однако, не стоит это делать приведенным выше методом, гораздо разумнее положить все доступные размеры в контейнер и воспользоваться алгоритмом std::lower_bound.

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

24 июля 2007

Boost.ForEach and rvalue [2]

По поводу использования rvalue-(proxy)контейнеров в Boost.ForEach: Eric Niebler, создатель этой библиотеки, пояснил мне, что намерено выбрал использование const_iterator-ов для rvalue объектов, чтобы предотвратить ненамеренное изменение контейнеров, возвращенных как rvalue. Что касается proxy-контейнеров, таких как boost::iterator_range, то Эрик предлагает приравнять в них константные итераторы к обычным, так как изменение самого прокси-контейнера не происходит. Цитирую:

The interface I chose preserves object lifetimes and prevents inadvertent mutation of temporary objects. Weakening its guarantees is a bad idea.

If you want the proxy to offer either a const or mutable interface *and* you want our proxy to work with BOOST_FOREACH, you should base it on the const-ness or mutability of the object being proxied, not the constness of the proxy itself. Consider:
template<class Range>
struct proxy {
typedef typename range_result_iterator<Range>::type iterator;
typedef typename range_result_iterator<Range>::type const_iterator;
iterator begin() const { return boost::begin(rng_); }
iterator end() const { return boost::end(rng_); }
// etc ...
Range &rng_;
};
Now, you can have const and mutable proxied objects like:
// ok, a mutable proxy
proxy< std::vector<int> > p1;

// ok, still a mutable proxy
proxy< std::vector<int> > const p2;

// ok, a const proxy
proxy< std::vector<int> const > p3;

// still a const proxy
proxy< std::vector<int> const > const p4;

23 июля 2007

Boost.ForEach and rvalue

Когда я начал использовать Boost.ForEach, первая мысль, которая пришла мне в голову, была: как делается выбор между iterator и const_iterator? Сначала я не стал тратить время на выяснение этого вопроса, но потом все-таки пришлось это сделать - один фрагмент моего кода не хотел компилироваться при использовании BOOST_FOREACH. Нижеприведенный код демонстрирует проблему:
typedef vector<string> vec;
vec get_vector();

void test()
{
// отлично компилируется
get_vector().push_back("oh god, it's writable!");

// получаем невозможность сконвертировать const string в string&
BOOST_FOREACH(string& s, get_vector())
{
...
}
}
В моем случае был, конечно не vector, а прокси-контейнер, но суть понятна: при использовании rvalue-контейнера выбирается const_iterator и получаем невозможность модифицировать значения в контейнере.

Но ведь в том же бусте есть прокси-контейнеры (iterator_range и ко), неужели не подумали о них? Очень не верится.

И я не ошибался свято веря в создателей foreach - с iterator_range приведенная конструкция успешно работает! Оказывается, для определения прокси-контейнеров предусмотрен специальный хак (по-другому не назовешь) - boost_foreach_is_lightweight_proxy.

Однако ни использование ::boost_foreach_is_lightweight_proxy, ни boost::foreach::is_lightweight_proxy почему-то не помогло мне скомпилировать приведенный выше код:
inline boost::mpl::true_ *
boost_foreach_is_lightweight_proxy(vec *&, boost::foreach::tag) { return 0; }
Вот теперь думаю - то ли я тупой, то ли сани не едут.

19 июля 2007

Exception Handling Cost

Почти все C++-программисты понимают как транслируется в ассемблер вызов функции, сколько он примерно "стоит" в байтах и тактах процессора. Многие понимают сколько стоит вызов виртуальной функции. Но вот во что выливается обработка исключений - я думаю имеют представление далеко не все. Некоторые даже вообще не используют исключений, боясь что это очень "дорого" по времени исполнения.

Наткнулся в Google.Video на презентацию Exception Handling Cost. Информация из первых уст: автор занимается обработчиками исключений в команде компилятора Visual C++.

PS. файл с презентацией

17 июля 2007

Boost.Iterator

При реализации шаблонных алгоритмов, наткнулся на необходимость приводить указатели к ссылкам. В случае с контейнерами и интервалами все довольно просто - boost::indirect_iterator помогает конвертировать итераторы по указателям в итераторы по ссылкам.

Как быть с обычными указателями, в бусте так и не нашел. Тот же boost::ref не инициируется из указателя, только из ссылки. Можно написать что-то типа:
template <typename T>
inline T& GetReference(T& Reference)
{
return Reference;
}

template <typename T>
inline T& GetReference(T* Pointer)
{
return *Pointer;
}
...но что-то мне кажется что в бусте есть что-то подобное, вопрос - где?

Кстати, в том же бусте обнаружил filter_iterator, который недавно собственноручно изобретал в качестве велосипеда :-E. А стоило лишь заглянуть в boost. Вобщем, как в Южном парке - "Это уже было в Симпсонах!"

02 июля 2007

WTL 8.0

Зарелизился WTL 8.0. Никаких особых killer features не появилось. Теперь WTL может работать с ATL 3.0, что актуально когда Platform SDK скачано с сайта Microsoft, а не получено в комплекте с Visual Studio. Такой момент имеет быть при использовании Visual Studio 2005 Express.