27 февраля 2007

GetOpenFileName issues

MSDN пишет:
OFN_NOCHANGEDIR
Restores the current directory to its original value if the user changed the directory while searching for files.
Windows NT 4.0/2000/XP: This flag is ineffective for GetOpenFileName.

Однако под XP этот флаг великолепно работает. Интересно, что они имели ввиду под "ineffective"?

24 февраля 2007

IDispatch Wrapper

Работать с COM-объектами из pure C довольно неудобно. Тут одназначно нужно использовать разные C++ wrapper-ы и helper-ы. Некоторые из них доступны "из коробки": _com_ptr_t, _variant_t, директива #import. Но с IDispatch все же не особо удобно работать, как например в Visual Basic.

Немного погуглив обнаружил отличный wrapper для IDispatch by Mike Morearty. Позволяет намного упростить жизнь:
CDispatchPtr htmldoc = ...;
_bstr_t html = htmldoc.Get("body").Get("innerHTML");
htmldoc.Put("title", "New Title");
htmldoc.Get("body").Get("firstChild").Invoke("insertAdjacentText", "afterBegin", "hello world");

Надо будет еще в дополнение к нему написать wrapper для COM-коллекций в STL-совместимые контейнеры.

PS. Хотя в этом IDispatch wrapper-е явно не сказано про лицензию, но по почте Майк ответил "you're free to use the code, and to modify it."

19 февраля 2007

The sence of Boost.Ref

Неприятная особенность boost::bind заключается в том, что все параметры передаются по значению. Тем самым происходит "обрезание" производного объекта до его базового класса в случае, если передается ссылка на базовый класс.

Выход - использование boost::reference_wrapper, который инкапсулирует в себя ссылку и уже далее передается по значению:
class base
{
public:
virtual std::string whoami() const { return "base"; }
virtual ~base() {};
};

class derived : public base
{
public:
virtual std::string whoami() const { return "derived"; }
};

void whoisthis(const base& obj)
{
std::cout << obj.whoami() << std::endl;
}

void FunctorsTest3()
{
derived d;
base& b = d;

whoisthis(d); // works ok
boost::bind(whoisthis, b)(); // cut derived to base
boost::bind(whoisthis, boost::ref(b))(); // works ok
}

15 февраля 2007

Loki::Functor vs boost::function

Исследовал пути решения простенькой задачи.

Задача

1. Есть иерархия классов
class Base {};
class Derived : public Base {};
...

2. Есть функторы, принимающие ссылки на Base, Derived, etc...

3. Есть динамический массив функторов из п.2

Задача: обработать объект из иерархии Base функторами из массива п.3.

Исходные данные

Функторы, входящие в массив:
void Handler1(Base&);
void Handler2(Derived&);

Вспомогательный "конвертер" функторов для приведения к их одному типу:
template <typename T>
void HandlerConversion(void (*Handler)(T&), Base& c)
{
if (T* t = dynamic_cast<T*>(&c))
Handler(*t);
}

Loki

Вспомнив, что функторы есть в Loki, попробовал начать с этой библиотеки:
typedef Loki::Functor<void, LOKI_TYPELIST_1(Control&)> ControlFunctor;
typedef std::vector<ControlFunctor> FunctorVector;

И тут началось...

Такой вод код работает:
template <class T>
void RegisterHandler(FunctorVector& v, void (*Handler)(T&))
{
Loki::Functor<void, LOKI_TYPELIST_2(void(*)(T&), Control&)> cf(HandlerConversion<T>);
v.push_back(Loki::BindFirst(cf, Handler));
}

А такой вот вылетает с ошибкой из-за проблем с auto_ptr внутри Loki::Functor:
template <class T>
void RegisterHandler(FunctorVector& v, void (*Handler)(T&))
{
v.push_back(Loki::BindFirst(Loki::Functor<void, LOKI_TYPELIST_2(void(*)(T&), Control&)>(HandlerConversion<T>), Handler));
}

Дальше - больше. Если объявить переменную FunctorVector v; внутри тестовой main(), то все работает отлично, а если как глобальную переменную - вылетает при выходе из main(). Помогает v.clear(); в конце main(). Жесть какая-то. Подобную веселую реализацию Loki::Functor не решился использовать в рабочем проекте.

Boost

Boost тоже имеет реализацию функторов - boost::function:
typedef boost::function<void (Base&)> ControlFunctor;
typedef std::vector<ControlFunctor> FunctorVector;


Здесь все гораздо проще:
template <class T>
void RegisterHandler(FunctorVector& v, void (*Handler)(T&))
{
v.push_back(boost::bind(HandlerConversion<T>, Handler, _1));
}

И главное - работает как часы (в отличие от Loki).