Update of /cvsroot/pclasses/pclasses2/src/s11n In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32352/src/s11n Added Files: abstract_creator.h algo.h classload.h class_name.h data_node_algo.h data_node.cpp data_node_functor.h data_node.h data_node_serialize.h functor.h list.h Makefile.toc map.h name_type.h node_traits_wrapper.h pods_streamable.h pointer_stripper.h reg_list_specializations.h reg_map_specializations.h reg_node_type.h reg_serializable_traits.h s11n.cpp s11n_debuggering_macros.h s11n.h s11n_node.cpp s11n_node.h sam_std_containers.h str.h string_util.cpp string_util.h traits.h valarray.h Log Message: egg --- NEW FILE: s11n_node.cpp --- #include <algorithm> // for_each #include <iterator> #include "functor.h" // object_deleter, child_pointer_deep_copier #include "s11n_node.h" namespace s11n { #define NODE_CLASS_NAME "s11n::s11n_node" // note that the default implclass is not useable for purposes of classloading // serializables, but: // a) the parsers need some text to read. // b) "raw" nodes are often useful for de/ser'ing maps, lists, etc. // c) Serializables must set their implclass *anyway*, so they will // (or should) always overwrite the default class. s11n_node::s11n_node( const std::string & name ) : m_name(name),m_iname(NODE_CLASS_NAME) { } s11n_node::s11n_node( const std::string & name, const std::string implclass ) : m_name( name ), m_iname( implclass ) { } s11n_node::s11n_node() : m_name("s11n_node"), m_iname(NODE_CLASS_NAME) { } s11n_node & s11n_node::operator=( const s11n_node & rhs ) { if( &rhs == this ) return *this; this->copy( rhs ); return *this; } s11n_node::s11n_node( const s11n_node & rhs ) { if( &rhs == this ) return; this->copy( rhs ); } s11n_node::~s11n_node() { this->clear_children(); } s11n_node::map_type & s11n_node::properties() { return this->m_map; } const s11n_node::map_type & s11n_node::properties() const { return this->m_map; } void s11n_node::copy( const s11n_node & rhs ) { if ( &rhs == this ) return; this->clear(); this->name( rhs.name() ); this->class_name( rhs.class_name() ); std::copy( rhs.begin(), rhs.end(), std::insert_iterator<map_type>( this->m_map, this->m_map.begin() ) ); std::for_each( rhs.children().begin(), rhs.children().end(), s11n::child_pointer_deep_copier<child_list_type>( this->children() ) ); } s11n_node::child_list_type & s11n_node::children() { return this->m_children; } const s11n_node::child_list_type & s11n_node::children() const { return this->m_children; } void s11n_node::clear() { this->clear_children(); this->clear_properties(); } void s11n_node::class_name( const std::string & n ) { this->m_iname = n; } std::string s11n_node::class_name()const { return this->m_iname; } void s11n_node::name( const std::string & n ) { this->m_name = n; } std::string s11n_node::name() const { return this->m_name; } bool s11n_node::is_set( const std::string & key ) const { return this->m_map.end() != this->m_map.find( key ); } void s11n_node::unset( const std::string & key ) { s11n_node::map_type::iterator iter; iter = m_map.find( key ); if ( iter == m_map.end() ) return; m_map.erase( iter ); return; } void s11n_node::clear_properties() { if ( m_map.empty() ) return; m_map.erase( m_map.begin(), m_map.end() ); } void s11n_node::clear_children() { std::for_each( this->children().begin(), this->children().end(), s11n::object_deleter() ); this->children().clear(); } s11n_node::iterator s11n_node::begin() { return this->m_map.begin(); } s11n_node::const_iterator s11n_node::begin() const { return this->m_map.begin(); } s11n_node::iterator s11n_node::end() { return this->m_map.end(); } s11n_node::const_iterator s11n_node::end() const { return this->m_map.end(); } } // namespace s11n #undef NODE_CLASS_NAME --- NEW FILE: data_node_serialize.h --- #ifndef p_DATA_SERIALIZE_H_INCLUDED #define p_DATA_SERIALIZE_H_INCLUDED //////////////////////////////////////////////////////////////////////// // data_node_serialize.h: // // Defines the core de/serialize() functions (and close friends). // // License: Public Domain // Author: st...@s1... //////////////////////////////////////////////////////////////////////// #include <string> #include <list> #include <map> #include <deque> #include <cassert> #include <typeinfo> #include "s11n_debuggering_macros.h" // COUT/CERR #include "class_name.h" // ::classname<>() #include "classload.h" // classload() #include "traits.h" // s11n_traits //////////////////////////////////////////////////////////////////////////////// // NO DEPS ON data_node.h/s11n_node.h ALLOWED! //////////////////////////////////////////////////////////////////////////////// #define NODEERR if(0) CERR // ^^^ a debugging macro namespace { // anon namespace important for linking reasons, to avoid ODR violations. /*** s11n_api_marshaler is OBSOLETED (as of version 0.9.3) by s11n_traits. It is implemented in terms of s11n_traits for backwards compatibility, but it will be phased out at some point. For the default implementation s11n_traits<SerializableT> is used to marshal the de/serialize() calls. */ template <typename SerializableT> struct s11n_api_marshaler { /** Same as SerializableT. */ typedef SerializableT serializable_type; /** Returns s11n_traits<serializable_type>::serialize_functor()( dest, src ). It also sets dest's class_name() to ::classname<serializable_type>(), but this is only suitable for monomorphic serializable_types. */ template <typename NodeType> static bool serialize( NodeType &dest, const serializable_type & src ) { typedef s11n::node_traits<NodeType> NTR; typedef s11n::s11n_traits<serializable_type> STR; NTR::class_name( dest, ::classname<serializable_type>() ); // only good for monomorphs return STR::serialize_functor()( dest, src ); } /** Returns s11n_traits<SerializableT>::deserialize_functor()( src, dest ). */ template <typename NodeType> static bool deserialize( const NodeType & src, serializable_type & dest ) { typedef s11n::s11n_traits<serializable_type> STR; return STR::deserialize_functor()( src, dest ); } }; /** A specialization to handle pointer types the same as reference/value types. It simply translates the pointers into references. */ template <typename SerializableT> struct s11n_api_marshaler<SerializableT *> { /** The SerializableT templatized type, minus any pointer part. */ typedef SerializableT serializable_type; /** Convenience typedef: this class' parent type. */ typedef s11n_api_marshaler<serializable_type> parent_type; /** Returns parent_type::serialize( dest, *src ); src must be a valid pointer. */ template <typename NodeType> static bool serialize( NodeType &dest, const serializable_type * src ) { if( ! src ) return false; return parent_type::serialize( dest, *src ); } /** Returns parent_type::deserialize( src, *dest ); dest must be a valid pointer. */ template <typename NodeType> static bool deserialize( const NodeType & src, serializable_type * dest ) { if( ! dest ) return false; return parent_type::deserialize( src, *dest ); } }; } // anon namespace /********************************************************************** General Conventions: NodeType should conform to the conventions laid out by s11n::data_node. SerializableTypes/BaseTypes: - BaseT must have the following in it's interface: - bool SerializeFunction( NodeType & dest ) const; - bool DeserializeFunction( const NodeType & dest ); SerializeFunction/DeserializeFunction need not be virtual, though they may be. Proxy functors: Serialization functor must have: bool operator()( NodeType & dest, const BaseT & src ) const; Deserialization functor must have: bool operator()( const NodeType & src, BaseT & dest ) const; They may be the same functor type - const resolution will determine which s11n uses. Sometimes this causes ambiguity, and may require 2 functors. These signatures apply for all functors designed to work as de/serializers. **********************************************************************/ namespace s11n { /** Returns func( target, src ). Note that using this function bypasses internal API marshalling for src. This is, however, useful in some cases. Note that if src initiates recursive serialization on objects of it's own type the framework will revert to using it's API marshalling, bypassing SerializerFunctorT. A solution is sought for this problem, but none exists yet. */ template <typename DataNodeType,typename SerializableT, typename SerializerFunctorT> bool serialize( DataNodeType & target, const SerializableT & src, const SerializerFunctorT & func ) { try { return func( target, src ); } catch(...) { CERR << "Unknown exception during serialize<>(DataNode,SerializableT,SerializerFunctorT)!\n"; return false; } } /** Returns func( src, target ). Note that using this function bypasses internal API marshalling for the target. This is, however, useful in some cases. See the complementary form of serialize() for important information regarding recursive deserialization. */ template <typename DataNodeType, typename DeserializableT, typename DeserializerFunctorT> bool deserialize( const DataNodeType & src, DeserializableT & target, const DeserializerFunctorT & func ) { try { return func( src, target ); } catch(...) { CERR << "Unknown exception during deserialize<>(DataNode,DeserializableT,DeserializerFunctorT)!\n"; return false; } } /** Serializes src to target using the default API marshaling mechanism. */ template <typename DataNodeType, typename SerializableT> bool serialize( DataNodeType & target, const SerializableT & src ) { try { // typedef typename s11n_traits<SerializableT>::serialize_functor Proxy; // return serialize<DataNodeType,SerializableT,Proxy>( target, src, Proxy() ); // return Proxy()( target, src ); return s11n_api_marshaler<SerializableT>::serialize( target, src ); } catch(...) { CERR << "Unknown exception during serialize<>(DataNode,SerializableT)!\n"; return false; } } /** Deserializes target from src using the default API marshaling mechanism. */ template <typename DataNodeType, typename DeserializableT> bool deserialize( const DataNodeType & src, DeserializableT & target ) { try { // typedef typename s11n_traits<DeserializableT>::deserialize_functor Proxy; // return deserialize<DataNodeType,DeserializableT,Proxy>( src, target, Proxy() ); // return Proxy()( src, target ); return s11n_api_marshaler<DeserializableT>::deserialize( src, target ); } catch(...) { CERR << "Unknown exception during deserialize<>(DataNode,DeserializableT)!\n"; return false; } } /** Tries to deserialize a DeserializableT from src, using <code>s11n_traits<DeserializableT>::factory_type()(node_traits<DataNodeType>::class_name(src))</code> to create a new DeserializableT. Returns 0 on error, otherwise returns a pointer to a new object, which the caller takes ownership of. */ template <typename DataNodeType, typename DeserializableT> DeserializableT * deserialize( const DataNodeType & src ) { typedef s11n_traits<DeserializableT> STR; typedef node_traits<DataNodeType> NTR; DeserializableT * obj = STR::factory_type()( NTR::class_name( src ) ); if( ! obj ) { NODEERR << "deserialize<>(DataNode): impl class '" << NTR::class_name( src )<<"' classload failed.\n" << "It is probably not registered with it's base classloader.\n"; return 0; } if( ! deserialize<DataNodeType,DeserializableT>( src, *obj ) ) { NODEERR << "deserialize<>(DataNode): failed for unknown reason.\n"; delete( obj ); // must not throw! obj = 0; } return obj; } /** Serializes src to as a subnode of target, named nodename. Except for the addition of a subnode, it is identical to serialize( target, src ). This is a convenience function: not part of the s11n kernel. */ template <typename DataNodeType, typename SerializableT > bool serialize_subnode( DataNodeType & target, const std::string & nodename, const SerializableT & src ) { NODEERR << "serialize_subnode<>(DataNodeType, '"<<nodename<<"', SerializableT)\n"; DataNodeType * sub = new DataNodeType; typedef node_traits<DataNodeType> NT; NT::name( *sub, nodename ); if( ! serialize<DataNodeType,SerializableT>( *sub, src ) ) { delete( sub ); sub = 0; } else { target.children().push_back( sub ); } return sub != 0; } /** If a child named subnodename is found in src then this function returns deserialize( child, target ) and returns it's result, otherwise it returns 0. This is a convenience function: not part of the s11n kernel. */ template <typename DataNodeType, typename DeserializableT> bool deserialize_subnode( const DataNodeType & src, const std::string & subnodename, DeserializableT & target ) { const DataNodeType * ch = find_child_by_name( src, subnodename ); if( ! ch ) return false; return deserialize<DataNodeType,DeserializableT>( *ch, target ); } /** If a child named subnodename is found in src then this function returns the result of deserialize(child), otherwise it returns 0. This is a convenience function: not part of the s11n kernel. */ template <typename DataNodeType, typename DeserializableT> DeserializableT * deserialize_subnode( const DataNodeType & src, const std::string & subnodename ) { const DataNodeType * ch = find_child_by_name( src, subnodename ); if( ! ch ) return false; return deserialize<DataNodeType,DeserializableT>( *ch ); } /** Clones an arbitrary SerializableType using it's DataNodeType serialization implementation. Returns a clone of tocp, or returns 0 on error. The caller owns the returned pointer. This copy is polymorphism-safe as long as all participating Serializables (re)implement the appropriate de/serialize operations, similarly to as they would do for a copy ctor or classical Clone() member function. Tip: clone() is a convenient way to test new de/serialize functions, e.g., for new Serializables, because if it works then deserializing from streams/files will also work. This function takes SerializableType through the whole de/serialize process, including classloading. */ template <typename DataNodeType, typename SerializableType> SerializableType * clone( const SerializableType & tocp ) { DataNodeType node; if( ! serialize( node, tocp ) ) return 0; return deserialize<DataNodeType,SerializableType>( node ); } /** "Casts" t1 to t2 using serialization. This will work whenever t1 and t2 are "semantically compatible", whatever that really means. It can be used, e.g., to copy a list<int> to a vector<double>, provided both types have been proxied. Note that in the case of containers, the pointerness of the contained types is irrelevant: this works on both, thus a list<int> can be "cast" to a vector<double*>. As usual for a failed deserialization, if it returns false then t2 may be in an undefined state. There is no guaranty, however, that t2's deserialize operator will ever be called, as the serialization of t1 must first succeed for that to happen. */ template <typename NodeType, typename Type1, typename Type2> bool s11n_cast( const Type1 & t1, Type2 & t2 ) { NodeType n; return serialize<NodeType,Type1>( n, t1 ) && deserialize<NodeType,Type2>( n, t2 ); } } // namespace s11n #undef NODEERR #endif // s11n_DATA_SERIALIZE_H_INCLUDED --- NEW FILE: s11n_node.h --- #ifndef p_S11N_NODE_HPP_INCLUDED #define p_S11N_NODE_HPP_INCLUDED //////////////////////////////////////////////////////////////////////// // s11n_node.h // A reference implementation for s11n's Data Node concept. // License: Public Domain // Author: st...@s1... //////////////////////////////////////////////////////////////////////// #include <string> #include <map> #include <vector> #include "str.h" // to/from() string namespace s11n { /** EXPERIMENTAL: Not for use in client code! s11n_node is a reference Data Node implementation intended for use with s11n::node_traits. */ class s11n_node { public: /** The map type this object uses to store properties. */ typedef std::map < std::string, std::string > map_type; /** A pair type used to store key/value properties internally. */ typedef map_type::value_type value_type; /** The type used to store property keys. For compatibility with std::map. */ typedef map_type::key_type key_type; /** The type used to internally store property values. For compatibility with std::map. */ typedef map_type::mapped_type mapped_type; /** The container type used to store this object's children. It contains (s11n_node *). While the exact type is not guaranteed, it is guaranteed to obey the most-commonly-used std::list/vector conventions: push_back(), erase(), etc. */ typedef std::vector<s11n_node *> child_list_type; /** For iterating over properties using STL conventions. Dereferences to a value_type object. */ typedef map_type::iterator iterator; /** For iterating over properties using STL conventions. Dereferences to a value_type object. */ typedef map_type::const_iterator const_iterator; /** Creates a new node with an empty name() and an class_name() of "s11n::s11n_node". This node is functionally useless until it's name is set, as nodes with empty names are not supported by any current i/o parsers. */ s11n_node(); /** Creates a new node with the given name() and an class_name() of "s11n::s11n_node". */ explicit s11n_node( const std::string & name ); /** Creates a new node with the given name() and and class_name(). Does not throw. */ s11n_node( const std::string & name, const std::string implclass ); /** Destroys all child objects owned by this object, freeing up their resources. Does not throw. */ ~s11n_node(); /** Copies the properties, name, class name and children of rhs. If rhs is this object then this function does nothing. Does not throw. */ s11n_node & operator=( const s11n_node & rhs ); /** See copy(). Does not throw. */ s11n_node( const s11n_node & rhs ); /** Returns a list of the s11n_node children of this object. The caller should not delete any pointers from this list unless he also removes the pointers from the list, or else they will get double-deleted later. In practice it is (almost) never necessary for client code to manipulate this list directly. */ child_list_type & children(); /** The const form of children(). */ const child_list_type & children() const; /** Removes all properties and deletes all children from this object, freeing up their resources. Any pointers to children of this object become invalided by a call to this function (they get deleted). */ void clear(); /** Defines the class name which should be used as the implementation class when this node is deserialize()d. Client Serializable types should call this one time from their serialize() method, <em>after</em> calling the parent class' serialize() method (if indeed that is called at all), passing it the name of their class, <em>using the name expected by the classloader</em>. By convention the class name is the same as it's C++ name, thus Serializable class foo::FooBar should call: <pre> node.class_name( "foo::FooBar" ); </pre> from it's serialize() function. If classes to not set this then the serialized data will not have a proper implementation class name. That *will* break deserialization. */ void class_name( const std::string & n ); /** Returns the implementation class name set via class_name(). */ std::string class_name() const; /** The name which should be used as the key for storing the node. This is normally translated to something like an XML element name (e.g., <name>), and should not contain spaces or other characters which may not be usable as key names. To be safe, stick to alphanumeric and underscores, starting with a letter or underscore. (This class does no enforce any naming conventions, but your data file parsers very well may.) */ void name( const std::string & n ); /** Returns this node's name, as set via name(string). */ std::string name() const; /** Lexically casts val to a string and stores it as a property. If this type conversion is not possible it will fail at compile time. A value-conversion failure, on the other hand, is not caught at compile time. T must support complementary ostream<< and istream>> operators. */ template < typename T > void set( const std::string & key, const T & val ) { this->m_map[key] = str::to(val); } /** Tries to get a property named key and lexically cast it to type T. If this type conversion is not possible it will fail at compile time. A value-conversion failure, on the other hand, is not caught at compile time. If value conversion fails, or if the requested property is not set, then defaultval is returned. This can be interpretted as an error value if the client so chooses, and it is often helpful to pass a known-invalid value here for that purpose. */ template < typename T > T get( const std::string & key, const T & defaultval ) const { const_iterator cit = this->m_map.find( key ); return ( this->m_map.end() == cit ) ? defaultval : str::from( (*cit).second, defaultval); } /** Returns true if this object contains the given property, else false. */ bool is_set( const std::string & key ) const; /** Removes the given property from this object. */ void unset( const std::string & key ); /** Returns the first item in this object's property map. Note that the iterator represents a value_type (std::pair), so use (*it).first to get the key and (*it).second to get the value. */ iterator begin(); /** Returns a const_iterator pointing at this object's first property. */ const_iterator begin() const; /** * The after-the-end iterator for this object's properties. */ iterator end(); /** * The after-the-end iterator for this object's properties. */ const_iterator end() const; map_type & properties(); const map_type & properties() const; private: map_type m_map; // stores key/value properties. std::string m_name; // name of this node std::string m_iname; // class_name name of this node child_list_type m_children; // holds child pointers /** Copies all properties and child s11n_nodes from rhs into this object, as well as any other details which need to be copied. This can be a very expensive operation, and is rarely necessary. */ void copy( const s11n_node & rhs ); /** Removes all property entries from this object. */ void clear_properties(); /** Removes all children from this object, deleting all child pointers. */ void clear_children(); }; // class s11n_node } // namespace s11n #endif // s11n_S11N_NODE_HPP_INCLUDED --- NEW FILE: abstract_creator.h --- #ifndef p_ABSTRACT_CREATOR_H_INCLUDED #define p_ABSTRACT_CREATOR_H_INCLUDED 1 //////////////////////////////////////////////////////////////////////// // abstract_creatore.h: // Defines a difficult-to-describe class. See the docs. // // Author: stephan beal <st...@s1...> // License: Public Domain //////////////////////////////////////////////////////////////////////// #include <string> #include "classload.h" // classloader-related funcs #include "class_name.h" namespace s11n { /** abstract_creator is a helper to avoid some code having to know if a type is created on a stack or the heap. This makes some template code easier to write, as it avoids syntax errors when trying something like: <pre> if( object type is a pointer type ) delete(object); else { ... } </pre> This implementation creates items the stack via the default ctor. If instantiated with (T *) a pointer/heap-based specialization is activated instead. Designed for use with, e.g., ListType::value_type, which will may be, e.g., either T or (T *). These objects contain no state information. */ template <typename T> struct abstract_creator { /** Same as (T). */ typedef T value_type; /** Same as (T). */ typedef T base_value_type; /** This implementation assigns v to value_type() and returns true. The string argument is bogus for this implementation, and is used by the pointer specialization to implement polymorphic classloading of value_type. Historical note: before s11n release 0.9.17 this function incorrectly did nothing. While this works for many cases, it caused a subtle, hard-to-track bug in s11n::list::deserialize_list(), in which a deserialized list was re-used when the function was called recursively. Many thanks to Patrick Lin for reporting that problem. */ static bool create( value_type & v, const std::string & /* implclass */ = std::string() ) { // if( 0 ) CERR << "create(&) implclass="<<implclass<<": initializing @" <<std::hex<<&v <<"\n"; v = value_type(); return true; } /** Does nothing in this specialization. Specializations which allocate resources are expected to release them here. Note that release() exists because using a static boolean to flag a clean-up mode won't work: <pre> typedef double T; T val; if( val is a pointer type ) { delete( val ); } else ... </pre> That will only work when T is actually a pointer type. Thus the following workaround: <pre> typedef [some type] T; T val; typedef abstract_creator<T> AC; assert( AC::create(val) ); // fine for (T) or (T *) ... use val ... AC::release(val); // deletes pointers. No-op for value types </pre> That works with pointers or non-pointers, and simply does nothing for non-pointers. See object_reference_wrapper for a type which can wrap function calls to objects using the dot notation, regardless of their pointerness. */ static void release( value_type & ) { return; } }; // abstract_creator<T> /** A specialization of abstract_creator to create objects on the heap, using the s11n classloader. */ template <typename T> struct abstract_creator<T *> { /** Same as (T *). */ typedef T * value_type; /** Same as (T). */ typedef T base_value_type; /** Tries to create a value_type object, using classload<base_value_type>( key ) to create it. v is assigned to it's value, which may be 0. Returns true if an object is created, else false. The caller owns the returned object. Maintenance note: new classloader registrations may need to be installed as new types show up, especially for streamables/PODs (because those aren't normally registered as classes), or this function won't handle them. In s11n this registration is handled by most of the various proxy installation macros. */ static bool create( value_type & v, const std::string & implclass = std::string() /* guess! */ ) { const std::string key = (! implclass.empty()) ? implclass : ::classname<value_type>(); v = ::s11n::cl::classload<base_value_type>( key ); if( 0 ) CERR << "create(*) implclass=["<<key<<"] allocated @" <<std::hex<<&v <<"\n"; return 0 != &v; } /** Deletes v and assigns it to 0. */ static void release( value_type & v ) { //CERR << "release(" << std::hex<<v<<")\n"; delete( v ); v = 0; } }; // abstract_creator<T *> } // namespace #endif // s11n_ABSTRACT_CREATOR_H_INCLUDED --- NEW FILE: algo.h --- #ifndef p_ALGO_H_INCLUDED #define p_ALGO_H_INCLUDED 1 ///////////////////////////////////////////////////////////////////////// // algo.h: generic algorithms // Author: stephan beal <st...@s1...> // License: Public Domain ///////////////////////////////////////////////////////////////////////// #include <functional> // for_each() #include "functor.h" // object_deleter() namespace s11n { /** For each item in (begin,end] object_deleter()(*item) is called. After this call the container from which the iterators come still contains the items but they are invalid - deleted pointers. The client should call erase(begin,end) to delete the entries, or use convenience function free_list_entries(), which accepts a list-style container. */ template <typename IterT> void delete_objects( IterT begin, IterT end ) { std::for_each( begin, end, object_deleter() ); } /** For each item in [first,last), copies the item to OutputIt if pred(*item) returns true. Copied from: http://www.bauklimatik-dresden.de/privat/nicolai/html/en/cpp.html */ template <typename InputIt, typename OutputIt, typename UnaryPredicate> OutputIt copy_if (InputIt first, InputIt last, OutputIt result, UnaryPredicate pred) { while (first != last) { if (pred(*first)) *result++ = *first; ++first; } return result; } /** Deallocates all objects in list. If the entries are pointers they are deleted, else this is a no-op. After this call, list will be empty. */ template <typename ListType> void free_list_entries( ListType & list ) { object_reference_wrapper<ListType> wr(list); // in case ListType is a pointer type if( ! wr.good() ) return; delete_objects( wr().begin(), wr().end() ); wr().clear(); } /** Deletes any pointers in map, and does nothing for reference types, then calls map.clear(). This is used to treat arbitrary maps containing pointers or value types identically, by applying the same set of deallocation rules for both cases, simplifying some template implementations. After calling this, map will be empty. */ template <typename MapType> void free_map_entries( MapType & map ) { object_reference_wrapper<MapType> wr(map); // in case MapType is a pointer type if( ! wr.good() ) return; std::for_each( wr().begin(), wr().end(), pair_entry_deallocator() ); wr().clear(); } } #endif // s11n_ALGO_H_INCLUDED --- NEW FILE: reg_map_specializations.h --- // a supermacro to generate some partial template specializations for map-type classes. #ifndef S11N_MAP_TYPE #error "You must define S11N_MAP_TYPE before including this file. e.g., to std::map or std::multimap." #endif #ifndef S11N_MAP_TYPE_NAME #error "You must define S11N_MAP_TYPE_NAME before including this file. e.g., to \"list\" or \"vector\"." #endif #ifndef S11N_MAP_TYPE_PROXY #define S11N_MAP_TYPE_PROXY ::s11n::map::map_serializable_proxy #endif namespace { // set up ::classname<>() //////////////////////////////////////////////////////////// // S11N_MAP_TYPE template <typename MapType, typename ValueType> struct class_name< S11N_MAP_TYPE<MapType, ValueType> > { typedef S11N_MAP_TYPE<MapType, ValueType> named_type; static const char * name() { static bool inited = false; if( (!inited) && (inited=true) ) { ::s11n::cl::classloader_register_base< named_type >( S11N_MAP_TYPE_NAME ); } return S11N_MAP_TYPE_NAME; } }; } // anon namespace namespace s11n { /** s11n_traits<> specialization for std::map types. */ template <typename KeyT, typename ValT> struct s11n_traits < S11N_MAP_TYPE<KeyT,ValT> > { typedef S11N_MAP_TYPE<KeyT,ValT> serializable_type; typedef S11N_MAP_TYPE_PROXY serialize_functor; typedef serialize_functor deserialize_functor; typedef ::s11n::cl::object_factory<serializable_type> factory_type; static const bool cl_reg_placeholder = (::classname<serializable_type>(),true) ; }; } // namespace s11n #undef S11N_MAP_TYPE_PROXY #undef S11N_MAP_TYPE_NAME #undef S11N_MAP_TYPE --- NEW FILE: data_node.h --- #ifndef p_DATA_NODE_H_INCLUDED #define p_DATA_NODE_H_INCLUDED // data_node.h // License: Public Domain // Author: st...@s1... #include <string> #include <map> #include <vector> #include <cassert> #include <typeinfo> #include <pclasses/Util/LexT.h> #include "functor.h" // some helpful functors namespace s11n { /** data_node is the next generation of s11n::s11n_node. It is a pure container, without any de/serialization-related methods. It is non-polymorphic, in contrast to s11n_node. This type is a reference implementation for the functionality required of data nodes by the s11n core framework. Any type which conforms to this class' interface/conventions should be usable by s11n::serialize() and related functions. Common conventions for types "conventionally compatible" with data_node are listed below. - Should not throw exceptions, especially in the ctors/dtor. - Each node must have a name, accessible via name(), which "should" conform to common element naming conventions (alphanumeric/underscore). - Must be default constructable via new() and it must be safely deletable via delete(). - Must support deep copy operations, though specific optimizations (e.g. copy-on-write) are not excluded as long as their operation is transparent to client code. - Owns all of it's child pointers, or at least does not require client code to delete them. - Structured like a DOM element, with an arbitrary number of unique key/value pairs and an arbitrary number of child data_node objects (of the same base type as the parent node). - Property key and value types must be i/ostreamable, so as to inherently support easy conversion between strings and arbitrary types such as PODs and std::string, as well as user-defined streamable types. - Each node must have a proper impl_class() (documented at length elsewhere). - Should follow common std::map-style conventions for getting/setting properties, in addition to any other (convenience) interface. The type should provide begin()/end() iterator accessors for the properties. - Should support common std::list-style conventions for traversing child nodes. The conventional function for accessing the children list is named children(). - Need not be polymorphic, but it may be so. - Data nodes are not, by their nature, also Serializable (as such). Indeed, their whole purpose is store data for Serializable types. They ARE "serializable" in the sense that their key/value sets must be eventually convertable to some "external representation" (i.e., strings). - Should not be implemented as template types, for usability and maintenance reasons. One exception might be to go STL-style, and define it as a class template, but provide a typedef or subclass/specialization for the "standard" variant (as is the case for std::string). The first prototype of this library thought it would be clever to use s11n_node<SerializableType>, but i can promise you that causes Coding Hell down the road in client code if you ever try to mix different SerializableTypes in the same code.) More specific conventions will be detailed later in the library manual. */ class data_node { public: /** The map type this object uses to store items internally. */ typedef std::map < std::string, std::string > map_type; /** A pair type used to store key/value properties internally. */ typedef map_type::value_type value_type; /** For compatibility with std::map */ typedef map_type::key_type key_type; /** For compatibility with std::map */ typedef map_type::mapped_type mapped_type; /** The container type used to store this object's children. It contains (data_node *). While the exact type is not guaranteed, it is guaranteed to obey the most-commonly-used std::list/vector conventions: push_back(), erase(), etc. */ typedef std::vector<data_node *> child_list_type; /** For iterating over properties using STL conventions. Dereferences to a value_type object. */ typedef map_type::iterator iterator; /** For iterating over properties using STL conventions. Dereferences to a value_type object. */ typedef map_type::const_iterator const_iterator; /** Creates a new node with an empty name() and an impl_class() of "s11n::data_node". This node is functionally useless until it's name is set, as nodes with empty names are not supported by any current i/o parsers. */ data_node(); /** Creates a new node with the given name() and an impl_class() of "s11n::data_node". */ explicit data_node( const std::string & name ); /** Creates a new node with the given name() and and impl_class(). Does not throw. */ data_node( const std::string & name, const std::string implclass ); /** Destroys all child objects owned by this object, freeing up their resources. Does not throw. */ ~data_node(); /** See copy(). Does not throw. */ data_node & operator=( const data_node & rhs ); /** See copy(). Does not throw. */ data_node( const data_node & rhs ); /** Returns a list of the data_node children of this object. The caller should not delete any pointers from this list unless he also removes the pointers from the list, or else they will get double-deleted later. In practice it is (almost) never necessary for client code to manipulate this list directly. See node_child_simple_formatter<> for a funcfor designed to quickly serialize lists such as this one. */ child_list_type & children(); /** The const form of children(). */ const child_list_type & children() const; /** Removes all properties and deletes all children from this object, freeing up their resources. Any pointers to children of this object become invalided by a call to this function (they get deleted). Since client code doesn't normally hold pointers to data_node children this should not be a practical problem. This is normally only used in test code, not client code. */ void clear(); /** older name for clear(). Deprecated. */ void reset() { this->clear(); } /** Defines the class name which should be used as the implementation for the node when it is deserialize()d. Client Serializable types should call this one time from their serialize() method, <em>after</em> calling the parent class' serialize() method (if indeed that is called at all), passing it the name of their class, <em>as it will be used by the classloader</em>. By convention the class name is the same as it's C++ name, thus Serializable class foo::FooBar should call: <pre> node.impl_class( "foo::FooBar" ); </pre> from it's serialize() function. Historical note: the above call <em>should</em> be handled 100% transparently by this library. There are, however, legitimate use-cases which the template/macro-based solution cannot catch, so users are encouraged to set it manually, as described above. If you don't then you your serialized data will all have the same impl_class: probably that of the base-most Serializable class. That *will* break deserialization. */ void impl_class( const std::string & n ); /** Returns the implementation class name set via impl_class(). */ std::string impl_class() const; /** The name which should be used as the key for storing the node. This is normally translated to something like an XML element name (e.g., <name>), and should not contain spaces or other characters which may not be usable as key names. To be safe, stick to alphanumeric and underscores, starting with a letter or underscore. (This class does no enforce any naming conventions, but your data file parsers certainly will.) */ void name( const std::string & n ); /** Returns this node's name, as set via name(string). */ std::string name() const; /** std::string propval = node["bar"] is functionally identical to node.get_string("bar"). Unlike std::map and the like, calling this operator with a key which is not in the object does not create a new entry - it simply returns an empty string in that case. This behaviour is not a specific of the interface for purposes of data_node conventions. */ const mapped_type operator[] ( const key_type & key ) const; /** Untested. Returns a non-const reference to a property with the given key. If key does not exist a new entry is created. */ mapped_type & operator[] ( const key_type & key ); /** For compatibility with std::map. Inserts a value_type (i.e., pair) into this object. */ void insert( const value_type & ); /** * Returns the value for key, or defaultVal if the key * is not set. * * Maintenance note: this is the "master" * getter. Almost all other getXXX() functions call * this one, so do not call them from inside this * function. * */ std::string get_string( const std::string & key, const std::string & defaultVal = std::string() )const; /** Sets the given key to the given value. See get_string(): same notes apply here. */ void set_string( const std::string & key, const std::string & val ); /** See set_string(). This function is identical except that it converts val to string before saving it. If this type conversion is not possible it will fail at compile time. A value-conversion failure, on the other hand, is not caught at compile time. */ template < typename T > void set( const std::string & key, const T & val ) { ::P::Util::LexT lex(val); this->set_string( key, val ); } /** The <const char *> variants of get() and set() are to help the developer avoid having to cast so much and to help out compilers which have mediocre template support. :/ */ void set( const char *key, const char *val ) { this->set_string( key, val ); } /** Overloaded for strings-via-streams reaons. */ void set( const std::string & key, const char *val ) { this->set_string( key, val ); } /** Overloaded for strings-via-streams reaons. */ void set( const char *key, const std::string & val ) { this->set_string( key, val ); } /** See get_string(). This function is identical except that the returned string is converted to type T. If this type is not possible it will fail at compile time. A value-conversion failure, on the other hand, is not caught at compile time. If value conversion fails then defaultval is returned. This can be interpretted as an error value if the client so chooses, and it is often helpful to pass a known-invalid value here for that purpose. */ template < typename T > T get( const std::string & key, const T & defaultval ) const { ::P::Util::LexT foo = this->get_string( key, ::P::Util::LexT(defaultval).str() ); return foo.template castTo<T>( defaultval ); } /** get(): see <code>set( const char *, const char * )</code>. Same notes apply. */ std::string get( const char *key, const char *val ) const { return this->get_string( key, val ); } /** Overloaded for strings-via-streams reasons. */ std::string get( const std::string & key, const char *val ) const { return this->get_string( key, val ); } /** Overloaded for strings-via-streams reasons. */ std::string get( const char *key, const std::string & val ) const { return this->get_string( key, val ); } /** Returns true if this object contains the given property, else false. */ bool is_set( const std::string & key ) const; /** Removes the given property from this object. */ void unset( const std::string & key ); /** set_bool() is provided to work around a potential compiler warning: <pre> props.set( "foo", false ); // ambiguous: may be bool, const char *, char, or even int :/ </pre> */ void set_bool( const std::string & key, bool val ); /** get_bool(key) returns true if key's value is true, as evaluated by the static function bool_val(). */ bool get_bool( const std::string & key, bool defaultVal ) const; /** * Returns the bool value of the passed string. * The following string values are considered equal to true: * * true, TRUE, True * yes, YES, Yes, y, Y * 1 * * Anything else evaluates to false. * * Case IS significant! */ static bool bool_val( const std::string & key ); /** * Returns end() if the key is not in our map, otherise it returns * that iterator. Use the iterator's 'first' member to get the key * and 'second' to get at it's value. */ iterator find( const std::string & key ); /** * Returns the first item in the data map. * You can use this to iterate, STL-style: * <pre> * data_node::iterator it = node.begin(); * for( ; node.end() != it; ++it ) { ... } * </pre> * * Note that the iterator represents a * value_type (std::pair), so use (*it).first to get * the key and (*it).second to get the value. */ iterator begin(); /** Returns a const_iterator pointing at this object's first property. */ const_iterator begin() const; /** * The after-the-end iterator for the data map. */ iterator end(); /** * The after-the-end iterator for the data map. */ const_iterator end() const; /** * Returns the internally-used map_type (see the typedefs). * It is safe to modify this directly. */ map_type & map(); /** * Returns the internally-used map_type (see the typedefs). */ const map_type & map() const; /** Returns the number of properties in this object. */ size_t size() const; private: map_type m_map; // stores key/value properties. std::string m_name; // name of this node std::string m_iname; // impl_class name of this node child_list_type m_children; // holds child pointers /** Copies all properties and child data_nodes from rhs into this object, as well as any other details which need to be copied. This can be a very expensive operation, and is rarely necessary. */ void copy( const data_node & rhs ); ... [truncated message content] |