RInside Version 0.2.12
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
binarystream.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 Christian Authmann
3  */
4 
5 #pragma once
6 
7 #include "typeid.h"
8 
9 #include <unistd.h>
10 #include <string>
11 #include <vector>
12 #include <type_traits>
13 #include <utility>
14 #include <exception>
15 
16 /*
17  * This is a stream class for IPC, meant to allow serialization of objects.
18  *
19  * We could use the POSIX socket API directly, but we choose to use a simple
20  * wrapper for convenience and error handling via exceptions.
21  *
22  * We are not using std::iostream for several reasons.
23  * First, the default overloads are meant for human-readable display, not
24  * for efficient binary serialization.
25  * Second, they're not reversible:
26  * out << 2 << 7 << 42
27  * will result in a stream "2742", which cannot be correctly deserialized using
28  * in >> a >> b >> c
29  *
30  * Instead, we're opting for a very simple binary stream implementation
31  * providing nothing but read() and write() functions, including some
32  * overloads.
33  *
34  * - Primitive types are serialized as their binary representation.
35  * Do not attempt to communicate between machines of different word size
36  * or endianess!
37  * - some native types (std::string, ...) have their own serialization functions
38  * - other classes must implement serialize() and deserialize() methods
39  * (See foo.h for an example)
40  *
41  * Note that this is not meant as a lesson in good IPC or serialization design,
42  * it's just a simple helper class to keep the rest of the code more readable.
43  */
44 
45 class BinaryStream {
46  public:
47  BinaryStream(int read_fd, int write_fd);
48  ~BinaryStream();
49  void close();
50 
51  BinaryStream(const BinaryStream &) = delete;
52  BinaryStream &operator=(const BinaryStream &) = delete;
55 
56  static BinaryStream connectToUnixSocket(const char *);
57 
58  void write(const char *buffer, size_t len);
59  template<typename T> void write(const T& t);
60  template<typename T> void write(T& t);
61 
62  size_t read(char *buffer, size_t len);
63  template<typename T> typename std::enable_if< std::is_arithmetic<T>::value, size_t>::type
64  read(T *t) { return read((char *) t, sizeof(T)); }
65  template<typename T>
66  T read();
67 
68  class stream_exception : std::exception {
69  };
70 
71  private:
72  bool is_eof;
74 };
75 
76 /*
77  * Declare functions for serialization/deserialization of important native classes
78  */
79 namespace serialization {
80  template <typename T>
81  struct serializer { };
82 
83  template <>
84  struct serializer<std::string> {
85  static void serialize(BinaryStream &, const std::string &);
86  static std::string deserialize(BinaryStream &);
87  };
88 
89  template <typename T>
90  struct serializer<std::vector<T>> {
91  static void serialize(BinaryStream &, const std::vector<T> &);
92  static std::vector<T> deserialize(BinaryStream &);
93  };
94 }
95 
96 
97 /*
98  * Figure out if a class has serialize and deserialize methods
99  */
101  /*
102  * For void_t, see the CppCon2014 talk by Walter E. Brown: "Modern Template Metaprogramming: A Compendium", Part II
103  */
104  template<typename...>
105  struct void_t_struct { using type = void; };
106  template<typename... C>
107  using void_t = typename void_t_struct<C...>::type;
108 
109  /*
110  * Figuring out whether a class has serialize() and deserialize() members
111  */
112  template <typename T>
113  using serialize_member_t = decltype( std::declval<T&>().serialize( std::declval<BinaryStream&>() ) );
114 
115  template <typename T>
116  using deserialize_member_t = decltype( T::deserialize( std::declval<BinaryStream&>() ) );
117 
118  template<typename T, typename = void>
119  struct has_serialization_members_cv : std::false_type { };
120 
121  template<typename T>
123  : std::integral_constant<bool, std::is_same<serialize_member_t<T>,void>::value && std::is_same<deserialize_member_t<T>, T>::value > { };
124 
125  template<typename T>
126  struct has_serialization_members : has_serialization_members_cv< typename std::decay<T>::type > { };
127 
128  /*
129  * Templates for serialization
130  */
131  // Arithmetic types: serialize the binary representation
132  template <typename T>
133  typename std::enable_if< std::is_arithmetic<T>::value >::type stream_write(BinaryStream &stream, T& t) {
134  stream.write((const char *) &t, sizeof(T));
135  }
136 
137  // User-defined types: call .serialize()
138  template <typename T>
139  typename std::enable_if< has_typeid<T>::value && std::is_class<T>::value && has_serialization_members<T>::value >::type stream_write(BinaryStream &stream, T& t) {
140  t.serialize(stream);
141  }
142 
143  // Other classes: hopefully there's a function in the serialization namespace
144  template <typename T>
145  typename std::enable_if< has_typeid<T>::value && std::is_class<T>::value && !has_serialization_members<T>::value >::type stream_write(BinaryStream &stream, T &t) {
147  }
148 
149 
150  /*
151  * Typed template for deserialization
152  */
153  // Arithmetic types: deserialize the binary representation
154  template <typename T>
155  typename std::enable_if< std::is_arithmetic<T>::value, T >::type stream_read(BinaryStream &stream) {
156  T value;
157  stream.read(&value);
158  return value;
159  }
160 
161  // User-defined types: call ::deserialize()
162  template <typename T>
163  typename std::enable_if< has_typeid<T>::value && std::is_class<T>::value && has_serialization_members<T>::value, T >::type stream_read(BinaryStream &stream) {
164  return T::deserialize(stream);
165  }
166 
167  // Other classes: hopefully there's a function in the serialization namespace
168  template <typename T>
169  typename std::enable_if< has_typeid<T>::value && std::is_class<T>::value && !has_serialization_members<T>::value, T >::type stream_read(BinaryStream &stream) {
170  return serialization::serializer< typename std::decay<T>::type >::deserialize(stream);
171  }
172 }
173 
174 
175 template<typename T> void BinaryStream::write(const T& t) {
176  binary_stream_helpers::stream_write<const T>(*this, t);
177 }
178 template<typename T> void BinaryStream::write(T& t) {
179  binary_stream_helpers::stream_write<T>(*this, t);
180 }
181 
182 template<typename T> T BinaryStream::read() {
183  return binary_stream_helpers::stream_read<T>(*this);
184 }
typename void_t_struct< C...>::type void_t
Definition: binarystream.h:107
void write(const char *buffer, size_t len)
decltype(std::declval< T & >().serialize(std::declval< BinaryStream & >())) serialize_member_t
Definition: binarystream.h:113
BinaryStream(int read_fd, int write_fd)
std::enable_if< std::is_arithmetic< T >::value >::type stream_write(BinaryStream &stream, T &t)
Definition: binarystream.h:133
decltype(T::deserialize(std::declval< BinaryStream & >())) deserialize_member_t
Definition: binarystream.h:116
BinaryStream & operator=(const BinaryStream &)=delete
static BinaryStream connectToUnixSocket(const char *)
size_t read(char *buffer, size_t len)
std::enable_if< std::is_arithmetic< T >::value, T >::type stream_read(BinaryStream &stream)
Definition: binarystream.h:155
std::enable_if< std::is_arithmetic< T >::value, size_t >::type read(T *t)
Definition: binarystream.h:64