diff --git a/examples/1_stream_known.cpp b/examples/1_stream_known.cpp new file mode 100644 index 0000000000000000000000000000000000000000..67355d180ab8b340ad45a48584f1fd71e24e615f --- /dev/null +++ b/examples/1_stream_known.cpp @@ -0,0 +1,26 @@ + +#include <iostream> +#include <sstream> +#include <cassert> +#include <tscpp.h> +#include "types.h" + +using namespace std; +using namespace tscpp; + +int main() +{ + //Serialize to buffer + Point3d p1(1,2,3); + stringstream ss; + OutputArchive oa(ss); + oa<<p1; + + //Unserialize from buffer + Point3d p2; + InputArchive ia(ss); + ia>>p2; + assert(p1==p2); + + cout<<"Test passed"<<endl; +} diff --git a/examples/Makefile b/examples/Makefile index ca23e85127e5c2083d8c5a8ca58497579827254d..eb56345f9773054944548f2fbb8d3cdf4d66d02e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -3,10 +3,12 @@ CXX = g++ CXXFLAGS = -std=c++11 -O2 -Wall -I.. all: + $(CXX) $(CXXFLAGS) 1_stream_known.cpp ../tscpp.cpp -o 1_stream_known $(CXX) $(CXXFLAGS) 3_buffer_known.cpp ../tscpp.cpp -o 3_buffer_known $(CXX) $(CXXFLAGS) 4_buffer_unknown.cpp ../tscpp.cpp -o 4_buffer_unknown + ./1_stream_known ./3_buffer_known ./4_buffer_unknown clean: - rm -f 3_buffer_known 4_buffer_unknown + rm -f 1_stream_known 3_buffer_known 4_buffer_unknown diff --git a/tscpp.cpp b/tscpp.cpp index 29634ef57cf3b0544da3b1845d33703dca3d1805..ae0ca42ba41228635e932ffcac1f2893f4c2499b 100644 --- a/tscpp.cpp +++ b/tscpp.cpp @@ -26,6 +26,7 @@ ***************************************************************************/ #include "tscpp.h" +#include <memory> #if defined(__GNUC__) && !defined(_MIOSIX) #include <cxxabi.h> #endif @@ -107,4 +108,40 @@ string demangle(const string& name) #endif } +void OutputArchive::serializeImpl(const char *name, const void *data, int size) +{ + int nameSize=strlen(name); + os.write(name,nameSize+1); + os.write(reinterpret_cast<const char*>(data),size); +} + +void InputArchive::unserializeImpl(const char *name, void *data, int size) +{ + auto pos=is.tellg(); + int nameSize=strlen(name); + unique_ptr<char[]> unserializedName(new char[nameSize+1]); + is.read(unserializedName.get(),nameSize+1); + if(is.eof()) + { + is.seekg(pos); + throw 1; //FIXME + } + + if(memcmp(unserializedName.get(),name,nameSize+1)) + { + is.seekg(pos); + throw 2; //FIXME + } + + //NOTE: we are writing on top of a constructed type without calling its + //destructor. However, since it is trivially copyable, we at least aren't + //overwriting pointers to allocated memory. + is.read(reinterpret_cast<char*>(data),size); + if(is.eof()) + { + is.seekg(pos); + throw 3; //FIXME + } +} + } //namespace tscpp diff --git a/tscpp.h b/tscpp.h index 5e063225c53af1798eb179b487aa6706bb020607..d46b3489349e559b5434b1fce67e25989208f080 100644 --- a/tscpp.h +++ b/tscpp.h @@ -29,6 +29,8 @@ #include <type_traits> #include <functional> +#include <ostream> +#include <istream> #include <cstring> #include <string> #include <map> @@ -186,4 +188,88 @@ std::string peekTypeName(const void *buffer, int bufSize); */ std::string demangle(const std::string& name); +/** + * The output archive. + * This class allows to serialize objects to any ostream using the familiar + * << syntax + */ +class OutputArchive +{ +public: + /** + * Constructor + * \param os ostream where srialized types will be written + */ + OutputArchive(std::ostream& os) : os(os) {} + + /** + * \internal + */ + void serializeImpl(const char *name, const void *data, int size); + +private: + OutputArchive(const OutputArchive&)=delete; + OutputArchive& operator=(const OutputArchive&)=delete; + + std::ostream& os; +}; + +/** + * Serialize a type + * \param oa archive where the type will be serialized + * \param t type to serialize + */ +template<typename T> +OutputArchive& operator<<(OutputArchive& oa, const T& t) +{ + #ifndef _MIOSIX + static_assert(std::is_trivially_copyable<T>::value,"Type is not trivially copyable"); + #endif + oa.serializeImpl(typeid(t).name(),&t,sizeof(t)); + return oa; +} + +/** + * The input archive. + * This class allows to unserialize types from a stream, as long as you know + * what types have been serialized in which order. Otherwise have a look at + * UnknownInputArchive. + * To unserialize, use the familiar >> syntax. + */ +class InputArchive +{ +public: + /** + * Constructor + * \param os ostream where srialized types will be written + */ + InputArchive(std::istream& is) : is(is) {} + + /** + * \internal + */ + void unserializeImpl(const char *name, void *data, int size); + +private: + InputArchive(const InputArchive&)=delete; + InputArchive& operator=(const InputArchive&)=delete; + + std::istream& is; +}; + +/** + * Unserialize a type + * \param ia archive where the type has been serialized + * \param t type to unserialize + */ +template<typename T> +InputArchive& operator>>(InputArchive& ia, T& t) +{ + #ifndef _MIOSIX + static_assert(std::is_trivially_copyable<T>::value,"Type is not trivially copyable"); + #endif + ia.unserializeImpl(typeid(t).name(),&t,sizeof(t)); + return ia; +} + } // namespace tscpp