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>
В частности, те же проблемы из-за одного итератора на входе: вектор не содержит последнего `\0' из строки. То что parse_cast<int>(vc.begin()) работает — в данном случае, просто совпадение. Точно так же мог бы быть и seg fault.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
) сделать parse_cast.
Из этого получилось небольшое упражнение по шаблонам, которое и предлагаю на суд общественности.
Сознательно не положил сразу в Исходники, ибо мне хочется обсудить, доусовершенствовать и т.п. и т.д.
Прошу прощения также за комментарии на английском — привычка-с
Просьба помочь избавиться от: ret_val_adaptor и it_adaptor ( IT — regards )
А также подсказать, где я ошибся, накосячил, перемудрил, недодумал и т.п. Спасибо заранее.
Проверено на MSVC7 и Intel6 — вроде работает.
#include <string>
#include <vector>
// Helper function to skip white spacetemplate<typename It>
inline It skip_ws(It p)
{
while ((*p == ' ' || *p == '\t') && *(++p));
return p;
}
// just create typename for template argument and specializationstruct hex;
// Empty template, doesn't containt required info
// If someone will try to parse_cast to unknown type, error will be issued at compile timetemplate<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 atoitemplate<>
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 inttemplate<>
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 anyif (*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 selectionunsigned 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);
}
Здравствуйте Павел Кузнецов, Вы писали:
ПК>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.
Это понятно, я думал о границах, но пока безрезультатно. Там еще вопросы будут с потоками, хотелось бы для потоков тоже сделать вариант. Буду ждать завтрашней реакции и думать
int main()
{
// few tests to ensure correct function selectionunsigned 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
Здравствуйте 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, другая — синтаксическая схожесть с шаблонными функциями. Поэтому я и выбрал аналогичный синтаксис.
Конечно, в предложенном мной варианте множество проблем, однако как стартовая точка — пойдёт