Pomyślałem nad tym trochę, poprzyglądałem się problemowi i postanowiłem coś spłodzić. Oto efekt:
http://melpon.org/wandbox/permlink/fK4YU1mc4tw7CSNZ
binary_write.hpp
#pragma once
#include <type_traits>
#include <boost/fusion/tuple.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <sstream>
#include <cstring>
namespace detail{
template<typename OutStream, typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type write(OutStream &stream, const T &value){
stream.write(reinterpret_cast<const char *>(&value), sizeof(value));
}
template<typename OutStream, typename T>
typename std::enable_if<std::is_same<T, const char *>::value>::type
write(OutStream &stream, const T &str){
std::size_t len = std::strlen(str)+1;
write(stream, len);
stream.write(reinterpret_cast<const char *>(str), len);
}
template<typename OutStream, typename T>
typename std::enable_if<std::is_class<T>::value>::type
write(OutStream &stream, const T &obj){
obj.write_to(stream);
}
template<class... Types>
struct write_proxy{
write_proxy(Types... args): args(args...){}
template<typename OutStream>
void to(OutStream &stream){
for_each(args, [&](auto value){
write(stream, value);
});
}
boost::fusion::tuple<Types...> args;
};
}
template<class... Types>
auto write(Types... args){
return detail::write_proxy<Types...>(args...);
}
binary_read.hpp
#pragma once
#include <type_traits>
#include <boost/fusion/tuple.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <sstream>
#include <cstring>
#include <string>
#include <memory>
namespace detail{
template<typename OutStream, typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type read(OutStream &stream, T &value){
stream.read(reinterpret_cast<char *>(&value), sizeof(value));
}
template<typename OutStream, typename T>
typename std::enable_if<std::is_same<T, std::string>::value>::type
read(OutStream &stream, T &str){
std::size_t len;
read(stream, len);
str.reserve(len);
auto buff = std::unique_ptr<char[]>(new char[len]);
stream.read(buff.get(), len);
str = std::string(buff.get())+"\0";
}
template<typename OutStream, typename T>
typename std::enable_if<(std::is_class<T>::value && !std::is_same<T, std::string>::value)>::type
read(OutStream &stream, T &obj){
obj.read_from(stream);
}
template<class... Types>
struct read_proxy{
read_proxy(Types &... args): args(args...){}
template<typename OutStream>
void from(OutStream &stream){
for_each(args, [&](auto &value){
read(stream, value);
});
}
boost::fusion::tuple<Types&...> args;
};
}
template<class... Types>
auto read(Types&... args){
return detail::read_proxy<Types...>(args...);
}
Przykład użycia:
#include <iostream>
#include <string>
#include "binary_write.hpp"
#include "binary_read.hpp"
using namespace std;
/* spanish inquisition */
using binarystream = stringstream;
struct Foo{
int a, b;
string str;
void write_to(binarystream &ostream) const{
write(a, b, str.data()).to(ostream);
}
void read_from(binarystream &istream){
read(a, b, str).from(istream);
}
};
int main(){
binarystream stream;
write(1, Foo{2, 3, "Hello World!"}, "Nice to meet you!").to(stream);
int a = 0;
Foo foo;
string text;
read(a, foo, text).from(stream);
cout << a << endl
<< "Foo{a="<<foo.a << ", b="<<foo.b << ", str=\""<<foo.str << "\"}" << endl
<< text << endl;
}
1
Foo{a=2, b=3, str="Hello World!"}
Nice to meet you!