28 мая 2007

Auto-sorted vector

Помнится еще Александреску в одной из своих книг упоминал как может сортированный vector или deque по скорости поиска элементов превосходить std::set и std::multiset. В последних контейнерах идет большой оверхед по размеру потребляемой памяти, из-за чего процессору приходится больше данных читать из памяти. Недостаток сортированных vector/deque при вставке - слишком часто придется двигать элементы, если вставлять сразу в нужное место. Однако часто вставка идет большой порцией элементов, что позволяет сначала накидать новые элементы как попало (через push_back), а потом отсортировать контейнер.

Что удивительно, ни в boost, ни в Loki я подобного сортированного вектора в виде отдельного класса (шаблона классов) не нашел. В Loki есть подобная штука - AssocVector, но реализует она функциональность не set/multiset, а map. Тоже, кстати, весьма полезная вещь.

Google навел меня на класс с нужной функциональностью на сайте codeproject. Однако там он какой-то сыроватый, VC++-only, да и без набора юнит-тестов, так что использовать его в реальном проекте я бы не стал. Те, кому наплевать на сырость могут вполне его использовать, а к более продвинутым людям просьба сделать подобную вещь нормально и пропихнуть ее таки в boost, т.к. предыдущие "пропихиватели" похоже не справились (см. здесь и здесь).

Остальные же могут использовать std::vector и std::deque, просто сортируя их и получая прирост производительности (по сравнению с std::set/multiset). Ключевых функций для написания будет две: insert и find. Их можно легко реализовать через родные сердцу алгоритмы бинарного поиска - std::lower_bound() и std::upper_bound().

Ссылка по теме: Why you shouldn't use set (and what you should use instead)

26 мая 2007

XML Data Binding in C++

Статья An Introduction to XML Data Binding in C++ на The C++ Source напоминает, что DOM И SAX - уже прошлый век, и давно уже пора мапить XML-данные на C++-классы.

Суть такова: пишите XML Schema для своих XML, некая утилита (binding compiler) автоматически генерирует по нему C++-классы, с которыми намного приятнее общаться, нежели, например с DOM:

XML:
<person>
<name>John Doe</name>
<gender>male</gender>
<age>32</age>
</person>

C++:
ifstream ifs ("person.xml");
auto_ptr<person_t> p = person (ifs);

if (p->age () > 30)
cerr << p->name () << endl;

24 мая 2007

Interview with A. Stepanov

Интересное интервью с Александром Степановым, главным идеологом и создателем STL. Особенно примечательно как он не любит ООП в общем и Java в частности.

19 мая 2007

Loki:ScopeGuard

Александреску - умный парень, но наверно скорее теоретик, чем практик. Иначе как объяснить такое:

В Loki можно найти полезную вещь - scope guard. Поясню зачем она нужна. Все знакомы с RAII и с тем, что очень удобно делать работу по управлению ресурсами в деструкторе - это безопасно с точки зрения исключений, так как при откате стека по исключению вызываются деструкторы всех уничтожаемых объектов. Например:
class File
{
public:
File(const char* FileName) { OpenFile(FileName); }
~File() { CloseFile(); }
};

{
File f("test.txt");
Foo();
}
Файл закроется даже в случае если Foo бросит исключение.

Однако создавать такие RAII-классы для каждого случая - довольно муторная работа. Представьте:
{
class Guard
{
public:
Guard(Module& M, Window& W) : m(M), w(W) {}
~Guard() { w.Close(); m.Terminate(); }
private:
Window& w;
Module& m;
}

Guard g(module, window);
Foo();
}
Гораздо проще было бы жить, если бы можно было записать все это намного короче:
{
AutoGuard(window.Close());
AutoGuard(module.Terminate());
Foo();
}
Так вот предложенная Александреску конструкция такое счастье и привносит в нашу жизнь, выглядит это так:
{
LOKI_ON_BLOCK_EXIT_OBJ(window, Windows::Close);
LOKI_ON_BLOCK_EXIT_OBJ(module, Module::Terminate);
Foo();
Все бы замечательно, но это не компилируется без using namespace Loki, так как макрос LOKI_ON_BLOCK_EXIT_OBJ выглядит так:
#define LOKI_ON_BLOCK_EXIT_OBJ ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) = MakeObjGuard
А ведь дядька Александреску мог бы и пожалеть людей и написать так:
#define LOKI_ON_BLOCK_EXIT_OBJ ::Loki::ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) = ::Loki::MakeObjGuard

А ведь не написал. Видимо не использовал это в реальных проектах. Или у него там сплошные using namespace xxx стоят?

PS. Оказывается уже исправили.

15 мая 2007

WinMain

Объявление WinMain имеет вид:
int WINAPI WinMain(      
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
);
Не видите ничего странного?

lpCmdLine имеет тип LPSTR, а по хорошему нужно бы LPTSTR (или даже LPCTSTR, но не суть). Кстати, GetCommandLine() возвращает LPTSTR.

argv тоже все держит в ANSI кодировке, видимо для совместимости со старым кодом. Но для создания юникодной версии парсить командную строку вручную не обязательно - можно воспользоваться CommandLineToArgvW().

12 мая 2007

Windows Sysinternals

Много полезных Windows-разработчику и администратору утилит можно найти на сайте Sysinternals, в частности RegMon и Process Explorer.

10 мая 2007

When edit control sends WM_CTLCOLORSTATIC

Edit control при отрисовке своего фона шлет свому "родителю" сообщение WM_CTLCOLOREDIT, в качестве результата получает HBRUSH, которым и рисует свой фон. Static контролы шлют WM_CTLCOLORSTATIC. Однако, бывают ситуации, когда и Edit запрашивает фон через WM_CTLCOLORSTATIC. Это происходит в двух случаях:
1. когда контрол заблокирован - EnableWindow(Edit, FALSE)
2. когда контрол в режиме read-only - SendMessage(Edit, EM_SETREADONLY, TRUE)

Чтобы read-only edit выглядел не как заблокированный а как обычный контрол, родителю нужно перехватить WM_CTLCOLORSTATIC и подменить его на WM_CTLCOLOREDIT:
LRESULT SomeWindow::OnCtlColorStatic(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// paint read-only edits as usual edits
return DefWindowProc(IsWindowEnabled((HWND)lParam) ? WM_CTLCOLOREDIT : WM_CTLCOLORSTATIC, wParam, lParam);
}
Можно также использовать технику message reflection, перенеся выбор фона с родителя на сам edit control.