вторник, 7 декабря 2010 г.

simple bind

В ряде алгоритмов стандартной библиотеки (например, find_if или remove_if) нужно писать функтор. Некоторые этот функтор пишут прямо рядом с вызовом функции, т.е. используют локальный класс (это разрешают некоторые компиляторы). Однако, стандарт запрещает использовать локальный класс в шаблонных функциях, а это значит, что неплохо бы ещё поместить этот функтор в отдельное пространство имен. Многовато кода для простого вызова функции...

Кстати, код будет выглядеть следующим образом:
#include <algorithm>
#include <vector>

struct some {
    long some_field1_;
    int some2_;
};

namespace impl {
// такая структура будет нужна для каждого поля, которое будет использоваться в алгоритмах
struct comp_some_some2 {
    comp_some_some2( int some2 ) : some2(some2) {}
    bool operator()( const some& x ) const { return (x.some2_==some2); }
private:
    int some2;
};
}//namespace impl

int main()
{
    std::vector<some> some_lot;
    // ...

    int to_find = 10;
    //
    std::find_if( 
      some_lot.begin(), 
      some_lot.end(), 
      impl::comp_some_some2(to_find) 
    );

    // ...    
    return 0;
}
Использование boost::bind сильно упрощает код, как можно видеть далее:
#include <algorithm>
#include <vector>
#include <boost/bind.hpp>
struct some {
    long some_field1_;
    int some2_;
};

int main()
{
    std::vector<some> some_lot;
    // ...

    int to_find = 10;
    // using boost::bind
    std::find_if( 
      some_lot.begin(), 
      some_lot.end(), 
      boost::bind(&some::some2_, _1) == to_find 
    );

    // ...
    return 0;
}
Для примера, приведу как это может выглядеть в C++0x:
// using C++ lambda function
std::find_if( 
  some_lot.begin(), 
  some_lot.end(), 
  [&to_find](const some& x){ return (x.some2_ == to_find); } 
);

четверг, 25 ноября 2010 г.

strong typedef

Известно, что typedef не создает новый тип, а просто вводит алиас для существующего типа. Т.е., например, объявив
typedef int myint_t;
нельзя перегрузить функцию для myint_t, так как типы неразличимы для компилятора:
void f( int );
void f( myint_t ); // error
Если мы хотим эти типы различать, то решение состоит в том, чтобы создать структуру myint_t вместо typedef и перегрузить все необходимые операции, а также оператор приведения типа. В библиотеке Boost уже есть обертка для такой задачи — strong_typedef, что избавляет от необходимости писать её самим.

вторник, 23 ноября 2010 г.

Как так выходит, что разработчики, пока учатся в институте, на всех этапах участвуют в процессе обмена готовыми решениями — то есть, грубо говоря, качают из интернета курсовые, списывают на экзаменах и т.п.? А вот когда они же начинают писать программы, то каждый считает долгом написать свой велосипед, например, те же связанные списки, вместо того, чтобы поискать готовые решения, по крайней мере, в стандартной библиотеке или в Boost...

воскресенье, 14 ноября 2010 г.

boost indirect_iterator

boost содержит удобную обертку indirect_iterator. Эта обертка делает дополнительное разыменование в operator*(). Это позволяет удобно работать с контейнерами, которые содержат указатели на элементы вместо самих элементов. Например,
struct test {
  int field1;
  test( int v ) : field1( v ) {}
};

typedef boost::shared_ptr<test> test_ptr_t;
typedef std::vector<test_ptr_t> test_t;
test_t test_array;

int main()
{
  test_array.push_back( test_ptr_t( new test(1) ) );
  test_array.push_back( test_ptr_t( new test(5) ) );
  test_array.push_back( test_ptr_t( new test(10) ) );

  typedef boost::indirect_iterator<test_t::const_iterator> test_const_iterator;
  using boost::make_indirect_iterator;
  for ( test_const_iterator it = make_indirect_iterator( test_array.begin() ); 
    it != make_indirect_iterator( test_array.end() ); ++it )
  {
    // пишем it->field1 вместо (*it)->field1
    std::cout << it->field1 << std::endl;
  }
}

Писанины многовато, но иногда оно того стоит. В алгоритамах indirect_iterator упрощает написание функторов:
test_const_iterator f = std::find_if(
  make_indirect_iterator( test_array.begin() ),
  make_indirect_iterator( test_array.end() ),
  boost::bind( &test::field1, _1 ) == 5 );
std::cout << f->field1 << std::endl;

Стоит отметить, что счастливые пользователи компиляторов с поддержкой нового стандарта(например, GNU C++ 4.5 или MSVC++ 2010) могут заменить все это ситаксическое безобразие лямбда функциями и написать следующее:
auto x = std::find_if(
  test_array.begin(),
  test_array.end(),
  [](test_ptr_t v) { return v->field1 == 5; } );
std::cout << (*x)->field1 << std::endl;

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

Upgrade to VS2010

У нас идет плановый переход на Visual Studio 2010. Почти все проекты сконвертились без приключений. Пока только две проблемы заставили подумать.

1. В соответствии с новым, ещё не принятым, стандартом, у ассоциативных контейнеров iterator и const_iterator — оба константные итераторы (см. §23.2.4). С одной стороны, это позволяет избежать ошибок на этапе компиляции, ведь, например, у std::set изменение элемента контейнера может разрушить этот контейнер. С другой стороны, если мы точно знаем, что мы меняем элемент и при этом порядок элементов не меняется, то можно сэкономить кучу тактов, если менять элементы напрямую. К счастью, для второго случая остается лазейка — можно объявить поля структуры, которые мы хотим менять, mutable. Конечно, нужно быть очень осторожным, но предполагается, что если мы пишем mutable, то мы знаем что делаем.

2. В проектах со смешанным кодом C++ и C++/CLI иногда перестали подгружаться managed-ресурсы. Расследование показало, что у resx-файлов поле Logical resource name в настройках проекта равно чему-то вроде $(RootNamespace).%(Filename).resources. Так вот этот %(Filename) ни чем не заменяется при компиляции. Проблема решается ручной настройкой для каждого resx-файла. Надо бы баг запостить в connect.microsoft.com, но пока не до этого.

Также, к сожалению, подсветка синтаксиса для стандартных типов (из стандартной библиотеки) в C++ по прежнему отсутствует. Поэтому пригодился старый файл usertype.dat, который можно положить рядом с devenv.exe и все указанные типы начинают светиться любимым цветом (любимый цвет выбирается). Каждый может написать свой файл usertype.dat, формат предельно прост — каждая строка содержит один тип.

четверг, 7 октября 2010 г.

Обо всем понемногу

Сегодня прислали материальное подтверждение, что наша компания получила статус Microsoft Certified Partner. В составе: стеклянная фиговина (и ещё одна запасная), как на картинке, и большой постер. К статусу прилагается куча лицензий на софт, в том числе 10 комплектов Visual Studio 2010 Premium с подпиской MSDN. Кстати, для небольших стартапов партнерство (даже без сертификаций) является неплохим способом получить лицензионные средства разработки и прочий софт за недорого. Подробнее об этом рассказывали на форуме Innovation Day 5 октября, ну и ещё не раз расскажут видимо. А почитать можно по ссылке. Стоит отметить, что мы успели завершить все формальности до октября, а иначе такого статуса не дали бы, т.к. система плюшек поменялась. Хорошо это или плохо — пока не ясно.

Кстати о студии... Предварительно в VS 2010 уже видны косяки, которые остались ещё с бэты. Во первых, конвертация проектов не проходит так гладко, как хотелось бы. А второе, это то, что помимо обычных зависимостей в С++ солюшене, сделали ссылки как в .NET и совсем не очевидно, что зависимости нужно ставить в двух местах теперь. Всякие вкусности из нового стандарта уже есть, но пользоваться ими, к сожаления, пока нельзя (о чем я писал тут).

Стоит упомянуть, что на Innovation Day раздавали книжки по Share Point 2007, но оставили без футболок, а в обед оставили без десерта, что очень печально — экономить на разработчиках плохо ибо, перефразируя Этвуда(Atwood), Cookies is Cheap, Programmers are Expensive. Помимо этого все было посвящено переходу всего и вся в облако, но судя по количеству вопросов, особой популярностью эта тема пока не пользуется. Облачное счастье последовательно продвигается Микрософтом уже довольно давно, но меня лично пока не впечатляет. Вообще-то облака — это не открытие, просто в других облаках нельзя писать на C#. Материалы по докладам можно будет найти позже здесь.

Тем временем, нашему серверу, который обеспечивает команду сорсами, багами, доками, билд сервером на виртуалке и прочим, предстоит миграция с Ubuntu 8.04 LTS x32 на Ubuntu 10.04 LTS x64. Если у читателей присутствует опыт как перенести все конфиги и получить тот же набор пакетов, то ссылки в комментариях(или по почте) приветствуются. Можно просто скопировать все из /etc в новую ОС?

Ещё мы опять ищем разработчиков на С++, так, что можно заполнять анкету и слать на [email protected] вместе с резюме.

пятница, 3 сентября 2010 г.

Function try block

Думаю, многие применяли try-catch блоки для ловли исключений. Однако, есть у этих блоков одна малоизвестная особенность введенная в стандарт в 1995 году. Называется function-try-block и применяется, в основном, для того, чтобы поймать исключения в списке инициализации конструктора. Рассмотрим небольшой пример:
struct A
{
private:
  std::string s;
public:
  A( int value ) : s( boost::lexical_cast<std::string>( value ) ) {}
};

Что будет с данным кодом, если boost::lexical_cast вдруг выкинет исключение? А будет то, что экземпляр класса A не создастся. Это может быть совсем не то, что мы хотим. Как раз для таких целей и нужен function-try-block. Выглядить это будет так:
struct A
{
private:
  std::string s;
public:
  A( int value ) try : s( boost::lexical_cast<std::string>( value ) ) {}
  catch ( boost::bad_lexical_cast ) { /* handle lexical_cast exception here */ }
};

Обратите внимание в каком месте находится ключевое слово try.

среда, 1 сентября 2010 г.

До нового стандарта осталось чуть

Надоело называть новый стандарт C++0x? Так вот Саттер пишет, что, вероятно, уже в марте будут подчищены последние нестыковки и стандарт будет выпущен под именем C++11. Самое замечательное из последней встрече комитета по стандарту С++ — это решение, что деструкторы будут noexcept по умолчанию. Это будет подсказкой для новичков в программировании. Я бы ещё сделал, чтобы все конструкторы были explicit по умолчанию, но это порушит совместимость между стандартами.

вторник, 27 июля 2010 г.

struct in for-loop

Не многие знают, и уж совсем почти никто не пользуется, возможностью объявлять в циклах переменные разных типов. Достигается это с помощью неименованной структуры, как показаны ниже:
for( struct {int i; long i2;} x = {1, 1}; x.i2 < mid; x.i++, x.i2 = x.i * x.i )
{
  std::cout << x.i2 << std::endl;
}
Пользователи компилятора от Microsoft, не пытайтесь повторить это дома — это не работает в MSVC++. Те кто пользовался VC6 помнят и другие косяки с переменными в циклах. Можно потренироваться в GNU C++ и в Comeau.

UPDATE (2015-12-22): В Visual Studio 2015 данная фича языка C++ также не поддерживается, но в этой версии MSVS можно выбрать Clang 3.7 в качестве компилятора и тогда сборка будет успешной.

среда, 30 июня 2010 г.

case-insensitive substring search

Что делать, если нужно найти подстроку в строке без учета регистра? Можно, конечно, написать целый case-insensitive класс строк, как предлагает Саттер. Ну а если уже есть строки std::string и хочется в них искать? В это случае можно использовать стандартный алгоритм std::search с собственным предикатом:
// templated version of my_equal so it could 
// work with both char and wchar_t
template<typename charT>
struct my_equal {
  my_equal( const std::locale& loc ) : loc_(loc) {}
  bool operator()(charT ch1, charT ch2) {
    return std::toupper(ch1, loc_) == std::toupper(ch2, loc_);
  }
private:
  const std::locale& loc_;
};

Можно написать ещё вспомогательную фукнцию, чтобы не писать каждый раз строку с длинными итераторами:
// find substring (case insensitive)
template<typename T>
int ci_find_substr( const T& str1, const T& str2, const std::locale& loc = std::locale() )
{
  T::const_iterator it = std::search( str1.begin(), str1.end(), 
    str2.begin(), str2.end(), my_equal<T::value_type>(loc) );
  if ( it != str1.end() )
    return it - str1.begin();
  else
    return -1; // not found
}

Использовать совсем просто:
// не забываем включить необходимые части
#include <locale>
#include <iostream>
#include <algorithm>
using namespace std;

int main(int arc, char *argv[]) 
{
  // string test
  std::string str1 = "FIRST HELLO";
  std::string str2 = "hello";
  int f1 = ci_find_substr( str1, str2 );

  // wstring test
  std::wstring wstr1 = L"ОПЯТЬ ПРИВЕТ";
  std::wstring wstr2 = L"привет";
  int f2 = ci_find_substr( wstr1, wstr2 );

  return 0;
}

воскресенье, 27 июня 2010 г.

Лето

В Москве очень жарко. Из крана для холодной воды течет теплая. Плавится асфальт и мозг. Комп можно включать только утром, в остальное время перегревается. Работать невозможно...

Выбрал погоду на яндексе, получается в Дублин надо ехать работать. Говорят, там свою Кремниевую долину уже построили без лишнего шума.

Доставка погодыЯ сказал, что хочу +20°С, и мою погоду доставили в 167 городов.
Среди них:
— Севилья (Испания)
— Кордова (Испания)
— Кастория (Греция)
А вашу?
Доставка и прогноз погоды

пятница, 4 июня 2010 г.

Function overload

Как известно, если в наследнике определить функцию с таким же именем, как в базовом классе, то она скроет все функции этого базового класса с этим именем не смотря на аргументы. Например,
struct B {
void f( char );
void f( std::string );
};

struct D : B {
void f(int) { f('c'); } // получаем рекурсивный вызов D::f(int)
};

Однако, если мы хотим оставить функцию(или несколько функций) базового класса, а не скрывать её, то можно написать using B::f;
struct D : B {
using B::f;
void f( int ) { f('c'); } // вызывается B::f(char)
};

четверг, 3 июня 2010 г.

typedef forward declare

При реализации PIMPL идиомы скрывается реализация и необходимо писать предварительные определения(forward declaration) классов, структур и прочего. Возникает вопрос — как написать предварительное объявление для typedef. Делается это следующим образом: сначала определяем класс, который учавствует в typedef, а потом пишем само определение. Например,
class A;
typedef std::shared_ptr<A> A_ptr;

Интересно, что стандарт запрещает писать предварительные объявления для классов стандартной библиотеки (17.4.2.1/3). Единственно правильный вариант — включать стандартные файлы. Для группы iosteam классов в стандарте сделан отдельный файл <iosfwd> (27.2) с преварительным определениями. Его можно включать и программа будет считаться корректной.

четверг, 13 мая 2010 г.

explicit template specialization

При написании шаблонного класса бывает необходимо, чтобы разные специализации класса имели разную реализацию какой-то только одной фукнции. Для этого нет необходимости писать вручную специализацию всего класса, достаточно написать специализацию только одной функции. Например:
template<typename T>
class VeryBigClass {
public:
// предполагается, что здесть ещё куча фукнций
double one( int ) {};
// ...


// и ещё одна нешаблонная функция, которую будем определять
void f();
};

// реализация для общего случая
template<typename T> void VeryBigClass<T>::f() { std::cout << "generic" << std::endl; }
// реализация для типа long
template<> void VeryBigClass<long>::f() { std::cout << "long" << std::endl; }

Стандарт разрешает это несмотря на то, что фукнция f() сама по себе не шаблонная. Это разрешается пунктом стандарта C++'03 14.7/3 и ещё есть хороший пример в 14.5.2/2.

Разработчики бывает думают, что код выше невозможен, вероятно, из-за того, что таким способом невозможно написать частичную специализацию класса. Т.е. мы не можем написать функуцию VeryBigClass<T>::f() и ещё одну VeryBigClass<T*>::f() не продублировав определение весего класса ещё раз (либо разбив его на части).

вторник, 11 мая 2010 г.

C++ inheritance

Небольшой пример для объяснения разницы между public, private и protected наследованием:
class A 
{
public:
  int x;
protected:
  int y;
private:
  int z;
};

class B : public A
{
// x is public
// y is protected
// z is not accessible from B
};

class C : protected A
{
// x is protected
// y is protected
// z is not accessible from C
};

class D : private A
{
// x is private
// y is private
// z is not accessible from D
};

Код с ответами, но если убрать комментарии, то можно спрашивать на собеседовании. Ещё интересные моменты: размер класса D такой же как у A, порядок элементов x, y и z не гарантируется (т.е. не факт что &x < &y < &z).

пятница, 23 апреля 2010 г.

Pointer to mutable member

Указатели на члены класса позволяют обращаться с членами класса не зная их имени.
struct S {
int i;
};

int S::* pm = &S::i;
Далее эти указатели можно использовать следующим образом:
S cs;
cs.*pm = 10;
Интересно, что нет никакого специального синтаксиса для переменных с квалификатором mutable. Более того, стандарт запрещает использовать указатель на mutable член класса для модификации константного объекта класса (C++ Standard 5.5/5). Следующий код не будет компилироваться:
struct S {
mutable int i;
};
const S cs;
int S::* pm = &S::i; // pm refers to mutable member S::i
cs.*pm = 88;         // ill-formed: cs is a const object
Запрещено это было, видимо, т.к. компилятору сложно отслеживать на какой именно член класса указывает pmmutable или нет. Как и большинство других, это ограничение можно обойти создав обертку:
template<typename T> struct mutable_wrapper { mutable T value; };
Далее используем следующим образом:
struct S {
mutable_wrapper<int> i;
};

const S cs;
mutable_wrapper<int> S::* pm = &S::i;
(cs.*pm).value = 88;
В обертке можно перегрузить операторы приведения типа, чтобы синтаксически скрыть обертку, но это дела вкуса. На мой взгляд лучше явно видеть, когда модифицируются mutable члены класса.

вторник, 20 апреля 2010 г.

in_addr visualizer

Странно, что в Visual Studio нет готового визуализатора для типа in_addr. Я поудивлялся и написал свой:
in_addr {
preview
(
#( "[ ",
[$e.S_un.S_un_b.s_b1,u], ".",
[$e.S_un.S_un_b.s_b2,u], ".",
[$e.S_un.S_un_b.s_b3,u], ".",
[$e.S_un.S_un_b.s_b4,u],
" ]"
)
)
}
Его нужно добавить в файл autoexp.dat. Тогда структура in_addr в отладчике будет выглядеть как [ XXX.XXX.XXX.XXX ]. Удобно также смотреть адреса, которые прячутся в типах long написав в окне Watch (in_addr*)&address. Тогда вместо 990554304 получаем [ 192.168.10.59 ].

После изменения autoexp.dat студию нужно перезапустить (иногда работает без перезапуска).

пятница, 16 апреля 2010 г.

Песенка для запоминания π

Искал в инете число $\pi$ с большой точностью. Наткнулся на милую песенку тут. Смотреть со звуком.

Кстати, число с нужной точностью нашел тут.

пятница, 9 апреля 2010 г.

sizeof char и другие сложности с типами

Составил таблицу с размерами типов в различных компиляторах и операционных системах (32- и 64-битных). Это полезно знать, чтобы учитывать при проектировании переносимых решений. Также полезно знать, что лучше использовать типы фиксированной длины. В новых версиях стандарта (начиная с C++11) для этого имеется файл stdint.h. Аналогичный файл существует и для языка C. Для более старых версий можно использовать pstdint.h. А использование в интерфейсах модулей знаковых типов фиксированной длины, позволяет относительно легко стыковаться с модулями на других языках, которые написаны в соответствии с Common Language Specification.

ТипMSVC++
32-bit
MSVC++
64-bit
gcc++ 4.4.1
32-bit
gcc++ 4.7.2
64-bit
char1111
short2222
int4444
long4448
size_t4848
wchar_t2244
long long8888
time_t18848

1) В версии MSVC++ старше 2005 time_t был определен как long int (32 bits) и поэтому не мог использоваться для дат позднее 19 января 2038 3:14:07 UTC. Начиная с Visual C++ 2005 time_t стал эквивалентен __time64_t по умолчанию, но это поведение можно изменить определив _USE_32BIT_TIME_T (тогда time_t будет определен как __time32_t). В таблице показан размер time_t по умолчанию.

четверг, 8 апреля 2010 г.

Сравнение поддержки C++0x в GNU C++ и MSVC++2010

Скот Мейерс(Scott Meyers) опубликовал сравнение поддержки стандарта C++0x в GNU C++ и Visual Studio. Исследование включает также стандартную библиотеку. Сравниваются g++ 4.3, g++ 4.4, MSVC++ 9 и MSVC++ 2010 Beta 2. Посмотреть можно здесь.

вторник, 23 марта 2010 г.

Effective C++ compiler

Кстати, пока смотрел флаги GNU C++ в предыдущем сообщении, наткнулся на флаг -Weffc++. Он говорит компилятору сообщать о нарушениях правил Скота Мейерса(Scott Meyers) из книг Effective C++ и More Effective C++. Контролируются следующие правила из первой книги:

11. Для классов с динамическим выделением памяти объявляйте копирующий конструктор и оператор присваивания.
12. Предпочитайте инициализацию присваиванию в конструкторах.
14. Делайте деструкторы виртуальными в базовых классах.
15. "operator=" должен возвращать ссылку на *this.
23. Не пытайтесь вернуть ссылку, когда вы должны вернуть объект.

И следующие из второй:

6. Различайте префиксную и постфиксную формы операторов инкремента и декремента.
7. Никогда не перегружайте опреаторы "&&", "||", и ",".

Хочу такую фичу в Visual Studio.

Сколько яблок у Маши?

Вопрос: У Миши было 5 яблок.
Два яблока он отдал Маше.
Сколько стало у Маши?
Ответ: Неизвестно. Нужно
инициализировать переменные.

Сегодня столкнулся с тем, что в релизе программа не работала, а в дебаге выполнялась вполне корректно. Причины могут быть разными, но первое что нужно проверять — это все ли переменные инициализированы. Интересно, что в Visual Studio нет никакого предупреждения с /W4 и флагом /analyze. В приведенном ниже примере компилятор нашел только одну проблему:
#include <iostream>
using namespace std;

struct  X
{
    int x;
};

struct Y
{
    int y;

    Y() {}
};

int main()
{
    X x;
    Y y;
    int ly = y.y; // не ловит
    int lx = x.x; // warning C6001: Using uninitialized memory 'x': Lines: 19, 20, 21, 22
    cout << ly << endl << lx << endl;
}
Из других компиляторов, которые я попробовал ошибку поймал только GNU C++ с флагом -Wuninitialized. Comeau С++ и Intel C++ поймали только одну ошибку. А мне казалось, что такие простые вещи уже научились ловить все приличные компиляторы...

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

MS In. Day

Микрософтский форум компаний-разработчиков Microsoft Innovation Day пройдет 26 марта в Москве. Поеду послушать про новые возможности Microsoft SQL Server 2008 R2. Если кому интересно, то посмотреть полную программу мероприятий и зарегистрироваться можно здесь. Участие бесплатно.

четверг, 11 марта 2010 г.

Cake with greeting

Что можно написать на торте для технически подкованного друга или подруги? Изобретательные айтишники предлагают следующую формулу: $$r = \frac{\sin\varphi\sqrt{\left|\cos\varphi\right|}}{\sin\varphi+\frac{7}{5}}-2\sin\varphi+2$$ Если сразу вообразить график функции не получается, то можно посмотреть в WolframAlpha или ниже:

среда, 3 марта 2010 г.

endl effect

При разработке часто используют вывод отладочной информации в лог файлы. В простейшем случае это может выглядеть вот так:
ofstream_log_file_ << 
  "Add useful debug message here " << 
  extra_info_ << std::endl;
В этом коде вызывается функция std::endl, которая помимо перевода строки для буферезированных потоков сбрасывает буфер в выходной файл. При небольшом количестве таких вызовов это даже полезно — в случае падения программы в логе останется полезная информация. Но для интенсивного логгирования, когда нужно писать в лог очень часто, эта дополнительная особенность снижает скорость в несчитанное количество раз.

Ускорить запись в лог очень просто — меняем std::endl на известный символ "\n".
ofstream_log_file_ << 
  "Add useful debug message here " << 
  extra_info_ << "\n";

вторник, 2 марта 2010 г.

Explicitly qualified name

Вчера наткнулся на интересную проблему. Рассмотрим следующий пример:
std::string convert();
// ...
namespace tools {
class Numeric {
// ...
friend std::string ::convert();
};
}
При компиляции этого кода компилятор выдает ошибку. Что тут не так и как исправить положение? Ответ и пояснение чуть позже.



Ошибка тут в том, что компилятор парсит этот код как std::string::convert, как правильно заметил ztonix в комментариях. К сожалению избавиться от :: нельзя, т.к. без точек мы декларируем функцию tools::convert и пометим её как friend.

Решением проблемы будет использовать скобки для выделения имени функции:
friend std::string (::convert)();
Стоит отметить, что со встроенными типами таких проблем нет. Компилятор однозначно воспринимает friend int ::convert();

среда, 24 февраля 2010 г.

Snow

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

пятница, 19 февраля 2010 г.

Thread pool sample

Приведу пример кода для RegisterWaitForSingleObject. Для начала создаем событие, по которому задача будет выполнятся. В этом примере это будет Event:
HANDLE render_event_ = CreateEvent( NULL, FALSE, FALSE, NULL );
Создается событие с автопереключением (второй аргумент FALSE), чтобы не вызывать вручную ResetEvent.

Далее вызываем RegisterWaitForSingleObject:
RegisterWaitForSingleObject( &render_wait_obj_, render_event_, RenderCallback, SOME_USER_PARAM, 1000, 0 );
Данным вызовом мы говорим вызывать функцию RenderCallback с аргументом SOME_USER_PARAM по событию render_wait_obj_, но не реже одного раза в 1000 миллисекунд. Возвращается render_wait_obj_ типа HANDLE, которое нужно передать в UnregisterWait, когда больше не нужно выполнять задачи зарегестрированного типа (например, при выходе из программы). Функция RenderCallback имеет следующий вид:
VOID CALLBACK RenderCallback( PVOID lpParameter, BOOLEAN TimerOrWaitFired )
{
// Какие-то действия. Параметр TimerOrWaitFired помогает
// узнать причину вызова RenderCallback.
// Ничего связанного с пулом потоков тут делать не требуется.
}
Когда нужно выполнить задачу RenderCallback просто вызываем SetEvent:
SetEvent( render_event_ );

среда, 17 февраля 2010 г.

Thread pool

При написании программ приходится сталкиваться с ситуациями, когда хотелось бы сделать какую-либо небольшую задачу асинхронной, но создание потока оказывается слишком накладным. Для этого существует концепция пула потоков (thread pool). Вкратце, суть идеи состоит в том, что существует некоторый набор потоков (пул), который может расширятся при необходимости, либо уменьшаться. Разработчику необходимо указывать функции, которые нужно выполнить асинхронно, а реализация пула потоков сама берется выполнить задачу наиболее эффективно используя возможности многоядерных процессоров. Посмотрим на то, что предлагает Windows API.

Простейший сценарий предлагается функцией QueueUserWorkItem. Она принимает указатель на функцию с одним параметром. Указанная задача передается в пул потоков и будет выполнена в соответствии с указанными флагами (флаги помогают пулу потоков определить как лучше выполнить задачу).

Функция RegisterWaitForSingleObject позволяет указать задачу, которая будет выполняться по событию (Event, Mutex, Semaphore, Console input и прочее). Если событие не возникает, то задача выполняется по истечении указанного периода времени. Это, например, удобно использовать для асинхронного отображения видео кадров приходящих по сети. При получении кадра он выводится, а если кадров долго нет, то показывается специальный обновляемый кадр с сообщением о проблеме.

Ещё одна интересная функция — CreateTimerQueueTimer — позволяет создать асинхронный таймер. В этом случае задача ставится в очередь на выполнение регулярно(если другое не задано) через указанный период времени. Уже ясно, что задача выполняется в отдельном потоке, в отличии от обычного таймера Windows.

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

вторник, 2 февраля 2010 г.

still here

Блог не забыт, просто много дел и написать не остается времени. Надеюсь, что в конце месяца появится минутка...

среда, 20 января 2010 г.

Debug database errors

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

Так исторически сложилось, что c базой данных мне приходится работать через OLE DB. Для получения текстовых сообщений об ошибках взаимодействия с БД можно использовать замечательную функцию AtlTraceErrorRecords. Эта функция выводит подробную информацию об ошибке на нужном языке, но работает только в отладочной сборке и выводит сообщения не в наш лог, что, очевидно, не очень удобно. Доработать эту функцию несложно, т.к. она предоставляется в исходных кодах в файле atldbcli.h. После доработки выглядит она следующим образом:
namespace DBUtils {

CString TraceErrorRecords(HRESULT hrErr) const
{
    CDBErrorInfo ErrorInfo;
    ULONG   cRecords;
    HRESULT   hr;
    ULONG   i;
    CComBSTR  bstrDesc, bstrHelpFile, bstrSource;
    GUID   guid;
    DWORD   dwHelpContext;
    WCHAR   wszGuid[40];
    CString   msg;

    // If the user passed in an HRESULT then trace it
    if (hrErr != S_OK)
        SystemLog<LOG_DEBUG > (L"OLE DB Error Record dump for hr = 0x%x") % hrErr;

    LCID lcLocale = GetSystemDefaultLCID();

    hr = ErrorInfo.GetErrorRecords(&cRecords);
    if (FAILED(hr) && ErrorInfo.m_spErrorInfo == NULL) {
        SystemLog<LOG_DEBUG>(L"No OLE DB Error Information found: hr = 0x%x") % hr;
    }
    else {
        for (i = 0; i < cRecords; i++) {
            hr = ErrorInfo.GetAllErrorInfo(i, lcLocale, &bstrDesc, &bstrSource, &guid,
                &dwHelpContext, &bstrHelpFile);
            if (FAILED(hr)) {
                SystemLog<LOG_DEBUG>(L"OLE DB Error Record dump retrieval failed: hr = 0x%x") % hr;
                return msg;
            }
            StringFromGUID2(guid, wszGuid, sizeof(wszGuid) / sizeof(WCHAR));
            CString tmpMsg;
            tmpMsg.Format(_T("Row #: %4d Source: \"%s\" Description: \"%s\" Help File: \"%s\" Help Context: %4d GUID: %s\n"),
                i, OLE2T(bstrSource), OLE2T(bstrDesc), OLE2T(bstrHelpFile), dwHelpContext, OLE2T(wszGuid));
            SystemLog<LOG_DEBUG>(tmpMsg);
            bstrSource.Empty();
            bstrDesc.Empty();
            bstrHelpFile.Empty();

            msg += tmpMsg;
        }
        SystemLog<LOG_DEBUG>(L"OLE DB Error Record dump end");
    }

    return msg;
}

}//namespace DBUtils
Для сохранения в файл здесь используется уже описанная ранее функция SystemLog. В результате в файл сохраняются сообщения, которые легко понять даже пользователю. Результат функции также можно использовать в окне Task Dialog в разделе с дополнительной технической информацией.