EDIT: one final thought is how would you handle read_binary(std::istream& s, char* value)? There is no sensible implementation, as a result it would break symmetry (consistency) of the read/write interface.
well, first off, if we're expected to read this data, we need to change the data we write in a few ways.
Firstly, strings need to be either null terminated (bad idea) or length prefixed (good idea). Secondly, no, you wouldn't return data in that manner. You would instead identify the interface you wish to provide (such as taking a std::vector<char>& and a length) and then implement that. Such as:
#include <iostream>
#include <string>
#include <type_traits>
#include <sstream>
#include <cstdlib>
#include <vector>
#include <iterator>
template<class T>
typename std::enable_if<std::is_fundamental<T>::value, std::ostream>::type&
write_binary(std::ostream& s, T const& value) {
s.write(reinterpret_cast<char const*>(&value), sizeof(T));
return s;
}
template<class T>
std::ostream& write_binary(std::ostream& s, T* ptr) {
static_assert(false, "Error: expected overload not found.");
}
template<class CharType, class CharTraits>
std::ostream& write_binary(std::ostream& s, std::basic_string<CharType, CharTraits> const& str) {
std::basic_string<CharType, CharTraits>::size_type size = str.length();
s.write(reinterpret_cast<char const*>(&size), sizeof(std::basic_string<CharType, CharTraits>::size_type));
s.write(reinterpret_cast<char const*>(&str.front()), str.length() * sizeof(CharType));
return s;
}
std::ostream& write_binary(std::ostream& s, std::string const& str) {
std::string::size_type size = str.length();
s.write(reinterpret_cast<char const*>(&size), sizeof(std::string::size_type));
s.write(reinterpret_cast<char const*>(&str.front()), str.length());
return s;
}
std::ostream& write_binary(std::ostream& s, std::wstring const& str) {
std::wstring::size_type size = str.length();
s.write(reinterpret_cast<char const*>(&size), sizeof(std::wstring::size_type));
s.write(reinterpret_cast<char const*>(&str.front()), str.length() * sizeof(std::wstring::value_type));
return s;
}
std::ostream& write_binary(std::ostream& s, char const* data, size_t length) {
s.write(data, length);
return s;
}
template<class Itor>
typename std::enable_if<
std::is_fundamental<typename std::iterator_traits<Itor>::value_type>::value
, std::ostream>::type&
write_binary(std::ostream& s, Itor begin, Itor end) {
if(begin == end)
return s;
if(std::is_same<typename std::iterator_traits<Itor>::iterator_category, std::random_access_iterator_tag>::value) {
s.write(reinterpret_cast<char const*>(&*begin), std::distance(begin, end) * sizeof(*begin));
} else {
for(; begin != end; ++begin) {
s.write(reinterpret_cast<char const*>(&*begin), sizeof(*begin));
}
}
return s;
}
template<class T>
typename std::enable_if<std::is_fundamental<T>::value, std::istream>::type&
read_binary(std::istream& s, T& outvalue) {
s.read(reinterpret_cast<char*>(&outvalue), sizeof(T));
return s;
}
template<class T>
std::istream& read_binary(std::istream& s, T* ptr) {
static_assert(false, "Error: expected overload not found.");
}
template<class CharType, class CharTraits>
std::istream& read_binary(std::istream& s, std::basic_string<CharType, CharTraits>& str) {
std::basic_string<CharType, CharTraits>::size_type size;
s.read(reinterpret_cast<char const*>(&size), sizeof(std::basic_string<CharType, CharTraits>::size_type));
str.resize(size);
s.read(reinterpret_cast<char const*>(&str.front()), str.length() * sizeof(CharType));
return s;
}
std::istream& read_binary(std::istream& s, std::string& str) {
std::string::size_type size;
s.read(reinterpret_cast<char*>(&size), sizeof(std::string::size_type));
str.resize(size);
s.read(reinterpret_cast<char*>(&str.front()), str.length());
return s;
}
std::istream& read_binary(std::istream& s, std::wstring& str) {
std::wstring::size_type size;
s.read(reinterpret_cast<char*>(&size), sizeof(std::wstring::size_type));
str.resize(size);
s.read(reinterpret_cast<char*>(&str.front()), str.length() * sizeof(std::wstring::value_type));
return s;
}
std::istream& read_binary(std::istream& s, std::vector<char>& data, size_t length) {
data.resize(length);
s.read(reinterpret_cast<char*>(&data.front()), data.size());
return s;
}
template<class Itor>
typename std::enable_if<std::is_fundamental<typename std::iterator_traits<Itor>::value_type>::value, std::istream>::type&
read_binary(std::istream& s, Itor begin, Itor end) {
if(begin == end)
return s;
if(std::is_same<typename std::iterator_traits<Itor>::iterator_category, std::random_access_iterator_tag>::value) {
s.read(reinterpret_cast<char*>(&*begin), std::distance(begin, end) * sizeof(*begin));
} else {
for(; begin != end; ++begin) {
s.read(reinterpret_cast<char*>(&*begin), sizeof(*begin));
}
}
return s;
}
int main() {
std::ostringstream oss;
write_binary(oss, 5);
int mynumber = rand();
write_binary(oss, mynumber);
write_binary(oss, "Hello world", 11);
write_binary(oss, std::string("Hello world"));
// write_binary(oss, "Hello world"); // error, no appropriate overload found.
#ifdef _MSC_VER
for each(auto val in oss.str()) {
std::cout<<std::hex<<static_cast<int>(val)<<" ";
}
#else
for(auto val : oss.str()) {
std::cout<<std::hex<<static_cast<int>(static_cast<unsigned char>(val))<<" ";
}
#endif
std::endl(std::cout);
std::istringstream iss(oss.str());
int value;
read_binary(iss, value);
std::cout<<"Should be 5: "<<value<<std::endl;
read_binary(iss, value);
std::cout<<"Should be random: "<<value<<std::endl;
std::vector<char> data(11);
read_binary(iss, data.begin(), data.end());
// Alternatively:
// std::vector<char> data;
// read_binary(iss, data, 11);
std::cout<<"Should be 'Hello world': "<<std::string(data.begin(), data.end())<<std::endl;
std::string str;
read_binary(iss, str);
std::cout<<"Should be 'Hello world': "<<str<<std::endl;
// char* ptr;
// read_binary(iss, ptr); // error, no appropriate overload found.
}