Friday, November 19, 2010

static_assert


    Все никак не уймусь с шаблонами. Один раз у меня возникла некая задача:
Есть какой-то класс, в конструктор которого передается целое число. Требование для пользователя класса - целое должно быть больше нуля и нечетным. Оставался вопрос. Как заставить пользователя передать правильные данные? Для начала нужен правильный комментарий о том что и как, а далее есть варианты:
1. Проверить в конструкторе переданый аргумент, и если он не правильный, то:
   – бросить исключение об ошибке(если можно бросать исключения);
   – вернуть out-parameter как результат работы конструктора.
2. Если аргумент не правильный, то «привести» его к правильному, правда не понятно в какую сторону.
    В принципе я бы выбрал первый вариант и не заморачивался бы. Но вот я вспомнил про проверку во время компиляции которую описывал Александреску. В итоге можно сделать так, что если пользователь класса передаст константу известную во время компиляции – то можно проверить ее при компиляции и если целое не верное – то выдать ошибку. Вот примитивный вариант самой проверки во время компиляции:

// cmnStaticAssert.h
#ifndef __CMN_STATIC_ASSERT_H__
#define __CMN_STATIC_ASSERT_H__

namespace utils
{
    template < bool > struct static_assert;
    template < > struct static_assert < true > { };
} // utils

#define STATIC_ASSERT(val, msg) \
{ \
    utils::static_assert <((val) != 0)> ERROR_##msg; \
    (void)ERROR_##msg; \
}

#endif // __CMN_STATIC_ASSERT_H__

Этот код я взял(немного переделав) из библиотеки Loki. Также есть в boost более продвинутая реализация, но сейчас для идеи хватит и этого.
Теперь его применение:

#include "cmnStaticAssert.h"
#include < crtdbg.h >

template < int TSize = 1 >
class CTest
{
    int size_;
public:
    CTest() : size_(TSize)
    {
        STATIC_ASSERT(TSize % 2 && TSize > 0, invalid_argument_format);
    }

    CTest(int size) : size_(size)
    {
        if (!(size % 2) || size < 1) throw 0;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    // Во время компиляции для константы
    {
        CTest<11> compile_time_test;
    }

    // Во время выполнения для переменной
    {
        int n = 11;
        try
        {
            CTest<> run_time_test(n);
        }
        catch(...) { _ASSERTE(false); }
    }

    return 0;
}

Если в первом случае будет: CTest<12> compile_time_test;
то будет ошибка компиляции:

error C2079: 'ERROR_invalid_argument_format' uses undefined struct 'utils::static_assert<__formal>'    e:\test\test\test.cpp   13

Во втором случае, если int n = 12;
будет сгенерированно исключение и в обработчике сработает assert.
Тоже самое касается параметров меньше единицы.

Thursday, November 4, 2010

'OleInitialize' и зарезервированный параметр 'pvReserved'

HRESULT OleInitialize(
  __in  LPVOID pvReserved // This parameter is reserved and must be NULL
);

Интересный факт, что этот параметр "раньше" был '__in IMalloc* pMalloc',
и использовался для указания своего аллокатора. Это было очень давно(упоминание об этом параметре было в 1994-ом), а параметр остался для совместимости.

Ксати, это касается и 'CoInitialize'.