Re: Упражнение с templates
От: Павел Кузнецов  
Дата: 24.10.02 16:19
Оценка: 18 (2)
Здравствуйте orangy, Вы писали:

O>parse_cast.


Sorry, подробнее сейчас некогда, может, завтра время еще будет...

Имхо, основная проблема с подобным дизайном заключается в том, что интерфейс как бы рассчитан на проход по последовательности (аргумент — итератор), однако функция не возвращает позицию (итератор) символа, следующего после извлеченного значения. Если начать это делать, то, имхо, в конечном итоге, интерфейс превратится в std::istream :-)

Кроме того, принимать один итератор очень необычно: в частности, это означает, что в контейнере придется хранить лишний терминальный элемент, что не совсем естественно для контейнеров, размер которых известен. Да и вообще, понятие zero-terminated последовательности получается достаточно странноватым :-)

Я бы просто разделил два интерфейса: один принимает пару итераторов, а второй — указатель const char*. Для последнего наличие `\0' в конце вполне естественно.

O>Просьба помочь избавиться от: ret_val_adaptor и it_adaptor ( IT — regards ;) )


Проблема с it_adaptor, на мой взгляд, обусловлена выбором интерфейса. parse_cast как бы ожидает итератор, а ты ему подсовываешь std::string... Включать здесь ad hoc неявное преобразование, на мой взгляд, идеологически некорректно.

Если разделить два интерфейса, как описано выше, то, имхо, parse_cast<>(str.c_str()) уже не выглядит таким страшным. Да и при желании можно просто перегрузить parse_cast для std::basic_string безо всяких it_adaptors.

O>
O>    <...>
O>    std::string test2 = "-12";
O>    std::vector<char> vc(test2.begin(), test2.end());
O>    ival = parse_cast<int>(vc.begin());
O>    <...>


В частности, те же проблемы из-за одного итератора на входе: вектор не содержит последнего `\0' из строки. То что parse_cast<int>(vc.begin()) работает — в данном случае, просто совпадение. Точно так же мог бы быть и seg fault.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Упражнение с templates
От: orangy Россия
Дата: 24.10.02 14:39
Оценка: 10 (1)
Привет,

В некотором царстве, в некотором государстве, был сайт RSDN, а в нём форумы, а в форумах Исходники, а в Исходниках постинг про atoi
Автор: orangy
Дата: 29.08.02

И закралась в голову шальная мысль, по аналогии со boost::lexical_cast (также смотри stream_cast
Автор: Igor Soukhov
Дата: 22.09.02
) сделать parse_cast.
Из этого получилось небольшое упражнение по шаблонам, которое и предлагаю на суд общественности.
Сознательно не положил сразу в Исходники, ибо мне хочется обсудить, доусовершенствовать и т.п. и т.д.
Прошу прощения также за комментарии на английском — привычка-с

Просьба помочь избавиться от: ret_val_adaptor и it_adaptor ( IT — regards )
А также подсказать, где я ошибся, накосячил, перемудрил, недодумал и т.п. Спасибо заранее.
Проверено на MSVC7 и Intel6 — вроде работает.

#include <string>
#include <vector>

// Helper function to skip white space
template<typename It>
inline It skip_ws(It p)
{
    while ((*p == ' ' || *p == '\t') && *(++p)); 
    return p;
}

// just create typename for template argument and specialization
struct hex; 

// Empty template, doesn't containt required info
// If someone will try to parse_cast to unknown type, error will be issued at compile time
template<typename C>
struct parser
{
    /*
        this is conversion result type

        typedef **** return_type;
        
        this is static function which parses input (iterator) and returns value
        template<typename It>
        static return_type process(It p)
    */
};

// specializations are pretty obvious, see also my post about atoi
template<>
struct parser<unsigned int>
{
    
    typedef unsigned int return_type;

    template<typename It>
    static return_type process(It p)
    {
        p=skip_ws(p);
        return_type val=0;
        while (*p>='0' && *p<='9') { val*=10; val+=*p - '0'; ++p; }
        return val;
    }
};

template<>
struct parser<unsigned long>
{
    typedef unsigned long return_type;

    template<typename It>
    static return_type process(It p)
    {
        p=skip_ws(p);
        return_type val=0;
        while (*p>='0' && *p<='9') { val*=10; val+=*p - '0'; ++p; }
        return val;
    }
};

template<>
struct parser<int>
{
    typedef int return_type;

    template<typename It>
    static return_type process(It p)
    {
        p=skip_ws(p);
        return_type val=0;
        int sign=1;
        sign*=(*p == '-' && (++p,true))?-1:((*p == '+' && (++p,true)),1);
        while (*p>='0' && *p<='9') { val*=10; val+=*p - '0'; ++p; }
        return val*sign;
    }
};

template<>
struct parser<long>
{
    typedef long return_type;

    template<typename It>
    static return_type process(It p)
    {
        p=skip_ws(p);
        return_type sign=1, val=0;
        sign*=(*p == '-' && (++p,true))?-1:((*p == '+' && (++p,true)),1);
        while (*p>='0' && *p<='9') { val*=10; val+=*p - '0'; ++p; }
        return val*sign;
    }
};

// specialization for conversion from hex
// uses artificial type to distinguish from unsigned int
template<>
struct parser<hex>
{
    typedef unsigned long return_type;

    template<typename It>
    static return_type process(It p)
    {
        p=skip_ws(p);
        return_type val=0;
        // skip 0x if any
        if (*p == '0') if (*++p == 'x') ++p;
        while ((*p>='0' && *p<='9') || (*p>='a' && *p<='f') || (*p>='A' && *p<='F')) 
        {
            val*=16; 
            val+=(*p<='9')?(*p - '0'):((*p<='F')?(*p-'A'+10):(*p-'a'+10)); 
            ++p; 
        }
        return val;
    }
};

/*    This adaptor is for MSVC, which won't understand the following declaration
 *
 *    template<typename C, typename It>
 *  typename parser<C>::return_type parse_cast(It p);
 *  error C2039: 'return_type' : is not a member of 'parser<C>'
 *
 *  May be someone can explain, why?
 */ 
template<typename C>
struct ret_val_adaptor
{
    typedef parser<C>::return_type return_type;
};

// Adaptor for iterator, so we can specify conversion from non-iterators to suitable types
// In good compiler we could use partial specialization of parse_cast instead.
template<typename It>
struct it_adaptor
{
    static const It &convert(const It &p) { return p; }
};

template<>
struct it_adaptor<std::string>
{
    static const char* convert(const std::string &p) { return p.c_str(); }
};

template<typename C, typename It>
typename ret_val_adaptor<C>::return_type parse_cast(It p)
{
    return parser<C>::process(it_adaptor<It>::convert(p));
}

int main()
{
    // few tests to ensure correct function selection
    unsigned int uival = parse_cast<unsigned int>("123");
    int ival = parse_cast<int>("-2123");
    unsigned int hexval = parse_cast<hex>("0xdeAdbEaf");
    unsigned long ulval = parse_cast<unsigned long>("-2123");
    long lval = parse_cast<long>("   -2123");
    
    std::string test = "ff";
    uival = parse_cast<hex>(test.begin());

    std::string test2 = "-12";
    std::vector<char> vc(test2.begin(), test2.end());
    ival = parse_cast<int>(vc.begin());
    lval = parse_cast<long>(test2);
}
RSDN@Home 1.0 alpha 12 (tester's build)
"Develop with pleasure!"
Re[2]: Упражнение с templates
От: orangy Россия
Дата: 24.10.02 16:42
Оценка:
Здравствуйте Павел Кузнецов, Вы писали:

ПК>Sorry, подробнее сейчас некогда, может, завтра время еще будет...

Спасибо

ПК><...>интерфейс как бы рассчитан на проход по последовательности (аргумент — итератор), однако функция не возвращает позицию (итератор) символа, следующего после извлеченного значения. Если начать это делать, то, имхо, в конечном итоге, интерфейс превратится в std::istream

ПК><...>означает, что в контейнере придется хранить лишний терминальный элемент, что не совсем естественно для контейнеров, размер которых известен.

Да-да, я об этом подумал и не раз. Просто пока не придумал как это сделать правильно без лишней сложности. Действительно, не хотелось бы в istream упасть

ПК>Я бы просто разделил два интерфейса: один принимает пару итераторов, а второй — указатель const char*. Для последнего наличие `\0' в конце вполне естественно.


Подумаю...

O>>Просьба помочь избавиться от: ret_val_adaptor и it_adaptor ( IT — regards )


ПК>Проблема с it_adaptor, на мой взгляд, обусловлена выбором интерфейса. parse_cast как бы ожидает итератор, а ты ему подсовываешь std::string... Включать здесь ad hoc неявное преобразование, на мой взгляд, идеологически некорректно.


Да, наверное, буду думать.

ПК><...> Точно так же мог бы быть и seg fault.

Это понятно, я думал о границах, но пока безрезультатно. Там еще вопросы будут с потоками, хотелось бы для потоков тоже сделать вариант. Буду ждать завтрашней реакции и думать
RSDN@Home 1.0 alpha 12 (tester's build)
"Develop with pleasure!"
Re: Упражнение с templates
От: c-smile Канада http://terrainformatica.com
Дата: 24.10.02 21:19
Оценка:
Здравствуйте orangy, это было круто!

Но смею предложить свой вариант:

int main()
{
    // few tests to ensure correct function selection
    unsigned int uival     = atoui("123");
    int ival         = atoi("-2123");
    unsigned int hexval     = atoui("0xdeAdbEaf");
    unsigned long ulval     = atoul("-2123");
    long lval         = atol("   -2123");
    ...
    
}


//it might be not so aesthetic but human readable though...


"Как жаль что дедушка Оккам не придумал саблю" (с) c-smile, 2002
Re[2]: Упражнение с templates
От: orangy Россия
Дата: 25.10.02 04:48
Оценка:
Здравствуйте c-smile, Вы писали:

CS>Здравствуйте orangy, это было круто!

CS>Но смею предложить свой вариант:

Позвольте позанудствовать в ответ

CS>
CS>int main()
CS>{
CS>    unsigned int uival     = atoui("123");
CS>    int ival         = atoi("-2123");
CS>    long lval         = atol("   -2123");
CS>}
CS>


Во-первых atoui в стандарте нет. atol и atoi есть, согласно С.2, однако согласно стандарту С они "subsumed by" strtol. В этой группе также есть strtoul.
Во-вторых, ваше предложение не будет работать с итераторами (например istream_iterator) и std::string, что не есть удобно...
В-третьих, см.subject — это упражнение на шаблоны, специализацию, обход отсутствия частичной специализации и т.п. Думаю, не мне одному будет полезно
В-четвертых, шаблонная реализация позволяет правильно и типобезопасно обрабатывать некоторые интересные (и важные) случаи:

template<class T>
class my_number
{
    T val;
public:
    ... всё что нужно для счастья ...

    template<class It>
    my_number<T> &operator=(const It&) { val = parse_cast<T>(It); }
};

std:vector<my_number<unsigned int> > v(10);
v[0] = "123";
v[1] = istream_iterator(f);



CS>//it might be not so aesthetic but human readable though...

В D&N папа Страуструп пишет, что любая явная конверсия (а это именно она) является потенциальной проблемой и должна быть явно заметна. Это одна из причин появления синтаксиса dynamic_cast, другая — синтаксическая схожесть с шаблонными функциями. Поэтому я и выбрал аналогичный синтаксис.

Конечно, в предложенном мной варианте множество проблем, однако как стартовая точка — пойдёт
RSDN@Home 1.0 alpha 12 (tester's build)
"Develop with pleasure!"
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.