четверг, 24 января 2008 г.

Долой switch!!!

Незнаю у кого как, но у меня, при виде оператора switch на 2 страницы, появляются подозрения, что, что-то здесь не так... По сути программист не должен в ручную прописывать все возможные варианты работы кода, за него это должен делать компилятор. В правильно написанной программе (на C++) роль switch чаще всего выполняет таблица виртуальных функций. Но ведь не будешь на каждую мелочь создавать иерархию объектов. В этом случае могут помочь шаблоны... Мне раньше часто приходилось писать уродливый код вроде этого:
int _tmain(int argc, _TCHAR* argv[])
{
    unsigned long value = 123465;
    unsigned char buffer[0x100];
    if ( value <= numeric_limits<unsigned char>::max() )
    {
        unsigned char* ptr = reinterpret_cast<unsigned char*>(buffer);
        *ptr = (unsigned char)value;
    } else if ( value <= numeric_limits<unsigned short>::max() ) {
        unsigned short* ptr = reinterpret_cast<unsigned short*>(buffer);
        *ptr = (unsigned short)value;
    } else if ( value <= numeric_limits<unsigned int>::max() ) {
        unsigned int* ptr = reinterpret_cast<unsigned int*>(buffer);
        *ptr = (unsigned int)value;
    }
    return 0;
}
здесь переменная value записывается в массив, причем в зависимости от ее значения, под ее хранение отводится 1, 2, или 4 байта... код для разных value отличается только типами значений, в остальном он идентичен. Может показаться что проблема притянута за уши, но это не так - это очень серьезная проблема)) Что если вариантов не 3, а скажем 10, а если потребуется добавить еще один? А что если потом потребуется изменить алгоритм ;) Я нашел решение этой проблемы в использовании списков типов. Вот мой вариант такого спска:
class null_t;
template <typename Head, typename Tail>
struct type_list_node_t
{
    typedef Head Type;
    typedef Tail Next;
};
а вот пример использования:
typedef type_list_node_t<unsigned char, 
        type_list_node_t<unsigned short,
            type_list_node_t<unsigned int, null_t> 
        > 
    > my_typelist;
в принципе все очень просто, type_list_node_t - это элемент списка, который может содержать еще один type_list_node_t и т.д. класс null_t служит маркером окончания списка... подробнее можно почитать у Александресску... В общем, не вдаваясь в подробности - следующий код занимается генерацией всех возможных ветвлений, для заданного набора типов, используя для этого рекурсию. Компилятор может это оптимизировать так, что по скорости выполнения, разницы, по сравнению с первым вариантом, не будет.
template<typename T>
struct limits_helper_t;

template<typename Head, typename Tail>
struct limits_helper_t< type_list_node_t<Head, Tail> >
{
    bool operator () (unsigned long value, unsigned char* buffer)
    {
        if ( value <= numeric_limits<Head>::max() )
        {
            Head* ptr = reinterpret_cast<Head*>(buffer);
            *ptr = (Head)value;
            return true;
        }
        limits_helper_t <Tail> next;
        return next(value, buffer);
    }
};

template<>
struct limits_helper_t< null_t >
{
    bool operator () (unsigned long value, unsigned char* buffer)
    {
        return false;
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    unsigned long value = 123465;
    unsigned char buffer[0x100];
    typedef type_list_node_t<unsigned char, 
                        type_list_node_t<unsigned short,
                            type_list_node_t<unsigned int, null_t> 
                        > 
                    > my_typelist;
    limits_helper_t<my_typelist> helper;
    helper(value, buffer);
    return 0;
}
однако чем-то смахивает на функциональное программирование :-)

4 комментария:

Анонимный комментирует...

код выложи плиз:)

Artem комментирует...

случайно наткнулся на этот пост... интересно конечно, но разве не лучше было бы сделать что-нибудь по типу

template<class TProposedSmallestType, class TValue>
bool FillBuffer(TValue Value, void* buffer)
{
if( Value <= std::numeric_limits<TProposedSmallestType>::max() )
{
TProposedSmallestType* ptr = reinterpret_cast<TProposedSmallestType*>(buffer);
*ptr = (TProposedSmallestType)Value;
return true;
}
return false;
}

int test()
{
unsigned long value = 123465;
unsigned char buffer[0x100];

FillBuffer<unsigned char>(value, buffer)
|| FillBuffer<unsigned short>(value, buffer)
|| FillBuffer<unsigned int>(value, buffer);
return 0;
}

Lazin комментирует...

действительно, просто и понятно, не то что у меня =)

Анонимный комментирует...

упражнения аэробики движения аэробики рефераты по физике дипломные по физике українські курсові українські дипломні роботи искусство массажа китай тюннинг автомобилей внешний тюнинг обои для стола обои из сериалов история древний мир рефераты по географии дипломные по географии индия seropol5