From: stephan b. <sg...@us...> - 2004-12-25 01:03:38
|
Update of /cvsroot/pclasses/pclasses2/include/pclasses/Util In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv28842/include/pclasses/Util Added Files: LexT.h Log Message: egg --- NEW FILE: LexT.h --- #ifndef p_UTIL_LEXT_HPP_INCLUDED #define p_UTIL_LEXT_HPP_INCLUDED 1 //////////////////////////////////////////////////////////////////////// // LexT.hpp: the lext::LexT class //////////////////////////////////////////////////////////////////////// #include <string> #include <sstream> #include <map> namespace P { namespace Util { /** The functions in the Private namespace should not be used by client code. */ namespace Private { /** Lexically casts str to a value_type, returning errorVal if the conversion fails. TODO: implement the following suggestion from Kai Unger <kai...@ha...> (21 Sept 2004): When the cast is done, you should check if there are unread characters left. For example, casting "1.2this_definitly_is_not_a_number" to double will not result in returning the error value, because conversion of "1.2" to 1.2d succeeds and the rest of the string is ignored. */ template <typename value_type> value_type fromString( const std::string & str, const value_type & errorVal ) throw() { std::istringstream is( str ); if ( !is ) return errorVal; value_type foo = value_type(); if ( is >> foo ) return foo; return errorVal; } /** Returns a string representation of the given object, which must be ostreamble. */ template <typename value_type> std::string toString( const value_type & obj ) throw() { std::ostringstream os; os << std::fixed; os << obj; return os.str(); } /** Convenience/efficiency overload. */ inline std::string fromString( const std::string & str, const std::string & errorVal ) throw() { return str; } /** Convenience/efficiency overload. */ inline std::string fromString( const char *str, const char *errorVal ) throw() { return str; } /** Convenience/efficiency overload. */ inline std::string toString( const char *obj ) throw() { return obj; } /** Convenience/efficiency overload. */ inline std::string toString( const std::string & obj ) throw() { return obj; } } // end Private namespace /** 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. 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...). Change history: 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. */ struct LexT { /** Constructs an empty object. Calling <code>castTo<T>()</code> on an un-populated LexT object will return T(). */ LexT() throw(){} ~LexT() throw() {} /** Lexically casts f to a string. */ template <typename FromT> LexT( const FromT & f ) throw() { this->m_data = Private::toString( f ); } /** Efficiency overload. */ LexT( const std::string & f ) throw() : m_data(f) { } /** See operator=(const char *) for a note about why this exists. */ LexT( const char * str ) throw() : m_data(str?str:"") { } /** Copies rhs's data to this object. */ LexT( const LexT & rhs ) throw() : m_data(rhs.m_data) { } /** Returns (this-<str() < rhs.str()). */ inline bool operator<( const LexT & rhs ) const { return this->str() < rhs.str(); } /** Returns (this-<str() > rhs.str()). */ inline bool operator>( const LexT & rhs ) const { return this->str() > rhs.str(); } /** Copies rhs's data and returns this object. */ inline LexT & operator=( const LexT & rhs ) throw() { if( &rhs != this ) this->m_data = rhs.m_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. */ inline LexT & operator=( const char * rhs ) throw() { this->m_data = rhs ? rhs : ""; return *this; } /** lexically casts str() to a ToType, returning dflt 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> ToType castTo( const ToType & dflt = ToType() ) const throw() { return Private::fromString( this->m_data, dflt ); } /** 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() { return this->template castTo<ToType>(); } /** Overload to avoid ambiguity in some cases. */ inline operator std::string () const throw() { return this->str(); } /** Returns the same as str(). */ operator std::string & () throw() { return this->m_data; } /** Returns the same as str(). */ operator const std::string & () const throw() { return this->m_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 this->str().c_str(); } /** Sets this object's value and returns this object. */ template <typename ToType> inline LexT & operator=( const ToType & f ) throw() { this->m_data = Private::toString( f ); return *this; } /** Returns a reference to this object's raw string data. */ inline std::string & str() throw() { return this->m_data; } /** Returns a const reference to this object's raw string data. */ inline const std::string & str() const throw() { return this->m_data; } /** Returns true if this object contains no data, else false. */ inline bool empty() const { return this->m_data.empty(); } private: std::string m_data; }; /** Copies a.str() to os. */ inline std::ostream & operator<<( std::ostream & os, const LexT & a ) { return os << a.str(); } /** 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 */ inline std::istream & operator>>( std::istream & is, LexT & a ) { while( std::getline( is, a.str() ).good() ); return is; } /** Casts lhs to a T object and returns true only if that object compares as equal to rhs. */ template <typename T> inline bool operator==( const LexT & lhs, const T & rhs ) { return lhs.template castTo<T>() == rhs; } /** Returns lhs.str() == rhs.str(). */ inline bool operator==( const LexT & lhs, const LexT & rhs ) { return lhs.str() == rhs.str(); } /** Avoid an ambiguity... If rhs == 0 then this function returns true if lhs.empty(). */ inline bool operator==( const LexT & lhs, const char * rhs ) { if( ! rhs ) return lhs.empty(); return lhs.str() == std::string(rhs); } /** Avoid an ambiguity... */ inline bool operator==( const LexT & lhs, const std::string & rhs ) { return lhs.str() == rhs; } } } // namespace P::Util #endif // p_UTIL_LEXT_HPP_INCLUDED //////////////////////////////////////////////////////////////////////// #ifdef s11n_S11N_INCLUDED // <--- that macro was added in s11n 0.9.7 # ifndef p_UTIL_LEXT_REGISTERED_WITH_S11N # define p_UTIL_LEXT_REGISTERED_WITH_S11N 1 // We have s11n! Let's use it! // Plug in a proxy for LexT, to make it work like a Streamable... # include <s11n.net/s11n/data_node_functor.hpp> // <-- s11n::streamable_type_serialization_proxy class # include <s11n.net/s11n/pods_streamable.hpp> // <-- required for s11n 0.9.14+ # define S11N_TYPE lext::LexT // <-- type of object to be treated as a Serializable # define S11N_TYPE_NAME "LexT" // <-- class name (for the classloader) # define S11N_SERIALIZE_FUNCTOR ::s11n::streamable_type_serialization_proxy // ^^^^ our de/s11n implementation (proxy functor) # include <s11n.net/s11n/reg_serializable_traits.hpp> // <-- register the above data with s11n # endif // p_UTIL_LEXT_REGISTERED_WITH_S11N #endif // s11n_S11N_INCLUDED //////////////////////////////////////////////////////////////////////// |