Boost logo

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