diff --git a/include/cereal/archives/xml.hpp b/include/cereal/archives/xml.hpp index 30aa0b9b..10393059 100644 --- a/include/cereal/archives/xml.hpp +++ b/include/cereal/archives/xml.hpp @@ -30,13 +30,35 @@ #include #include #include +#include #include #include #include +#include + namespace cereal { + namespace detail + { + struct XMLHelper + { + const char * popName() + { + if( names.empty() ) + return nullptr; + else + { + auto name = names.top(); + names.pop(); + return name; + } + } + + std::stack names; + }; + } // ###################################################################### //! An output archive designed to save data to XML class XMLOutputArchive : public OutputArchive @@ -44,11 +66,11 @@ namespace cereal public: //! Construct, outputting to the provided stream /*! @param stream The stream to output to. Can be a stringstream, a file stream, or - even cout! */ - XMLOutputArchive(std::ostream & stream) : + even cout! + @param precision The precision for floating point output */ + XMLOutputArchive(std::ostream & stream, size_t precision = 10 ) : OutputArchive(this), - itsStream(stream), - itsUnnamedCounter( 0 ) + itsStream(stream) { // rapidxml will delete all allocations when xml_document is cleared auto node = itsXML.allocate_node( rapidxml::node_declaration ); @@ -58,7 +80,12 @@ namespace cereal auto root = itsXML.allocate_node( rapidxml::node_element, "cereal" ); itsXML.append_node( root ); - itsNodes.push( root ); + itsNodes.emplace( root ); + + // testing + itsStream << std::boolalpha; + itsStream.precision( precision ); + //itsStream.setf( std::ios::floatfield, std::ios::fixed ); } //! Destructor, flushes the XML @@ -68,15 +95,6 @@ namespace cereal itsXML.clear(); } - //! Writes size bytes of data to the output stream - void saveBinary( const void * data, size_t size ) - { - size_t const writtenSize = itsStream.rdbuf()->sputn( reinterpret_cast( data ), size ); - - if(writtenSize != size) - throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize)); - } - void createNode( std::string const & name ) { createNode( name.c_str() ); @@ -85,16 +103,104 @@ namespace cereal void createNode( char const * name ) { auto node = itsXML.allocate_node( rapidxml::node_element, name ); - itsNodes.top()->append_node( node ); - itsNodes.push( node ); - ++itsUnnamedCounter; + itsNodes.top().node->append_node( node ); + itsNodes.emplace( node ); + + node->append_node( itsXML.allocate_node( rapidxml::node_data, nullptr, "5432" ) ); } + //! Saves some data, encoded as a string + /*! The data will be be named with the most recent name if one exists, + otherwise it will be given some default delimited value that depends upon + the parent node */ + template inline + void saveValue( T const & value ) + { + os.clear(); os.seekp(0); + os << value << std::ends; + //std::cout << ".... " << os.str().c_str() << std::endl; + insertValueNode( os.str().c_str() ); + itsStream << value << std::endl; + } + + //! Appends a named node to the current top node with some data + /*! The name is generated by calling the top node's getValueName(), + which will generate an incremental value name if one has not been set + prior to calling this */ + void insertValueNode( const char * data ) + { + //const auto nameString = itsNodes.top().getValueName(); + + // allocate strings for all of the data in the XML object + //auto namePtr = itsXML.allocate_string( nameString.data(), nameString.size() ); + auto dataPtr = itsXML.allocate_string( data ); + + // insert into the XML + itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data, nullptr, dataPtr ) ); + //itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_element, namePtr, dataPtr, nameString.size() ) ); + } + + void startNode() + { + const auto nameString = itsNodes.top().getValueName(); + + // allocate strings for all of the data in the XML object + auto namePtr = itsXML.allocate_string( nameString.data(), nameString.size() ); + + // insert into the XML + auto node = itsXML.allocate_node( rapidxml::node_element, namePtr, nullptr, nameString.size() ); + itsNodes.top().node->append_node( node ); + itsNodes.emplace( node ); + } + + void finishNode() + { + itsNodes.pop(); + } + + //! Saves some binary data, encoded as a base64 string + void saveBinaryValue( const void * data, size_t size ) + { + auto base64string = base64::encode( reinterpret_cast( data ), size ); + saveValue( base64string ); + //auto decoded = base64::decode(base64string); + //int const * zz = (int const*)decoded.data(); + //std::cout << zz[0] << " " << zz[1] << " " << zz[2] << std::endl; + }; + + struct NodeInfo + { + NodeInfo( rapidxml::xml_node<> * n = nullptr, + const char * nm = nullptr ) : + node( n ), + counter( 0 ), + name( nm ) + { } + + rapidxml::xml_node<> * node; + size_t counter; + const char * name; + + std::string getValueName() + { + if( name ) + { + auto n = name; + name = nullptr; + return {n}; + } + else + { + return "value" + std::to_string( counter++ ) + "\0"; + } + } + }; + private: std::ostream & itsStream; rapidxml::xml_document<> itsXML; //!< The XML document - std::stack*> itsNodes; //!< Stack of nodes, bottom one will always be the root - size_t itsUnnamedCounter; //!< Used to give names to un-named values + std::stack itsNodes; + std::ostringstream os; }; struct XMLInputArchive; @@ -108,10 +214,31 @@ namespace cereal //ar( t.value ); } + //! Saving for POD types to xml + template inline + typename std::enable_if::value, void>::type + save(XMLOutputArchive & ar, T const & t) + { + ar.saveValue( t ); + } + + //! saving string to xml template inline void save(XMLOutputArchive & ar, std::basic_string const & str) { - ar.createNode( str ); + ar.saveValue( str ); + } + + template + void prologue( XMLOutputArchive & ar, T const & data ) + { + ar.startNode(); + } + + template + void epilogue( XMLOutputArchive & ar, T const & data ) + { + ar.finishNode(); } // ###################################################################### diff --git a/sandbox.cpp b/sandbox.cpp index b0e0bd41..4949646d 100644 --- a/sandbox.cpp +++ b/sandbox.cpp @@ -295,14 +295,18 @@ int main() assert(e_in == e_out); - auto x = cereal::make_nvp( "a", cereal::make_nvp("b", 5) ); - { //std::stringstream os; cereal::XMLOutputArchive oar( std::cout ); oar( cereal::make_nvp("hello", 5 ) ); std::string bla("bla"); oar( bla ); + oar( 5 ); + oar( 3.3 ); + oar( 3.2f ); + oar( true ); + //int xxx[] = {-1, 95, 3}; + //oar.saveBinaryValue( xxx, sizeof(int)*3); }