12 ноября 2007

Integer division with rounding

Понадобилось мне целочисленное деление с округлением результата (точнее не целочисленное, а с фиксированной точкой - но суть-то, как известно, одна). "С округлением" - это когда 10/3=3, а 20/3=7.

С математикой у меня туго, поэтому написал это дело так:
__int64 RoundedDivision(__int64 Dividend, __int64 Divisor)
{
__int64 DoubleResult = (Dividend << 1) / Divisor;
return ((DoubleResult < 0) ? DoubleResult : (DoubleResult + 1)) >> 1;
}
Однако не покидает мысль что можно написать как-то попроще.

PS. Заодно немного потормозил над тем, что при отрицательном знаке DoubleResult не нужно вычитать единичку. Видимо совсем забыл как считать в дополнительном коде.

03 ноября 2007

Policy-based programming

Александреску в своей книге "Современное проектирование на С++" заразил меня тем, что он называет policy-based class design - разработка класса на основе его составляющих - "политик". Примером может являться умный указатель: при разработке обобщенного умного указателя можно не догадываться как именно конечному программисту потребуется от умного указателя:
а) управлять памятью (выделять память, удалять...),
б) реализовывать "владение" объектом (разделять одно значение, копировать...),
в) проверять значение указателя на правильность инициализации (через assert или исключения...),
г) ...

Что можно сделать - написать шаблон класса, который в качестве параметров (шаблона) принимает классы, реализующие эти самые стратегии: управления памятью, владения объектом, и т.д.

Так вот эту же замечательную идею можно легко использовать не только в статике (шаблоны), а и в динамике. Идея такова: для нужных стратегий (policy) пишете интерфес и храните в своем классе указатель (умный, разумеется) на объект этого интерфейсного класса.

Пример:
template <class StoragePolicy, class OwnershipPolicy>
class StaticPolicyDemo
{
void Delete()
{
if (OwnershipPolicy::IsDeletable())
StoragePolicy::Delete(ptr);
}
...
};

class DynamicPolicyDemo
{
class IStoragePolicy
{
virtual void Delete(Object*) = 0;
...
};
class IOwnershipPolicy
{
virtual bool IsDeletable() = 0;
...
};

void Delete()
{
if (_OwnershipPolicy->IsDeletable())
_StoragePolicy->Delete(ptr);
}

DynamicPolicyDemo(PStoragePolicy StoragePolicy, POwnershipPolicy OwnershipPolicy) :
_StoragePolicy(StoragePolicy), _OwnershipPolicy(OwnershipPolicy)
{
}

void SetStoragePolicy(PStoragePolicy StoragePolicy)
{
_StoragePolicy = StoragePolicy;
}

void SetOwnershipPolicy(POwnershipPolicy OwnershipPolicy)
{
_OwnershipPolicy = OwnershipPolicy;
}

typedef std::auto_ptr<IStoragePolicy> PStoragePolicy;
typedef std::auto_ptr<IOwnershipPolicy> POwnershipPolicy;
PStoragePolicy _StoragePolicy;
POwnershipPolicy _OwnershipPolicy;
};
Такая конструкция очень полезна, когда нужно в run-time менять части поведения некоторого объекта. Например, графического контрола Grid это может быть: а) внешний вид графического элемента (skin), б) источник записей, в) фильтр записей, и др. Если часто используются типовые policy, то для их хранения логично использовать shared_ptr (вместо auto_ptr, приведенного в примере).

02 ноября 2007

Default window procedure

При обработке оконных сообщений типичная ситуация такова: обработал сообщение, вернул значение. А если не обработал - тогда уж оно пойдет в DefWindowProc() / DefMDIChildProc (). Оказывается, такой алгоритм не всегда верен.

Например, если не передать WM_SIZE в DefMDIChildProc() для MDI-child окна, то при максимизации этого окна пользователь не сможет восстановить его размер - у окна не будет кнопок minimize и restore (обычно они появляются с правой стороны полосы меню mdi parent-а).

Как мне кажется, общее правило для обработки событий лучше иметь таким: если обработал сообщение, но не нужно ничего возвращать (как в случае уведомительных сообщений типа WM_SIZE) - лучше передать сообщение дальше по цепочке обработчиков. В WTL это можно сделать так:
LRESULT WindowClass::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
do stuff;

return bHandled = 0; // сбрасываем флаг bHandled
}