Michele Caini hace 7 años
padre
commit
ad9ec22fd1
Se han modificado 4 ficheros con 142 adiciones y 40 borrados
  1. 0 2
      TODO
  2. 1 1
      docs/CMakeLists.txt
  3. 141 0
      docs/lib.md
  4. 0 37
      docs/shared.md

+ 0 - 2
TODO

@@ -23,8 +23,6 @@
 * cleanup - see https://github.com/skypjack/entt/commit/ad5cedc08c83e8cbcc8aaeac9634d44624ffe35a#commitcomment-32380903
 
 ==> can we do more for shared libraries? who knows... see #144
-* get rid of get_pool_data (welcome structured bindings)
 * to be updated: dispatcher
-* to be updated: registry
 * to be updated: emitter
 * to be updated: doc

+ 1 - 1
docs/CMakeLists.txt

@@ -27,12 +27,12 @@ add_custom_target(
         CONTRIBUTING.md
         core.md
         entity.md
+        lib.md
         links.md
         locator.md
         meta.md
         process.md
         resource.md
-        shared.md
         signal.md
         extra.dox
 )

+ 141 - 0
docs/lib.md

@@ -0,0 +1,141 @@
+# Push EnTT across boundaries
+
+<!--
+@cond TURN_OFF_DOXYGEN
+-->
+# Table of Contents
+
+* [Introduction](#introduction)
+* [Shared types and traits class](#shared-types-and-traits-class)
+  * [Do not mix types](#do-not-mix-types)
+* [Macros, macros everywhere](#macros-macros-everywhere)
+  * [Conflicts](#conflict)
+* [Note](#note)
+<!--
+@endcond TURN_OFF_DOXYGEN
+-->
+
+# Introduction
+
+`EnTT` has historically had a limit when used across boundaries on Windows in
+general and on GNU/Linux when default visibility was set to _hidden_. The
+limitation is due mainly to a custom utility used to assign unique, sequential
+identifiers to different types. Unfortunately, this tool is used by several core
+classes (the `registry` among the others) that are thus almost unusable across
+boundaries.<br/>
+The reasons for that are beyond the purposes of this document. However, the good
+news is that `EnTT` also offers now a way to overcome this limit and to push
+things across boundaries without problems when needed.
+
+# Shared types and traits class
+
+To allow a type to work properly across boundaries when used by a class that
+requires to assign unique identifiers to types, users must specialize a class
+template to literally give a compile-time name to the type itself.<br/>
+The name of the class template is `shared_traits` and the specialization must be
+such that it exposes a static constexpr data member named `value` having type
+either `ENTT_ID_TYPE` or `entt::hashed_string::hash_type`. Its value is the user
+defined unique identifier assigned to the specific type.<br/>
+Identifiers are not to be sequentially generated in this case.
+
+As an example:
+
+```cpp
+struct my_type { /* ... */ };
+
+template<>
+struct entt::shared_traits<my_type> {
+    static constexpr auto value = "my_type"_hs;
+};
+```
+
+Because of the rules of the language, the specialization must reside in the
+global namespace or in the `entt` namespace. There is no way to change this rule
+unfortunately, because it doesn't depend on the library itself.
+
+The good aspect of this approach is that it's not intrusive at all. The other
+way around was in fact forcing users to inherit all their classes from a common
+base. Something to avoid, at least from my point of view.<br/>
+However, despite the fact that it's not intrusive, it would be great if it was
+also easier to use and a bit less error-prone. This is why a bunch of macros
+exist to ease defining shared types.
+
+## Do not mix types
+
+Someone might think that this trick is valid only for the types to push across
+boundaries. This isn't how things work. In fact, the problem is more complex
+than that.<br/>
+As a rule of thumb, users should never mix shared and non-shared types. Whenever
+a type is made shareable, all the types should be made shareable. As an example,
+consider the `registry` class template. In case it is pushed across boundaries,
+all the types of components should be shareable to avoid subtle bugs.
+
+Indeed, this constraint can be relaxed in many cases. However, it is difficult
+to define a general rule to follow that is not the most stringent, unless users
+know exactly what they are doing. Therefore, I won't elaborate on giving further
+details on the topic.
+
+# Macros, macros everywhere
+
+The library comes with a set of predefined macros to use to declare shared types
+or export already existing ones. In particular:
+
+* `ENTT_SHARED_TYPE` can be used to make shareable already existing types. This
+  macro must be used in the global namespace even when types to make shareable
+  are not.
+
+  ```cpp
+  ENTT_SHARED_TYPE(my_type)
+  ENTT_SHARED_TYPE(ns::another_type)
+  ```
+
+* `ENTT_SHARED_STRUCT` can be used to define and export a struct at the same
+  time. It accepts also an optional namespace in which to define the given type.
+  This macro must be used in the global namespace.
+
+  ```cpp
+  ENTT_SHARED_STRUCT(my_type, { /* struct definition */})
+  ENTT_SHARED_STRUCT(ns, another_type, { /* struct definition */})
+  ```
+
+* `ENTT_SHARED_CLASS` can be used to define and export a class at the same
+  time. It accepts also an optional namespace in which to define the given type.
+  This macro must be used in the global namespace.
+
+  ```cpp
+  ENTT_SHARED_CLASS(my_type, { /* class definition */})
+  ENTT_SHARED_CLASS(ns, another_type, { /* class definition */})
+  ```
+
+Nested namespaces are supported out of the box as well in all cases. As an
+example:
+
+```cpp
+ENTT_SHARED_STRUCT(nested::ns, my_type, { /* struct definition */})
+```
+
+These macros can be used to avoid specializing the `shared_traits` class
+template. In all cases, the name of the class is used also as a seed to generate
+the compile-time unique identifier.
+
+## Conflicts
+
+When using macros, unique identifiers are 32/64 bit integers generated by
+hashing strings during compilation. Therefore, conflicts are rare but still
+possible. In case of conflicts, everything simply will get broken at runtime and
+the strangest things will probably take place.<br/>
+Unfortunately, there is no safe way to prevent it. If this happens, it will be
+enough to give a different name to one of the conflicting types to solve the
+problem. To do this, users can either assign a different name to the class or
+define directly a specialization for the `shared_traits` class template.
+
+# Note
+
+I'm still working hard to make everything work across boundaries.<br/>
+The classes affected by the problem were `registry`, `dispatcher` and `emitter`.
+Currently, only the `registry` class fully support _shared types_. Using
+`dispatcher` and `emitter` across boundaries isn't allowed yet and can result in
+unexpected behavior on Windows in general and on GNU/Linux when default
+visibility is set to _hidden_.
+
+Stay tuned for future updates.

+ 0 - 37
docs/shared.md

@@ -1,37 +0,0 @@
-### EnTT and shared libraries
-
-To make sure that an application and a shared library that use both `EnTT` can
-interact correctly when symbols are hidden by default, there are some tricks to
-follow.<br/>
-In particular and in order to avoid undefined behaviors, all the instantiation
-of the `family` class template shall be made explicit along with the system-wide
-specifier to use to export them.
-
-At the time I'm writing this document, the classes that use internally the above
-mentioned class template are `dispatcher`, `emitter` and `registry`. Therefore
-and as an example, if you use the `registry` class template in your shared
-library and want to set symbols visibility to _hidden_ by default, the following
-lines are required to allow it to function properly with a client that also uses
-the `registry` somehow:
-
-* On GNU/Linux:
-
-  ```cpp
-  namespace entt {
-      template class __attribute__((visibility("default"))) family<struct internal_registry_component_family>;
-      template class __attribute__((visibility("default"))) family<struct internal_registry_handler_family>;
-  }
-  ```
-
-* On Windows:
-
-  ```cpp
-  namespace entt {
-      template class __declspec(dllexport) family<struct internal_registry_component_family>;
-      template class __declspec(dllexport) family<struct internal_registry_handler_family>;
-  }
-  ```
-
-Otherwise, the risk is that type identifiers are different between the shared
-library and the application and this will prevent the whole thing from
-functioning correctly for obvious reasons.