|
Boost : |
From: Oleg Abrosimov (beholder_at_[hidden])
Date: 2006-04-15 11:04:15
It seems that I'm going in the same direction too, but with a little
different approach. I belief it would be fruitful to merge our ideas and
efforts, of cause.
till now I have an interface specification and test code, but no
implementation, so fill free to compile the .cpp file, but linking is
not possible yet ;-)
Oleg Abrosimov.
// Copyright Oleg Abrosimov 2006.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// the test for proposal for type to string and
// string to type conversions for C++
#include "scvt.hpp"
using namespace boost;
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <cassert>
using namespace std;
// test all overloads of to_string function
string test_ovl()
{
string str =
to_string(12.3) + '\n' +
to_string(12.3, locale("")) + '\n' +
to_string(true, ios_base::boolalpha) + '\n' +
to_string(12.3, ios_base::hex, locale("")) + '\n';
return str;
}
// test all overloads of to_string function
wstring wtest_ovl()
{
wstring sendl;
sendl += wcout.widen('\n');
wstring str =
to_wstring(12.3) + sendl +
to_wstring(12.3, locale("")) + sendl +
to_wstring(true, ios_base::boolalpha) + sendl +
to_wstring(12.3, ios_base::hex, locale("")) + sendl;
return str;
}
// readability improvement test for to_string_cvt object
template <typename TChar>
basic_string<TChar> test_readabilty()
{
basic_ocvt<TChar> cvt(ios_base::hex, locale(""));
basic_string<TChar> sendl;
sendl = cvt.widen('\n');
basic_string<TChar> str =
cvt(12.3) + sendl +
cvt(12.4) + sendl +
cvt(12.5) + sendl +
cvt(12.6) + sendl +
cvt(12.7) + sendl;
return str;
}
// performance test for to_string_cvt object
template <typename TChar>
basic_string<TChar> test_performance()
{
basic_ocvt<TChar> cvt(ios_base::hex, locale(""));
basic_string<TChar> sendl;
sendl = cvt.widen('\n');
basic_string<TChar> str;
for (int i = 0; i < 100; ++i) {
str = cvt(i) + sendl;
}
return str;
}
// test functor functionality of to_string_cvt object
template <typename TChar>
basic_string<TChar> test_functor()
{
basic_ocvt<TChar> cvt(ios_base::hex, locale(""));
vector<double> vec_doubles(10, 1.2);
vector< basic_string<TChar> > vec_strings;
transform(
vec_doubles.begin(), vec_doubles.end(), // from
back_inserter(vec_strings), // to
cvt
);
std::basic_ostringstream<TChar> os;
basic_string<TChar> space;
space += os.widen(' ');
copy(
vec_strings.begin(), vec_strings.end(), // from
ostream_iterator<basic_string<TChar>, TChar>(os, space.c_str()) // to
);
return os.str();
}
template <typename TChar>
basic_string<TChar> test_to_string()
{
basic_string<TChar> str =
test_readabilty<TChar>() +
test_performance<TChar>() +
test_functor<TChar>();
return str;
}
// test all overloads of string_to function
void test_ovl1()
{
string s("12.3");
double d = string_to<double>(s);
assert(d == 12.3);
}
// test all overloads of to_string function
void wtest_ovl1()
{
string s("12.3");
wstring ws(s.begin(), s.end());
double d = string_to<double>(ws);
assert(d == 12.3);
}
// performance test for icvt object
template <typename TChar>
void test_performance1()
{
basic_icvt<TChar> cvt(ios_base::hex, locale(""));
basic_icvt_functor<int, TChar> cvtf(cvt);
string s("22");
basic_string<TChar> str(s.begin(), s.end());
for (int i = 0; i < 100; ++i) {
int j = cvtf(str);
int k;
cvt(str, k);
cvt(str, k, 0);
}
}
// test functor functionality of basic_i(o)cvt objects
template <typename TChar>
void test_functor1()
{
basic_ocvt<TChar> cvt(ios_base::hex, locale(""));
vector<double> vec_doubles(10, 1.2);
vector< basic_string<TChar> > vec_strings;
transform(
vec_doubles.begin(), vec_doubles.end(), // from
back_inserter(vec_strings), // to
cvt
);
basic_icvt<TChar> cvt1(ios_base::hex, locale(""));
basic_icvt_functor<double, TChar> cvtf1(cvt1);
vector<double> vec_doubles1(10, 0.0);
transform(
vec_strings.begin(), vec_strings.end(), // from
vec_doubles1.begin(), // to
cvtf1
);
for (int i = 0; i != vec_doubles.size(); ++i) {
assert(vec_doubles[1] == vec_doubles1[i]);
}
}
// test functor functionality of basic_i(o)cvt objects
template <typename TChar>
void test_functor2()
{
basic_iocvt<TChar> cvt(ios_base::hex, locale(""));
vector<double> vec_doubles(10, 1.2);
vector< basic_string<TChar> > vec_strings;
transform(
vec_doubles.begin(), vec_doubles.end(), // from
back_inserter(vec_strings), // to
cvt
);
basic_icvt_functor<double, TChar> cvtf1(cvt);
vector<double> vec_doubles1(10, 0.0);
transform(
vec_strings.begin(), vec_strings.end(), // from
vec_doubles1.begin(), // to
cvtf1
);
for (int i = 0; i != vec_doubles.size(); ++i) {
assert(vec_doubles[1] == vec_doubles1[i]);
}
}
template <typename TChar>
void test_string_to()
{
test_performance1<TChar>();
test_functor1<TChar>();
test_functor2<TChar>();
}
int main(int argc, char* argv[])
{
cout
<< test_ovl()
<< test_to_string<char>();
wcout
<< wtest_ovl()
<< test_to_string<wchar_t>();
test_ovl1();
test_string_to<char>();
wtest_ovl1();
test_string_to<wchar_t>();
return 0;
}
// scvt.hpp
// Copyright Oleg Abrosimov 2006.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// the proposal for type to string and
// string to type conversions for C++
#ifndef BOOST_STRING_CONVERSIONS_INCLUDED
// 1) type to string:
// requirements:
// 1) controlling the conversions via facets (locales)
// 2) compactness of resulting code
// 3) full power of iostreams in simple interface.
// All fuctionality accessible with iostreams
// (through manipulators) should be accessible.
#include <string>
#include <locale>
#include <ios>
#include <boost/operators.hpp>
/*
conversion (cvt) is modeled after iostreams.
The relation is:
external data sources <-> user data (in a program)
ios: external data sources - char sequence (from some device)
cvt: external data sources - some string type (from user input)
The hierarchy in cvt is the same as in ios:
cvt_base
^
|
basic_cvt<>
^ ^
/ \
basic_icvt<> basic_ocvt<>
^ ^
\ /
basic_iocvt<>
Templated classes in this hierarchy are parameterized
on character type (to support third-party strings)
and on a string type, defaults to std::basic_string<char_type>
rationale: to provide native support for any string type
*/
namespace boost {
// common functionality independent of char_type
class cvt_base
{
public :
// the state of conversion (like iostate)
// erange means result is out_of_range of target type
// fail means that string format can not be parsed
// it must not be a typedef because we want to
// overload on it
struct cvtstate : bitwise<cvtstate>
{
unsigned int value;
explicit cvtstate(int val) : value(val) {}
cvtstate& operator |= (cvtstate const& s) {
value |= s.value;
}
cvtstate& operator &= (cvtstate const& s) {
value &= s.value;
}
cvtstate& operator ^= (cvtstate const& s) {
value ^= s.value;
}
};
static const cvtstate goodbit;
static const cvtstate failbit;
static const cvtstate erangebit;
cvt_base();
explicit cvt_base(std::locale const&);
explicit cvt_base(std::ios_base::fmtflags);
cvt_base(std::ios_base::fmtflags, std::locale const&);
// exactly as in ios_base
std::locale getloc() const;
std::locale imbue(const std::locale&);
std::ios_base::fmtflags flags() const;
std::ios_base::fmtflags flags(std::ios_base::fmtflags);
std::streamsize precision() const;
std::streamsize precision(std::streamsize);
std::streamsize width() const;
std::streamsize width(std::streamsize);
void setf(std::ios_base::fmtflags mask);
std::ios_base::fmtflags setf(std::ios_base::fmtflags mask, std::ios_base::fmtflags unset);
void unsetf(std::ios_base::fmtflags mask);
// exactly as in basic_ios<>
cvt_base& copyfmt(const cvt_base&);
// cvtstate related operations
bool bad() const;
bool fail() const;
bool erange() const;
bool good() const;
void clear(cvtstate s = goodbit);
cvtstate exceptions() const;
cvtstate exceptions(cvtstate);
cvtstate rdstate() const;
void setstate(cvtstate);
};
const cvt_base::cvtstate
cvt_base::goodbit = cvt_base::cvtstate(0);
const cvt_base::cvtstate
cvt_base::failbit = cvt_base::cvtstate(1);
const cvt_base::cvtstate
cvt_base::erangebit = cvt_base::cvtstate(2);
// common char_type dependent functionality
template <
typename TChar,
typename TStr = std::basic_string<TChar>
>
class basic_cvt :
public cvt_base
{
typedef basic_cvt<TChar, TStr> this_type;
public :
typedef TChar char_type;
typedef TStr string_type;
basic_cvt();
explicit basic_cvt(std::locale const&);
explicit basic_cvt(std::ios_base::fmtflags);
basic_cvt(std::ios_base::fmtflags, std::locale const&);
// exactly as in basic_ios<>
char_type fill() const;
char_type fill(char_type);
char_type widen(char);
char narrow(char_type, char = '\0');
// safe bool idiom
typedef char_type * this_type::*unspecified_bool_type; //exposition only
operator unspecified_bool_type() const;
};
// 1) it can be used as a functor in std algorithms
// 2) it can be used to improve performance
// 3) it can be used to improve code readability, especially when
// non-default fmtflags or locale is used
template <
typename TChar,
typename TStr = std::basic_string<TChar>
>
class basic_ocvt :
public virtual basic_cvt<TChar, TStr>
{
public :
typedef TChar char_type;
typedef TStr string_type;
basic_ocvt();
explicit basic_ocvt(std::locale const&);
explicit basic_ocvt(std::ios_base::fmtflags);
basic_ocvt(std::ios_base::fmtflags, std::locale const&);
// can throw bad_alloc error
template <typename T>
string_type operator() (T const&);
};
typedef basic_ocvt<char> ocvt;
typedef basic_ocvt<wchar_t> wocvt;
// auxilary functions: to_string/to_wstring
// can throw bad_alloc error
#define BOOST_PP_DECLARE_TO_STRING_FUNCS(STR_TYPE) \
template <typename T> \
std::STR_TYPE to_##STR_TYPE(T const&); \
template <typename T> \
std::STR_TYPE to_##STR_TYPE(T const&, std::locale const&); \
template <typename T> \
std::STR_TYPE to_##STR_TYPE(T const&, std::ios_base::fmtflags); \
template <typename T> \
std::STR_TYPE to_##STR_TYPE(T const&, std::ios_base::fmtflags, std::locale const&);
BOOST_PP_DECLARE_TO_STRING_FUNCS(string)
BOOST_PP_DECLARE_TO_STRING_FUNCS(wstring)
#undef BOOST_PP_DECLARE_TO_STRING_FUNCS
} // namespace boost
// 2) string to type:
// requirements:
// 0) the input string should be valid, in a sense that the following code
// should works as expected:
// std::istringstream is(input_string);
// Type t;
// is >> t;
//
// 1) built-in integral types (int, short, char, long and unsigned versions)
// 2) built-in floating point types (float, double)
// 3) UDT
//
// (1) and (2) are processed with stoXXX funcs
// (3) is processed with std::stringstream
//
// Throws: invalid_argument if no conversion could be performed.
// Throws: range_error if the converted value is outside the range
// of representable values for the return type.
//
// 1) error handling and reporting. (what kind of error occured?)
// * optionally report failing without exceptions raising
// 2) check if the output was actually generated from the complete input
// example: lexical_cast<double>("1,2")
// * the issue here: is it required to the string given
// be a full representation of only one entity?
// It is different from iostreams-based approach,
// but seems reasonable enough.
// 3) controlling the conversions via facets (locales)
// 4) performance should be guaranted for built-in types at least.
// It can be done in terms of strtod-like functions
// 5) compactness of resulting code
// 6) full power of iostreams in simple interface.
// All fuctionality accessible with iostreams
// (through manipulators) should be accessible.
namespace boost {
// 1) it can be used as a functor in std algorithms
// through basic_icvt_functor adapter
// 2) it can be used to improve performance when many input is done
// 3) it can be used to improve code readability, especially when
// non-default std::ios_base::fmtflags or locale is used
template <
typename TChar,
typename TStr = std::basic_string<TChar>
>
class basic_icvt :
public virtual basic_cvt<TChar, TStr>
{
public :
typedef TChar char_type;
typedef TStr string_type;
basic_icvt();
explicit basic_icvt(std::locale const&);
// hex, oct, dec seems to be usefull here
// skipws is set by default
explicit basic_icvt(std::ios_base::fmtflags);
basic_icvt(std::ios_base::fmtflags, std::locale const&);
// constructors with exception info provided
basic_icvt(cvtstate except);
basic_icvt(cvtstate except, std::locale const&);
basic_icvt(std::ios_base::fmtflags, cvtstate except);
basic_icvt(std::ios_base::fmtflags, cvtstate except, std::locale const&);
// Throws: invalid_argument if no conversion could be performed
// and failbit is set
// Throws: range_error if the converted value is outside the range
// of representable values for the return type and erangebit is set.
template <typename Target>
void operator() (string_type const&, Target&);
template <typename Target>
void operator() (const char_type *const, Target&);
// we don't care about possible errors here.
// just return _default if unsuccessful.
// Note: exceptions are not thrown,
// but cvtstate is set to the reason why conversion was failed
template <typename Target>
void operator() (string_type const&, Target&, Target const& _default);
template <typename Target>
void operator() (const char_type *const, Target&, Target const& _default);
};
typedef basic_icvt<char> icvt;
typedef basic_icvt<wchar_t> wicvt;
// it provides interface for standart algorithms
// basic_icvt<char> cvt;
// basic_icvt_functor<int, char> cvtf(cvt);
// int i = cvtf("11");
// assert (i == 11);
//
// it can be used with basic_iocvt<> as well
//
// alternative whould be to add a Target parameter to
// basic_icvt<>, but it means that user should instantiate
// new basic_icvt<> for each Target type in her code.
// it whould be inefficient.
template <
typename Target,
typename TChar,
typename TStr = std::basic_string<TChar>
>
class basic_icvt_functor
{
public :
typedef TChar char_type;
typedef TStr string_type;
typedef basic_icvt<TChar, TStr> basic_icvt_type;
typedef Target result_type;
basic_icvt_functor(basic_icvt_type& _icvt) :
m_picvt(&_icvt) {}
result_type operator() (string_type const& src) {
result_type t;
(*m_picvt)(src, t);
return t;
}
result_type operator() (const char_type *const src) {
result_type t;
(*m_picvt)(src, t);
return t;
}
result_type operator() (string_type const& src, result_type const& _default) {
result_type t;
(*m_picvt)(src, t, _default);
return t;
}
result_type operator() (const char_type *const src, result_type const& _default) {
result_type t;
(*m_picvt)(src, t, _default);
return t;
}
private :
basic_icvt_type* m_picvt;
};
// template typedef
template <typename Target>
class icvt_functor :
public basic_icvt_functor<Target, char, std::string>
{
public :
typedef char char_type;
typedef std::string string_type;
typedef basic_icvt<char, std::string> basic_icvt_type;
typedef Target result_type;
icvt_functor(basic_icvt_type& _icvt) :
basic_icvt_functor<Target, char, std::string>(_icvt) {}
};
// template typedef
template <typename Target>
class wicvt_functor :
public basic_icvt_functor<Target, wchar_t, std::wstring>
{
public :
typedef wchar_t char_type;
typedef std::wstring string_type;
typedef basic_icvt<wchar_t, std::wstring> basic_icvt_type;
typedef Target result_type;
wicvt_functor(basic_icvt_type& _icvt) :
basic_icvt_functor<Target, wchar_t, std::wstring>(_icvt) {}
};
// versions with "T const&" argument don't generate exceptions
// or report error conditions, they returns the value specified with
// the parameter in question instead
//
// if no default value and no "cvt_base::cvtstate&" are provided
// it can generate invalid_argument or range_error exceptions
//
// versions with "cvt_base::cvtstate&" argument
// don't generate exceptions, but write reason for error
// in parameter in question instead.
// The default constructed value is returned in this case.
//
// mixed case returns the default parameter and writes reason for error
// in "cvt_base::cvtstate&" parameter
//
// "std::ios_base::fmtflags" parameter can be used to specify
// base for integral types: oct|dec|hex
#define BOOST_PP_DECLARE_STRING_TO_CONVERTOR_FUNCS(STR_TYPE) \
template <typename T> \
T string_to(STR_TYPE); \
template <typename T> \
T string_to(STR_TYPE, T const&); \
template <typename T> \
T string_to(STR_TYPE, cvt_base::cvtstate&); \
template <typename T> \
T string_to(STR_TYPE, T const&, cvt_base::cvtstate&); \
template <typename T> \
T string_to(STR_TYPE, std::locale const&); \
template <typename T> \
T string_to(STR_TYPE, T const&, std::locale const&); \
template <typename T> \
T string_to(STR_TYPE, std::locale const&, \
cvt_base::cvtstate&); \
template <typename T> \
T string_to(STR_TYPE, T const&, std::locale const&, \
cvt_base::cvtstate&); \
template <typename T> \
T string_to(STR_TYPE, std::ios_base::fmtflags); \
template <typename T> \
T string_to(STR_TYPE, T const&, \
std::ios_base::fmtflags); \
template <typename T> \
T string_to(STR_TYPE, std::ios_base::fmtflags, \
cvt_base::cvtstate&); \
template <typename T> \
T string_to(STR_TYPE, T const&, std::ios_base::fmtflags, \
cvt_base::cvtstate&); \
template <typename T> \
T string_to(STR_TYPE, std::ios_base::fmtflags, \
std::locale const&); \
template <typename T> \
T string_to(STR_TYPE, T const, std::ios_base::fmtflags, \
std::locale const&); \
template <typename T> \
T string_to(STR_TYPE, std::ios_base::fmtflags, \
std::locale const&, cvt_base::cvtstate&); \
template <typename T> \
T string_to(STR_TYPE, T const&, std::ios_base::fmtflags, \
std::locale const&, cvt_base::cvtstate&);
BOOST_PP_DECLARE_STRING_TO_CONVERTOR_FUNCS(std::string const&)
BOOST_PP_DECLARE_STRING_TO_CONVERTOR_FUNCS(std::wstring const&)
BOOST_PP_DECLARE_STRING_TO_CONVERTOR_FUNCS(const char *const)
BOOST_PP_DECLARE_STRING_TO_CONVERTOR_FUNCS(const wchar_t *const)
#undef BOOST_PP_DECLARE_STRING_TO_CONVERTOR_FUNCS
// 1) it can be used as a functor in std algorithms
// through basic_icvt_functor adapter
// 2) it can be used to improve performance when many input is done
// 3) it can be used to improve code readability, especially when
// non-default std::ios_base::fmtflags or locale is used
template <
typename TChar,
typename TStr = std::basic_string<TChar>
>
class basic_iocvt :
public basic_icvt<TChar, TStr>,
public basic_ocvt<TChar, TStr>
{
public :
typedef TChar char_type;
typedef TStr string_type;
basic_iocvt();
explicit basic_iocvt(std::locale const&);
// hex, oct, dec seems to be usefull here
// skipws is set by default
explicit basic_iocvt(std::ios_base::fmtflags);
basic_iocvt(std::ios_base::fmtflags, std::locale const&);
// constructors with exception info provided
basic_iocvt(cvtstate except);
basic_iocvt(cvtstate except, std::locale const&);
basic_iocvt(std::ios_base::fmtflags, cvtstate except);
basic_iocvt(std::ios_base::fmtflags, cvtstate except, std::locale const&);
// make it visible in an equal degree
using basic_icvt<TChar, TStr>::operator ();
using basic_ocvt<TChar, TStr>::operator ();
};
typedef basic_iocvt<char, std::string> iocvt;
typedef basic_iocvt<wchar_t, std::wstring> wiocvt;
} // namespace boost
#define BOOST_STRING_CONVERSIONS_INCLUDED
#endif //BOOST_STRING_CONVERSIONS_INCLUDED
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk