From: Christian P. <cp...@us...> - 2005-06-30 14:52:22
|
Update of /cvsroot/pclasses/pclasses2/include/pclasses In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31566/include/pclasses Added Files: LexT.h Log Message: - Moved LexT from P::Util to P namespace - Removed fromString()/toString() from Private namespace and put it in a separate Traits::LexT<> class. - Removed non-const accessors --- NEW FILE: LexT.h --- //////////////////////////////////////////////////////////////////////// // LexT.h: the P::LexT class, a simple class for lexically casting // i/ostreamable types. As of 30.06.2005 this class does not only // support i/ostreamable types, as you may provide a Traits // specialisation for a particular type. // // author: st...@s1... // License: Public Domain // // Modified by cp...@se... to use a Traits class for the // to/from string conversion. // //////////////////////////////////////////////////////////////////////// #ifndef P_LexT_h #define P_LexT_h #include <pclasses/Export.h> #include <pclasses/Exception.h> #include <string> #include <iostream> #include <sstream> namespace P { namespace Traits { //! Lexical type conversion traits /*! This traits class is used by P::LexT to convert a given type to/from a string. */ template <typename Type> struct LexT { /*! Returns a string representation of the given object, which must be ostreamble. */ static std::string toString(const Type& val) { std::ostringstream os; os << std::fixed; os << val; return os.str(); } /*! Lexically casts str to Type, returning errorVal if the conversion fails. */ static Type fromString(const std::string& str) throw(ConversionError) { std::istringstream is(str); Type val = Type(); if((is >> val) && is.eof()) return val; throw ConversionError("Could not convert to requested type", P_SOURCEINFO); } }; template < > struct LexT<std::string> { static std::string toString(const std::string& val) { return val; } static std::string fromString(const std::string& str) { return str; } }; template < > struct LexT<char*> { static std::string toString(const char* val) { return val; } static std::string fromString(const std::string& str) { return str; } }; } // !namespace Traits /*! LexT provides a really convenient way to lexically cast strings and other streamable types to/from each other. All parameterized types used by this type must be: - i/o streamable or provide a specialized Traits::LexT<> template. The operators must complement each other. - Assignable. - Default Constructable. This type is fairly light-weight, with only one std::string data member, so it should copy quickly and implicitely use std::string's CoW and reference counting features. Adding reference counting to this class would be of no benefit, and would probably hurt performance, considering that std::string's are optimized in these ways, and this type is simply a proxy for a std::string. For some uses the LexT type can replace the requirement for returning a proxy type from a type's operator[](), as discussed in Scott Meyers' <em>More Effective C++</em>, Item 30. This class originally was such a proxy, and then evolved into a generic solution for POD-based types, which inherently also covers most i/ostreamable types. It is less efficient than specialized proxies for, e.g. (char &), but it is also extremely easy to use, as shown here: <pre> LexT lex = 17; int bogo = lex; ulong bogol = bogo * static_cast<long>(lex); lex = "bogus string"; typedef std::map<LexT,LexT> LMap; LMap map; map[4] = "one"; map["one"] = 4; map[123] = "eat this"; map['x'] = "marks the spot"; map["fred"] = 94.3 * static_cast<double>( map["one"] ); map["fred"] = 10 * static_cast<double>( map["fred"] ); map["123"] = "this was re-set"; int myint = map["one"]; </pre> Finally, Perl-ish type flexibility in C++. :) It gets better: if we're using s11n, we can now save and load these objects at will: <pre> s11nlite::save( map, "somefile.s11n" ); ... LMap * map = s11nlite::load_serializable<LMap>( "somefile.s11n" ); </pre> If s11n is detected by this header it will automatically register a serialization proxy with s11n. See the end of this header file for the exact registration code. This software is released into the Public Domain by it's author, stephan beal (st...@s1...). If you want this class to work with the SIO framework you must include the following header: <pclasses/s11n/proxy/LexT_s11n.h> Change history: 30 Jun 2005 - Removed fromString()/toString() from Private namespace and put it in a separate Traits::LexT<> class. - Removed non-const accessors 28 Dec 2004: - Changed the impl of istream>>() to use getc() instead of getline(), so newlines are preserved. 25 Dec 2004: - Ported into the P::Classes tree. Renamed the class and some functions for P's naming conventions. 25 Nov 2004: - Minor doc updates. - Changed multiple-include guard to allow inclusion of this file twice for purposes of registering LexT with s11n. Before, this header must have been included AFTER including s11n to pick up the registration. Now including this header after including s11n is safe if it has previously been included (and thus didn't pick up s11n registration). 2 Oct 2004: - Accomodated recent changes in libs11n. 22 Aug 2004: - Added ambiguity-buster overloads for operator==() for (const char *) and std::string. 20 Aug 2004: - Added LexT::empty() - Moved LexT::operator==() to a free function. - Added LexT::operator=(const char *) (see API notes for why). 17 Aug 2004: - Initial release. - After-relase: - Added more docs. - Added std::string and (const char *) overloads, to avoid some ambiguities. 16 Aug 2004: - Zen Coding Session. */ class LexT { public: /*! Constructs an empty object. Calling <code>castTo<T>()</code> on an un-populated LexT object will return T(). */ LexT() throw() { } ~LexT() throw() { } /*! Copies rhs's data to this object. */ LexT(const LexT& rhs) : _data(rhs._data) { } /*! Lexically casts f to a string. */ template <typename FromT> LexT(const FromT& f) { _data = Traits::LexT<FromT>::toString(f); } /*! See operator=(const char *) for a note about why this exists. @fixme cproch: see operator=(const char*) */ LexT(const char * str) : _data(str ? str : "") { } /*! lexically casts the value to a ToType, throwing a ConversionError if the cast fails. When calling this function you may need to use the following syntax to avoid compile errors: Foo foo = lex.template castTo<Foo>(); (It's weird, i know, and the first time i saw it, finding the solution to took me days. (Thank you, Nicolai Josuttis!)) However, in normal usage you won't need to use this function, as the generic type conversion operator does the exact same thing: <pre> LexT lex = 17; int foo = lex; </pre> */ template <typename ToType> inline ToType castTo() const throw(ConversionError) { if(!_data.empty()) return Traits::LexT<ToType>::fromString(_data); return ToType(); } /*! Returns a const reference to this object's raw string data. */ inline const std::string& str() const throw() { return _data; } /*! Returns true if this object contains no data, else false. */ inline bool empty() const throw() { return _data.empty(); } /*! Copies rhs's data and returns this object. */ inline LexT& operator=(const LexT& rhs) { if(&rhs != this) _data = rhs._data; return *this; } /*! This overload exists to keep the compiler/linker from generating a new instantiation of this function for each differently-lengthed (const char *) which is assigned to a LexT. @fixme cproch: is this really the case ? i don't see why the compiler should generate a new instance of this method, cause LexT isnt a template class. */ inline LexT& operator=(const char* rhs) { _data = rhs ? rhs : ""; return *this; } /*! Sets this object's value and returns this object. */ template <typename ToType> inline LexT& operator=(const ToType& f) { _data = Traits::LexT<ToType>::toString(f); return *this; } /*! i used to LOVE C++... After writing this function i WORSHIP C++. The grace with which C++ handles this is pure magic, my friends. 16.8.2004 ----- stephan */ template <typename ToType> inline operator ToType() const throw(ConversionError) { return castTo<ToType>(); } /* Overload to avoid ambiguity in some cases. 30.6.2005 cproch: this operator actually produces an amgiguity */ //inline operator std::string () const throw() //{ return _data; } /* Returns the same as str(). 30.6.2005 cproch: this operator actually produces an amgiguity */ //operator const std::string& () const throw() //{ return _data; } /*! Overload to avoid ambiguity in some cases. Useful for mixing C and C++ APIs: <pre> LexT arg = "USER"; LexT user = ::getenv(arg); </pre> */ inline operator const char* () const throw() { return _data.c_str(); } /*! Returns (this-<str() < rhs.str()). */ inline bool operator<(const LexT& rhs) const throw() { return _data < rhs._data; } /*! Returns (this-<str() > rhs.str()). */ inline bool operator>(const LexT& rhs) const throw() { return _data > rhs._data; } /*! Returns lhs.str() == rhs.str() */ inline bool operator==(const LexT& rhs) const throw() { return _data == rhs._data; } /*! Casts lhs to a Type object and returns true only if that object compares as equal to rhs. */ template <typename Type> inline bool operator==(const Type& rhs) const throw(ConversionError) { return castTo<Type>() == rhs; } /*! Avoid an ambiguity... */ inline bool operator==(const std::string& rhs) const throw() { return _data == rhs; } /*! Avoid an ambiguity... If rhs == 0 then this function returns true if lhs.empty(). */ inline bool operator==(const char* rhs) const throw() { if(!rhs) return empty(); return _data == rhs; } /*! Copies a.str() to os. */ friend inline std::ostream& operator<<(std::ostream& os, const LexT& a) { return os << a._data; } /*! Reads from the input stream, appending to a.str() until the stream gives up. If the implementation of this function seems "wrong" to you, please read the justification in this paper: http://s11n.net/papers/lexically_casting.html */ friend inline std::istream& operator>>(std::istream& is, LexT& a) { return std::getline(is, a._data, static_cast<std::istream::char_type>( std::istream::traits_type::eof() )); } private: std::string _data; }; } // !namespace P #endif // !P_LexT_h |