Line data Source code
1 : /* Copyright (C) 2020 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #ifndef INCLUDED_SHAREABLE
19 : #define INCLUDED_SHAREABLE
20 :
21 : /*
22 :
23 : The Atlas UI DLL needs to share information with the game EXE. It's most
24 : convenient if they can pass STL objects, like std::wstring and std::vector;
25 : but that causes problems if the DLL and EXE were not compiled in exactly
26 : the same way.
27 :
28 : So, the Shareable<T> class is used to make things a bit safer:
29 : Simple types (primitives, basic structs, etc) are passed as normal.
30 : std::string is converted to an array, using a shared (and thread-safe) memory
31 : allocation function so that it works when the DLL and EXE use different heaps.
32 : std::vector is done the same, though its element type must be Shareable too.
33 :
34 : This ought to protect against:
35 : * Different heaps (msvcr71 vs msvcr80, debug vs release, etc).
36 : * Different STL class layout.
37 : It doesn't protect against:
38 : * Different data type sizes/ranges.
39 : * Different packing in our structs. (But they're very simple structs,
40 : only storing size_t and pointer values.)
41 : * Vtable layout - this code doesn't actually care, but the rest of Atlas does.
42 :
43 : Usage should be fairly transparent - conversions from T to Shareable<T> are
44 : automatic, and the opposite is automatic for primitive types.
45 : For basic structs, use operator-> to access members (e.g. "msg->sharedstruct->value").
46 : For more complex things (strings, vectors), use the unary operator* to get back
47 : an STL object (e.g. "std::string s = *msg->sharedstring").
48 : (That conversion to an STL object is potentially expensive, so
49 : Shareable<string>.c_str() and Shareable<vector>.GetBuffer/GetSize() can be used
50 : if that's all you need.)
51 :
52 : The supported list of primitive types is below (SHAREABLE_PRIMITIVE).
53 : Structs are made shareable by manually ensuring that all their members are
54 : shareable (i.e. primitives and Shareable<T>s) and writing
55 : SHAREABLE_STRUCTS(StructName);
56 : after their definition.
57 :
58 : */
59 :
60 : #include "SharedMemory.h"
61 :
62 : #include <cstring>
63 : #include <vector>
64 : #include <string>
65 :
66 : // we want to use placement new without grief
67 : // (Duplicated in SharedMemory.h)
68 : #undef new
69 :
70 : namespace AtlasMessage
71 : {
72 :
73 : // By default, things are not shareable
74 : template <typename T> class Shareable
75 : {
76 : public:
77 : Shareable();
78 : enum { TypeIsShareable = 0 };
79 : };
80 :
81 : // Primitive types just need a very simple wrapper
82 : #define SHAREABLE_PRIMITIVE(T) \
83 : template<> class Shareable<T> \
84 : { \
85 : T m; \
86 : public: \
87 : enum { TypeIsShareable = 1 }; \
88 : Shareable() {} \
89 : Shareable(T const& rhs) : m(rhs) {} \
90 : operator T() const { return m; } \
91 : T _Unwrap() const { return m; } \
92 : }
93 :
94 0 : SHAREABLE_PRIMITIVE(unsigned char);
95 0 : SHAREABLE_PRIMITIVE(int);
96 0 : SHAREABLE_PRIMITIVE(unsigned int);
97 : SHAREABLE_PRIMITIVE(long);
98 0 : SHAREABLE_PRIMITIVE(bool);
99 0 : SHAREABLE_PRIMITIVE(float);
100 0 : SHAREABLE_PRIMITIVE(double);
101 0 : SHAREABLE_PRIMITIVE(void*);
102 :
103 : #undef SHAREABLE_PRIMITIVE
104 :
105 : // Structs are similar to primitives, but with operator->
106 : #define SHAREABLE_STRUCT(T) \
107 : template<> class Shareable<T> \
108 : { \
109 : T m; \
110 : public: \
111 : enum { TypeIsShareable = 1 }; \
112 : Shareable() {} \
113 : Shareable(T const& rhs) { m = rhs; } \
114 : const T* operator->() const { return &m; } \
115 : operator T() const { return m; } \
116 : const T _Unwrap() const { return m; } \
117 : }
118 :
119 :
120 : // Shareable containers must have shareable contents - but it's easy to forget
121 : // to declare them, so make sure the errors are almost readable, like:
122 : // "use of undefined type 'REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE<T,__formal>
123 : // with [ T=AtlasMessage::sTerrainTexturePreview, __formal=false ]"
124 : //
125 : // (Implementation based on boost/static_assert)
126 : template <typename T, bool> struct REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE;
127 : template <typename T> struct REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE<T, true>{};
128 : template<int x> struct static_assert_test{};
129 : #define ASSERT_TYPE_IS_SHAREABLE(T) typedef static_assert_test< \
130 : sizeof(REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE< T, (bool)(Shareable<T>::TypeIsShareable) >)> \
131 : static_assert_typedef_
132 :
133 : template <typename T> inline const T* empty_str();
134 : template <> inline const char* empty_str() { return ""; }
135 0 : template <> inline const wchar_t* empty_str() { return L""; }
136 :
137 : // Shareable strings:
138 : template<typename C> class Shareable< std::basic_string<C> >
139 : {
140 : typedef std::basic_string<C> wrapped_type;
141 :
142 : C* buf; // null-terminated string (perhaps with embedded nulls)
143 : size_t length; // size of buf (including null)
144 : public:
145 : enum { TypeIsShareable = 1 };
146 :
147 0 : Shareable() : buf(NULL), length(0) {}
148 :
149 0 : Shareable(const wrapped_type& rhs)
150 : {
151 0 : length = rhs.length()+1;
152 0 : buf = (C*)ShareableMallocFptr(sizeof(C)*length);
153 0 : memcpy(buf, rhs.c_str(), sizeof(C)*length);
154 0 : }
155 :
156 0 : ~Shareable()
157 : {
158 0 : ShareableFreeFptr(buf);
159 0 : }
160 :
161 0 : Shareable<wrapped_type>& operator=(const Shareable<wrapped_type>& rhs)
162 : {
163 0 : if (&rhs == this)
164 0 : return *this;
165 0 : ShareableFreeFptr(buf);
166 0 : length = rhs.length;
167 0 : buf = (C*)ShareableMallocFptr(sizeof(C)*length);
168 0 : memcpy(buf, rhs.buf, sizeof(C)*length);
169 0 : return *this;
170 : }
171 :
172 0 : Shareable(const Shareable<wrapped_type>& rhs)
173 0 : : buf(NULL), length(0)
174 : {
175 0 : *this = rhs;
176 0 : }
177 :
178 0 : const wrapped_type _Unwrap() const
179 : {
180 0 : return (buf && length) ? wrapped_type(buf, buf+length-1) : wrapped_type();
181 : }
182 :
183 0 : const wrapped_type operator*() const
184 : {
185 0 : return _Unwrap();
186 : }
187 :
188 : // Minor optimisation for code that just wants to access the characters,
189 : // without constructing a new std::basic_string then calling c_str on that
190 0 : const C* c_str() const
191 : {
192 0 : return (buf && length) ? buf : empty_str<C>();
193 : }
194 : };
195 :
196 : // Shareable vectors:
197 : template<typename E> class Shareable<std::vector<E> >
198 : {
199 : ASSERT_TYPE_IS_SHAREABLE(E);
200 : typedef std::vector<E> wrapped_type;
201 : typedef Shareable<E> element_type;
202 : element_type* array;
203 : size_t size;
204 :
205 : // Since we're allocating with malloc (roughly), but storing non-trivial
206 : // objects, we have to allocate with placement new and call destructors
207 : // manually. (At least the objects are usually just other Shareables, so it's
208 : // reasonably safe to assume there's no exceptions or other confusingness.)
209 0 : void Unalloc()
210 : {
211 0 : if (array == NULL)
212 0 : return;
213 :
214 0 : for (size_t i = 0; i < size; ++i)
215 0 : array[i].~element_type();
216 0 : ShareableFreeFptr(array);
217 :
218 0 : array = NULL;
219 0 : size = 0;
220 : }
221 :
222 : public:
223 : enum { TypeIsShareable = 1 };
224 :
225 0 : Shareable() : array(NULL), size(0) {}
226 :
227 0 : Shareable(const wrapped_type& rhs)
228 : {
229 0 : size = rhs.size();
230 0 : array = static_cast<element_type*> (ShareableMallocFptr( sizeof(element_type)*size ));
231 0 : for (size_t i = 0; i < size; ++i)
232 0 : new (&array[i]) element_type (rhs[i]);
233 0 : }
234 :
235 0 : ~Shareable()
236 : {
237 0 : Unalloc();
238 0 : }
239 :
240 0 : Shareable<wrapped_type>& operator=(const Shareable<wrapped_type>& rhs)
241 : {
242 0 : if (&rhs == this)
243 0 : return *this;
244 0 : Unalloc();
245 0 : size = rhs.size;
246 0 : array = static_cast<element_type*> (ShareableMallocFptr( sizeof(element_type)*size ));
247 0 : for (size_t i = 0; i < size; ++i)
248 0 : new (&array[i]) element_type (rhs.array[i]);
249 0 : return *this;
250 : }
251 :
252 0 : Shareable(const Shareable<wrapped_type>& rhs)
253 0 : : array(NULL), size(0)
254 : {
255 0 : *this = rhs;
256 0 : }
257 :
258 0 : const wrapped_type _Unwrap() const
259 : {
260 0 : wrapped_type r;
261 0 : r.reserve(size);
262 : // (/Wp64 causes a spurious warning here. see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=253172)
263 : #ifdef _MSC_VER // (can't use MSC_VERSION here since this file is included by Atlas too)
264 : #pragma warning(push)
265 : #pragma warning(disable:4267)
266 : #endif
267 0 : for (size_t i = 0; i < size; ++i)
268 0 : r.push_back(array[i]._Unwrap());
269 : #ifdef _MSC_VER
270 : #pragma warning(pop)
271 : #endif
272 0 : return r;
273 : }
274 :
275 0 : const wrapped_type operator*() const
276 : {
277 0 : return _Unwrap();
278 : }
279 :
280 : // Minor optimisation for code that just wants to access the buffer,
281 : // without constructing a new std::vector
282 : const element_type* GetBuffer() const
283 : {
284 : return array;
285 : }
286 : size_t GetSize() const
287 : {
288 : return size;
289 : }
290 : };
291 :
292 :
293 : // Shareable callbacks:
294 : // (TODO - this is probably not really safely shareable, due to unspecified calling conventions)
295 : template<typename T> struct Callback
296 : {
297 : Callback() : cb(NULL), cbdata(NULL) {}
298 : Callback(void (*cb) (const T*, void*), void* cbdata) : cb(cb), cbdata(cbdata) {}
299 : void (*cb) (const T*, void*);
300 : void* cbdata;
301 : };
302 :
303 : template<typename T> class Shareable<Callback<T> >
304 : {
305 : ASSERT_TYPE_IS_SHAREABLE(T);
306 : public:
307 : Shareable(Callback<T> cb) : cb(cb) {}
308 : Callback<T> cb;
309 : void Call(const T& data) const
310 : {
311 : cb.cb(&data, cb.cbdata);
312 : }
313 : };
314 :
315 :
316 : }
317 :
318 : #endif // INCLUDED_SHAREABLE
|