🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
5.2 KiB

  1. # Crash Course: core functionalities
  2. <!--
  3. @cond TURN_OFF_DOXYGEN
  4. -->
  5. # Table of Contents
  6. * [Introduction](#introduction)
  7. * [Compile-time identifiers](#compile-time-identifiers)
  8. * [Runtime identifiers](#runtime-identifiers)
  9. * [Hashed strings](#hashed-strings)
  10. * [Conflicts](#conflicts)
  11. * [Monostate](#monostate)
  12. <!--
  13. @endcond TURN_OFF_DOXYGEN
  14. -->
  15. # Introduction
  16. `EnTT` comes with a bunch of core functionalities mostly used by the other parts
  17. of the library itself.<br/>
  18. Hardly users will include these features in their code, but it's worth
  19. describing what `EnTT` offers so as not to reinvent the wheel in case of need.
  20. # Compile-time identifiers
  21. Sometimes it's useful to be able to give unique identifiers to types at
  22. compile-time.<br/>
  23. There are plenty of different solutions out there and I could have used one of
  24. them. However, I decided to spend my time to define a compact and versatile tool
  25. that fully embraces what the modern C++ has to offer.
  26. The _result of my efforts_ is the `identifier` class template:
  27. ```cpp
  28. #include <ident.hpp>
  29. // defines the identifiers for the given types
  30. using id = entt::identifier<a_type, another_type>;
  31. // ...
  32. switch(a_type_identifier) {
  33. case id::type<a_type>:
  34. // ...
  35. break;
  36. case id::type<another_type>:
  37. // ...
  38. break;
  39. default:
  40. // ...
  41. }
  42. ```
  43. This is all what the class template has to offer: a `type` inline variable that
  44. contains a numerical identifier for the given type. It can be used in any
  45. context where constant expressions are required.
  46. As long as the list remains unchanged, identifiers are also guaranteed to be the
  47. same for every run. In case they have been used in a production environment and
  48. a type has to be removed, one can just use a placeholder to left the other
  49. identifiers unchanged:
  50. ```cpp
  51. template<typename> struct ignore_type {};
  52. using id = entt::identifier<
  53. a_type_still_valid,
  54. ignore_type<a_type_no_longer_valid>,
  55. another_type_still_valid
  56. >;
  57. ```
  58. A bit ugly to see, but it works at least.
  59. # Runtime identifiers
  60. Sometimes it's useful to be able to give unique identifiers to types at
  61. runtime.<br/>
  62. There are plenty of different solutions out there and I could have used one of
  63. them. In fact, I adapted the most common one to my requirements and used it
  64. extensively within the entire library.
  65. It's the `family` class. Here is an example of use directly from the
  66. entity-component system:
  67. ```cpp
  68. using component_family = entt::family<struct internal_registry_component_family>;
  69. // ...
  70. template<typename Component>
  71. component_type component() const noexcept {
  72. return component_family::type<Component>;
  73. }
  74. ```
  75. This is all what a _family_ has to offer: a `type` inline variable that contains
  76. a numerical identifier for the given type.
  77. Please, note that identifiers aren't guaranteed to be the same for every run.
  78. Indeed it mostly depends on the flow of execution.
  79. # Hashed strings
  80. A hashed string is a zero overhead unique identifier. Users can use
  81. human-readable identifiers in the codebase while using their numeric
  82. counterparts at runtime, thus without affecting performance.<br/>
  83. The class has an implicit `constexpr` constructor that chews a bunch of
  84. characters. Once created, all what one can do with it is getting back the
  85. original string or converting it into a number.<br/>
  86. The good part is that a hashed string can be used wherever a constant expression
  87. is required and no _string-to-number_ conversion will take place at runtime if
  88. used carefully.
  89. Example of use:
  90. ```cpp
  91. auto load(entt::hashed_string::hash_type resource) {
  92. // uses the numeric representation of the resource to load and return it
  93. }
  94. auto resource = load(entt::hashed_string{"gui/background"});
  95. ```
  96. There is also a _user defined literal_ dedicated to hashed strings to make them
  97. more user-friendly:
  98. ```cpp
  99. constexpr auto str = "text"_hs;
  100. ```
  101. ## Conflicts
  102. The hashed string class uses internally FNV-1a to compute the numeric
  103. counterpart of a string. Because of the _pigeonhole principle_, conflicts are
  104. possible. This is a fact.<br/>
  105. There is no silver bullet to solve the problem of conflicts when dealing with
  106. hashing functions. In this case, the best solution seemed to be to give up.
  107. That's all.<br/>
  108. After all, human-readable unique identifiers aren't something strictly defined
  109. and over which users have not the control. Choosing a slightly different
  110. identifier is probably the best solution to make the conflict disappear in this
  111. case.
  112. # Monostate
  113. The monostate pattern is often presented as an alternative to a singleton based
  114. configuration system. This is exactly its purpose in `EnTT`. Moreover, this
  115. implementation is thread safe by design (hopefully).<br/>
  116. Keys are represented by hashed strings, values are basic types like `int`s or
  117. `bool`s. Values of different types can be associated to each key, even more than
  118. one at a time. Because of this, users must pay attention to use the same type
  119. both during an assignment and when they try to read back their data. Otherwise,
  120. they will probably incur in unexpected results.
  121. Example of use:
  122. ```cpp
  123. entt::monostate<entt::hashed_string{"mykey"}>{} = true;
  124. entt::monostate<"mykey"_hs>{} = 42;
  125. // ...
  126. const bool b = entt::monostate<"mykey"_hs>{};
  127. const int i = entt::monostate<entt::hashed_string{"mykey"}>{};
  128. ```