LCOV - code coverage report
Current view: top level - source/tools/atlas/GameInterface - Shareable.h (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 0 74 0.0 %
Date: 2023-01-19 00:18:29 Functions: 0 85 0.0 %

          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

Generated by: LCOV version 1.13