Advertisement
Guest User

Untitled

a guest
Jul 15th, 2022
27
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 30.21 KB | None | 0 0
  1. #pragma once
  2.  
  3. #include "api_types.h"
  4.  
  5. struct tm_allocator_i;
  6. struct tm_temp_allocator_i;
  7.  
  8. // Global registry for API interfaces.
  9. //
  10. // The API registry is one of the most central pieces of The Machinery. It lets plugins expose APIs
  11. // that can be queried for and used by other parts of the system. This is the main way that
  12. // functionality is exposed in The Machinery. You can extend the machinery by adding new APIs in
  13. // your own plugins. See [[plugin.h]].
  14. //
  15. // To use an API, you can query for it in the function that wants to use it, like this:
  16. //
  17. // ~~~c
  18. // struct tm_unicode_api *api = tm_get_api(tm_global_api_registry, tm_unicode_api);
  19. // const bool valid = api->is_valid(utf8);
  20. // ~~~
  21. //
  22. // But querying for an API everytime you use it is a bit verbose and costly. Instead, the most
  23. // common pattern is to query for all the APIs that you want to use in your plugin's
  24. // [[tm_plugin_load_f]] function and store them in static variables. Something like this:
  25. //
  26. // ~~~c
  27. // static struct tm_os_api *tm_os_api;
  28. // static struct tm_unicode_api *tm_unicode_api;
  29. //
  30. // ...
  31. //
  32. // TM_DLL_EXPORT void tm_load_plugin(struct tm_api_registry_api *reg, bool load)
  33. // {
  34. //     tm_os_api = tm_get_api(reg, tm_os_api);
  35. //     tm_unicode_api = tm_get_api(reg, tm_unicode_api);
  36. // }
  37. // ~~~
  38. //
  39. // Now you can use [[tm_os_api]] and [[tm_unicode_api]] anywhere in your file. Requesting APIs like
  40. // this will also add *dependencies* to these APIs for your plugin. If the dependencies can't be
  41. // fulfilled, the plugin will be disabled. See more below.
  42. //
  43. // To register an API, you use the [[tm_set_or_remove_api()]] macro in your [[tm_plugin_load_f]]:
  44. //
  45. // ~~~c
  46. // static tm_my_plugin_api api = {
  47. //     .my_api_function = my_api_function,
  48. //     .other_api_function = other_api_function,
  49. // };
  50. //
  51. // #define tm_my_plugin_api_version TM_VERSION(1, 0, 0)
  52. //
  53. // TM_DLL_EXPORT void tm_load_plugin(struct tm_api_registry_api *reg, bool load)
  54. // {
  55. //     tm_set_or_remove_api(reg, load, tm_my_plugin_api, &api);
  56. // }
  57. // ~~~
  58. //
  59. // Here, `tm_my_plugin_api` is an API struct with function pointers defined in your header file.
  60. // Other parts of The Machinery can now get your API with `tm_get_api(reg, tm_my_plugin_api)` and
  61. // use it, using the declarations in the header file.
  62. //
  63. // Note that the [[tm_set_or_remove_api()]] macro performs two functions. When the plugin is loaded
  64. // (`load == true`), it adds the API to the registry. When the plugin is unloaded (`load == false`),
  65. // it removes the API from the registry.
  66. //
  67. // ## Interfaces
  68. //
  69. // In addition to APIs, the API registry also supports the concept of *interface implementations*.
  70. // The difference between an API and an interface is that APIs only have a single implementation,
  71. // whereas interfaces can have many implementations.
  72. //
  73. // For example the OS API [[tm_os_api]] provides the implementation of OS functions to access files,
  74. // memory, etc. It only has a single implementation for each supported platform, and it is this
  75. // implementation you call upon to perform OS functions.
  76. //
  77. // In contrast, each module that supports unit tests implements the [[tm_unit_test_i]] interface. By
  78. // querying for the [[tm_unit_test_i]] interface, you can enumerate all these implementations and
  79. // run all the unit tests.
  80. //
  81. // You use the [[tm_add_or_remove_implementation()]] macro to register an interface implementation,
  82. // similar to how you use [[tm_set_or_remove_api()]] to register an API. To get all the
  83. // implementations of a particular interface, use the [[TM_EACH_IMPLEMENTATION()]] macro.
  84. //
  85. // ## Hot reloading
  86. //
  87. // The API registry is built to support hot reloading of APIs. When a plugin is hot-reloaded, the
  88. // old version's [[tm_plugin_load_f]] function is called with `load == false`. This will remove its
  89. // APIs from the registry. When the API is removed, the function table is still kept in memory, it's
  90. // just cleared out with NULL pointers. Then, the new version's [[tm_plugin_load_f]] function is
  91. // called with `load == true`. This will copy the new function pointers into the API table. Callers
  92. // using the API table obtained from [[tm_get_api()]] will automatically call the new functions.
  93. //
  94. // !!! TODO: TODO
  95. //     Add a note about hot reloading of interfaces.
  96. //
  97. // ## Load order
  98. //
  99. // The Machinery doesn't have any way of controlling the load order of plugins. This means that when
  100. // you call [[tm_get_api()]], the plugin that provides the API might not be loaded yet. Thus, you
  101. // cannot use the API in the plugin's load function:
  102. //
  103. // ~~~c
  104. // TM_DLL_EXPORT void tm_load_plugin(struct tm_api_registry_api *reg, bool load)
  105. // {
  106. //     tm_my_plugin_api = tm_get_api(reg, tm_my_plugin_api);
  107. //
  108. //     // This will likely crash, because the plugin that provides `tm_my_plugin_api` might not
  109. //     // be loaded yet.
  110. //     tm_my_plugin_api->my_api_function();
  111. // }
  112. // ~~~
  113. //
  114. // An exception to this rule is that you can assume that all foundation APIs have been loaded before
  115. // your plugin's [[tm_plugin_load_f]] function gets called. So it's safe to use things like
  116. // [[tm_api_registry_api]] and [[tm_allocator_api]].
  117. //
  118. // If you want to use another API as part of the initialization of your plugin, you need to wait
  119. // until all plugins have been loaded. You can do that by registering a [[tm_plugin_init_i]]
  120. // callback. This callback will be called after all plugins have loaded. At this point the [[set()]]
  121. // functions will have been called and the APIs will be available.
  122. //
  123. // ~~~c
  124. // static struct tm_my_plugin_api *tm_my_plugin_api;
  125. //
  126. // static void init(struct tm_plugin_o *inst, tm_allocator_i *a)
  127. // {
  128. //     tm_my_plugin_api->my_api_function();
  129. // }
  130. //
  131. // static struct tm_plugin_init_i plugin_init = {
  132. //     .init = init,
  133. // };
  134. //
  135. // TM_DLL_EXPORT void tm_load_plugin(struct tm_api_registry_api *reg, bool load)
  136. // {
  137. //     tm_my_plugin_api = tm_get_api(reg, tm_my_plugin_api);
  138. //
  139. //     tm_add_or_remove_implementation(reg, load, tm_plugin_init_i, &plugin_init);
  140. // }
  141. // ~~~
  142. //
  143. // If [[tm_get_api()]] is called before the API has been set, it will return a pointer to an empty
  144. // API struct. (I.e., all the function pointers in the struct will be NULL.) Later, when [[set()]]
  145. // is called, the API will be filled in with actual function pointers that you can use.
  146. //
  147. // ## Optional APIs
  148. //
  149. // When you call [[tm_get_api()]] you are saying that your plugin depends on that API. If that API
  150. // is not available, an error will be generated and your plugin will be disabled.
  151. //
  152. // Sometimes, you want to use an API if it's available, but you are not dependent on it. (Maybe it
  153. // just provides some nice to have features.) In this case, you can use [[tm_get_optional_api()]].
  154. //
  155. // ~~~c
  156. // tm_get_optional_api(reg, &tm_my_plugin_api_opt, tm_my_plugin_api);
  157. // ~~~
  158. //
  159. // You can later check if the API was available by checking if `tm_my_plugin_api_opt` is NULL. Note
  160. // that you must perform this check after loading has completed:
  161. //
  162. // ~~~c
  163. // if (tm_my_plugin_api_opt)
  164. //     tm_my_plugin_api_opt->do_stuff();
  165. // ~~~
  166. //
  167. // ## Plugin versioning
  168. //
  169. // The API registry has a system for versioning APIs. This is needed because as APIs are modified,
  170. // code that was written to work with one version of the API might not work with the next version.
  171. // API versions are defined by a [[tm_version_t]] structure and the current API version is defined
  172. // in the header file, together with the API.
  173. //
  174. // ~~~c
  175. // struct tm_my_api {
  176. //     ...
  177. // };
  178. //
  179. // // Defines this API's current version as 1.0.2.
  180. // #define tm_my_api_version TM_VERSION(1, 0, 2)
  181. // ~~~
  182. //
  183. // The version of an API consists of three parts (*major*, *minor*, *patch*) using the
  184. // [SemVer](https://semver.org/) semantic versioning scheme:
  185. //
  186. // * The major version is changed for any breaking change.
  187. // * The minor version is changed when new functionality is added in a backwards-compatible way.
  188. // * The patch version is changed for backwards-compatible bug fixes.
  189. //
  190. // Backwards-compatible means a caller using the old header version can still use the new ABI.
  191. // Examples of backwards-compatible changes are:
  192. //
  193. // * Adding functions at *the end* of the API.
  194. // * Changing the names of functions and parameters (without changing the type singnature).
  195. // * Repurposing unused bits in structs.
  196. //
  197. // Note that adding functions in the middle of the API or changing the type of parameters or struct
  198. // fields is not backwards compatible.
  199. //
  200. // A call to `tm_get_api(reg, tm_my_api)` is expanded to `reg->get("tm_my_api", tm_my_api_version)`.
  201. // When this is compiled, `tm_my_api_version` is locked to the current value. If you recompile the
  202. // plugin with newer APIs, it will be locked to the updated `tm_my_api_version`.
  203. //
  204. // When you are requesting a specific version of an API, you may get back any compatible version
  205. // (same major version, same or newer minor version). If no compatible version is found, an error
  206. // message is printed and your plugin will be disabled.
  207. //
  208. // Let's see how that works in some practical examples:
  209. //
  210. // **Compatibility with new minor versions**:
  211. //
  212. // If you compiled your plugin for 1.0.0 and a new engine comes out with API 1.1.0, your plugin will
  213. // continue to work. Your request for 1.0.0 will be fulfilled by 1.1.0 which should be backwards
  214. // compatible with 1.0.0. However if a new engine version comes out with 2.0.0 your plugin will be
  215. // disabled since it is not compatible with the new API.
  216. //
  217. // **Compatibility with older minor versions**:
  218. //
  219. // If the current API version is at 1.1.0, but you want your plugin to work with 1.0.0, you should
  220. // explicitly request that version, instead of just using [[tm_get_api()]] (which will always get
  221. // the latest version):
  222. //
  223. // ~~~c
  224. // tm_my_api = reg->get(TM_STRINGIFY(tm_my_api), TM_VERSION(1, 0, 0));
  225. // ~~~
  226. //
  227. // Depending on what API you are running against, this might return 1.0.0, 1.1.0 or even 1.97.0. (If
  228. // you were requesting 1.1.0, but only 1.0.0 was available, you would get an error and your plugin
  229. // would be unloaded, since in this case you are saying that you want to use 1.1.0 features, which
  230. // are not available in 1.0.0.
  231. //
  232. // You can check which version you actually got by calling [[version()]] on the returned `tm_my_api`
  233. // pointer. You can also check if function pointers that were added in 1.1.0 are available or not.
  234. //
  235. // **Compatiblity with new major versions**:
  236. //
  237. // If you've compiled your plugin for 1.0.0 and a new engine comes out with 2.0.0, your plugin will
  238. // not work with the new API. The only way your plugin will work in this scenario is if the API
  239. // developer has decided to continue to support the 1.0.0 version of the API. They can do this by
  240. // making separate [[tm_set_or_remove_old_api_version()]] calls for each version:
  241. //
  242. // ~~~c
  243. // struct tm_my_api_v100 {
  244. //     ...
  245. // };
  246. // #define tm_my_api_v100_name "tm_my_api"
  247. // #define tm_my_api_v100_version TM_VERSION(1, 0, 0)
  248. //
  249. // struct tm_my_api {
  250. //     ...
  251. // };
  252. // #define tm_my_api_version TM_VERSION(2, 0, 0)
  253. //
  254. //
  255. // tm_set_or_remove_old_api_version(reg, true, tm_my_api_v100);
  256. // tm_set_or_remove_api(reg, true, tm_my_api);
  257. // ~~~
  258. //
  259. // If the API developer has done this, your plugin will continue to work with their new plugin
  260. // version. When you request 1.0.0, you will get the old `tm_my_api_v100` version exported by the
  261. // plugin.
  262. //
  263. // For your own plugins, you can decide whether you want to continue to support old API versions or
  264. // not. Doing so means more work, since you need to support more API versions, but less frustration
  265. // for your users.
  266. //
  267. // **Compatibility with older major versions**:
  268. //
  269. // If you want your plugin to be compatible with multiple older major versions of the API, you can
  270. // request all the older versions of the API using [[tm_get_optional_old_api_version()]] and use
  271. // whatever is available:
  272. //
  273. // ~~~c
  274. // tm_get_optional_old_api_version(&tm_my_api_v10, reg, tm_my_api_v10);
  275. // tm_get_optional_old_api_version(&tm_my_api_v20, reg, tm_my_api_v20);
  276. // tm_get_optional_old_api_version(&tm_my_api_v30, reg, tm_my_api_v30);
  277. // tm_get_optional_api(&tm_my_api, reg, tm_my_api);
  278. // ~~~
  279. //
  280. // Note that we use *optional* calls here, since we don't want to fail if the versions are not
  281. // available.
  282.  
  283. // Listener for receiving information about changes to the API registry. Use [[add_listener()]] to add
  284. // a listener to the API registry.
  285. typedef struct tm_api_registry_listener_i
  286. {
  287.     void *ud;
  288.  
  289.     // Called when an implementation was added for the interface `name` with the `version`.
  290.     void (*add_implementation)(void *ud, const char *name, tm_version_t version, const void *implementation);
  291. } tm_api_registry_listener_i;
  292.  
  293. // Global registry that keeps track of loaded APIs and interface implementations.
  294. //
  295. // The difference between an API and an interface is that APIs only have a single implementation,
  296. // whereas interfaces can have many implementations.
  297. //
  298. // For example the OS API [[tm_os_api]] provides the implementation of OS functions to access files,
  299. // memory, etc. It only has a single implementation for each supported platform, and it is this
  300. // implementation you call upon to perform OS functions.
  301. //
  302. // In contrast, each module that supports unit tests implements the [[tm_unit_test_i]] interface. By
  303. // querying for the [[tm_unit_test_i]] interface, you can enumerate all these implementations and run
  304. // all the unit tests.
  305. struct tm_api_registry_api
  306. {
  307.     // Can be used to check the version of the [[tm_api_registry_api]] itself. Plugins that don't
  308.     // have a matching version can use this to disable themselves.
  309.     //
  310.     // (Although once the [[tm_api_registry_api]] is locked at version 1.0.0, we don't expect to
  311.     // make any incompatible changes to it, as doing so would break the backwards compatibility of
  312.     // all plugins.)
  313.     tm_version_t (*api_registry_version)(void);
  314.  
  315.     // Sets an API in the registry. `name` is the name of the API that is implemented, `version` is
  316.     // the version that is being set, and `api` is a pointer to the struct of function pointers
  317.     // defining the API. `bytes` is the size of this struct.
  318.     //
  319.     // APIs can be implemented only once. If you call [[set()]] again with the same major version
  320.     // number, it replaces the previous API pointers. This can be used to implement hot-reload of
  321.     // APIs.
  322.     //
  323.     // You typically use the [[tm_set_or_remove_api()]] macro instead of calling this directly.
  324.     void (*set)(const char *name, tm_version_t version, const void *api, uint32_t bytes);
  325.  
  326.     // Removes `API` if it has been [[set()]].
  327.     //
  328.     // You typically use the [[tm_set_or_remove_api()]] macro instead of calling this directly.
  329.     void (*remove)(const void *api);
  330.  
  331.     // Gets a pointer to the API implementing the API `name` with a matching `version`.
  332.     //
  333.     // Versions match if they have the same major version number, except if the major version number
  334.     // is zero in which case an exact match is required.
  335.     //
  336.     // `get(name)` is guaranteed to always return the same pointer, throughout the lifetime of an
  337.     // application (whether `set(name)` has been called zero, one or multiple times). It returns a
  338.     // pointer to internal API registry memory and the actual API pointers are copied to this memory
  339.     // by [[set()]].
  340.     //
  341.     // On hot-reload these function pointers will be overwritten, but this is transparent to users
  342.     // of the API. They can continue to use the same interface pointer and will call the new methods
  343.     // automatically. (If they have cached the function pointers in the API locally, they will keep
  344.     // calling the old methods.)
  345.     //
  346.     // Calling [[get()]] on an API that hasn't been loaded yet will return a struct full of NULL
  347.     // function pointers. When the API is loaded (and calls [[set()]]), these NULL pointers will be
  348.     // replaced by the real function pointers of the API.
  349.     //
  350.     // To test whether an API has been loaded, you can test if it contains NULL function pointers or
  351.     // not, or call [[version()]].
  352.     //
  353.     // Calling [[get()]] from a plugin indicates that your plugin is dependent on the requested API.
  354.     // If the API is not available, an error will be generated during the
  355.     // [[disable_apis_missing_dependencies()]] phase and your plugin will be disabled. If your
  356.     // plugin is not dependent on the API, use [[get_optional()]] instead.
  357.     //
  358.     // You typically use the [[tm_get_api()]] macro instead of calling this function directly.
  359.     void *(*get)(const char *name, tm_version_t version);
  360.  
  361.     // As [[get()]], but used for *optional* APIs. This means that if the API is available, your
  362.     // plugin will use it, but if it's not, your plugin will still be able to run.
  363.     //
  364.     // [[get_optional()]] does not create a dependency on the API, your plugin will be allowed to
  365.     // run even if the API is not available.
  366.     //
  367.     // Note that unlike [[get()]], [[get_optional()]] takes a pointer to the API pointer as its
  368.     // argument. This pointer is saved and set to the API pointer if a matching  [[set()]] call is
  369.     // made. This means that you can test the pointer to check if the API is available or not:
  370.     //
  371.     // ~~~c
  372.     // if (tm_my_api_opt)
  373.     //     tm_my_api_opt->foo();
  374.     // ~~~
  375.     //
  376.     // You typically use the [[tm_get_optional_api()]] macro instead of calling this function
  377.     // directly.
  378.     void (*get_optional)(void **api, const char *name, tm_version_t version);
  379.  
  380.     // Returns the loaded version of the API `api`. If the API is not loaded, returns `{0, 0, 0}`.
  381.     //
  382.     // !!! NOTE: NOTE
  383.     //     If you want to check whether an API is available, you should call this *after*
  384.     //     the loading phase has completed. If you call this immediately after [[get()]],
  385.     //     it might return `{0}` for an API that will be set by a later call to [[set()]].
  386.     tm_version_t (*version)(void *api);
  387.  
  388.     // Adds an implementation of the interface named `name` with `version`.
  389.     //
  390.     // You typically use the [[tm_add_or_remove_implementation()]] macro instead of calling this
  391.     // directly.
  392.     void (*add_implementation)(const char *name, tm_version_t version, const void *implementation);
  393.  
  394.     // Removes the specified implementation of the interface `name` and `version`.
  395.     //
  396.     // You typically use the [[tm_add_or_remove_implementation()]] macro instead of calling this
  397.     // directly.
  398.     void (*remove_implementation)(const char *name, tm_version_t version, const void *implementation);
  399.  
  400.     // Returns an [[carray.inl]] of `void *` of all the implementations implementing the interface
  401.     // `name`, `version`.
  402.     //
  403.     // You typically use the [[tm_implementations()]] macro instead of calling this directly.
  404.     void **(*implementations)(const char *name, tm_version_t version);
  405.  
  406.     // Returns the number of implementations implementing the interface `name`, `version`.
  407.     //
  408.     // You typically use the [[tm_num_implementations()]] macro instead of calling this directly.
  409.     uint32_t (*num_implementations)(const char *name, tm_version_t version);
  410.  
  411.     // Returns the first implementation of the interface `name`, `version`, or `NULL` if there are
  412.     // no implementations. This is useful if you don't care which implementation you are using.
  413.     //
  414.     // You typically use the [[tm_first_implementation()]] macro instead of calling this directly.
  415.     void *(*first_implementation)(const char *name, tm_version_t version);
  416.  
  417.     // As [[first_implementation()]], but asserts that there is exactly one implementation of the
  418.     // interface `name`.
  419.     //
  420.     // You typically use the [[tm_single_implementation()]] macro instead of calling this directly.
  421.     void *(*single_implementation)(const char *name, tm_version_t version);
  422.  
  423.     // Adds a listener that will be called with changes to the API registry. Currently, only an
  424.     // [[add_implementation()]] callback is provided.
  425.     void (*add_listener)(const tm_api_registry_listener_i *listener);
  426.  
  427.     // Returns a pointer to a static variable that survives plugin reloads. `id` is a unique
  428.     // identifier for the variable and `size` its size in bytes. The first time this function is
  429.     // called, the variable will be zero-initialized.
  430.     //
  431.     // Use of static variables in DLLs can be problematic, because when the DLL is reloaded, the
  432.     // new instance of the DLL will get a new freshly initialized static variable, losing whatever
  433.     // content the variable had before reload. By using [[static_variable()]] instead, the variable
  434.     // data is saved in permanent memory.
  435.     //
  436.     // Instead of this:
  437.     //
  438.     // ~~~c dont
  439.     // uint64_t count;
  440.     //
  441.     // void f()
  442.     // {
  443.     //     ++count;
  444.     // }
  445.     // ~~~
  446.     //
  447.     // You would do this:
  448.     //
  449.     // ~~~c
  450.     // uint64_t *count_ptr;
  451.     //
  452.     // void load(struct tm_api_registry_api *reg)
  453.     // {
  454.     //     count_ptr = (uint64_t *)reg->static_variable(TM_STATIC_HASH("my_count", 0xa287d4b3ec9c2109ULL),
  455.     //         sizeof(uint64_t), __FILE__, __LINE__);
  456.     // }
  457.     //
  458.     // void f()
  459.     // {
  460.     //     ++*count_ptr;
  461.     // }
  462.     // ~~~
  463.     void *(*static_variable)(tm_strhash_t id, uint32_t size, const char *file, uint32_t line);
  464.  
  465.     // Starts a context for subsequent [[get()]],  [[set()]] and [[add_implementation()]] calls.
  466.     //
  467.     // All [[get()]] calls made in a particular context are considered to be dependencies for the
  468.     // [[set()]] and [[add_implementation()]] calls in that context. When plugin loading has
  469.     // completed, you can call [[disable_apis_missing_dependencies()]] to resolve those dependencies.
  470.     //
  471.     // Contexts can be nested. In a nested context, the [[get()]] calls of the parent context are
  472.     // considered dependencies of the child context.
  473.     //
  474.     // The plugin system calls [[begin_context()]] before loading a plugin, and [[end_context()]] at
  475.     // the completion, so you only need to call [[begin_context()]] and [[end_context()]] manually
  476.     // if your plugin as sub-contexts.
  477.     void (*begin_context)(const char *name);
  478.  
  479.     // Ends a context started by [[begin_context()]].
  480.     void (*end_context)(const char *name);
  481.  
  482.     // Resolves missing APIs.
  483.     //
  484.     // Call this function after plugin loading has completed. This function will check all contexts
  485.     // (created by [[begin_context()]], [[end_context()]] for missing API dependencies ([[get()]]
  486.     // calls that failed. If any dependencies are missing, an error will be printed and all the
  487.     // [[set()]] calls and [[add_implementation()]] calls made in the context will be disabled.
  488.     // (I.e., if a plugin can't fulfill it's dependencies, it will be disabled.)
  489.     void (*disable_apis_missing_dependencies)(void);
  490.  
  491.     // Returns a [[carray.inl]] of all available versions of the specified API.
  492.     tm_version_t *(*available_versions)(const char *name, struct tm_temp_allocator_i *ta);
  493. };
  494.  
  495. #define tm_api_registry_api_version TM_VERSION(1, 0, 0)
  496.  
  497. // Uses [[get()]] to get the current version of the API `TYPE`. Relies on a `TYPE_version` define
  498. // to get the version.
  499. #define tm_get_api(reg, TYPE) \
  500.     (struct TYPE *)reg->get(#TYPE, TYPE##_version)
  501.  
  502. // As [[tm_get_api()]], but calls [[get_optional()]].
  503. #define tm_get_optional_api(reg, ptr, TYPE)                           \
  504.     do {                                                              \
  505.         struct TYPE **typed_ptr = ptr;                                \
  506.         reg->get_optional((void **)typed_ptr, #TYPE, TYPE##_version); \
  507.     } while (0)
  508.  
  509. // Returns an older version of an API. `VERSION_TYPE` should be the type name of a struct defining
  510. // the old API. This macro depends on there being `VERSION_TYPE_name` and `VERSION_TYPE_version`
  511. // macros that define the name and the version of the API. E.g.:
  512. //
  513. // ~~~c
  514. // struct tm_my_api_v100 {
  515. //     ...
  516. // };
  517. // #define tm_my_api_v100_name "tm_my_api"
  518. // #define tm_my_api_v100_version TM_VERSION(1, 0, 0)
  519. //
  520. // tm_my_api_v100 = tm_get_old_api_version(reg, tm_my_api_v100);
  521. // ~~~
  522. #define tm_get_old_api_version(reg, VERSION_TYPE) \
  523.     (struct VERSION_TYPE *)reg->get(VERSION_TYPE##_name, VERSION_TYPE##_version)
  524.  
  525. // As [[tm_get_old_api_version()]], but calls [[get_optional()]].
  526. #define tm_get_optional_old_api_version(reg, ptr, VERSION_TYPE)                             \
  527.     do {                                                                                    \
  528.         struct VERSION_TYPE **typed_ptr = ptr;                                              \
  529.         reg->get_optional((void **)typed_ptr, VERSION_TYPE##_name, VERSION_TYPE##_version); \
  530.     } while (0)
  531.  
  532. // If `load` is *true*, uses [[set()]] to set the current version of the API `TYPE` to the specified
  533. // `ptr`. If `load` is *false*, removes the API with [[remove()]]. Relies on a `TYPE_version` macro
  534. // to specify the current version.
  535. #define tm_set_or_remove_api(reg, load, TYPE, ptr)                           \
  536.     do {                                                                     \
  537.         if (load) {                                                          \
  538.             struct TYPE *typed_ptr = ptr;                                    \
  539.             reg->set(#TYPE, TYPE##_version, typed_ptr, sizeof(struct TYPE)); \
  540.         } else                                                               \
  541.             reg->remove(ptr);                                                \
  542.     } while (0)
  543.  
  544. // As [[tm_set_or_remove_api()]] but used to export an old version of the API for backwards
  545. // compatibility:
  546. //
  547. // ~~~c
  548. // struct tm_my_api_v100 {
  549. //     ...
  550. // };
  551. // #define tm_my_api_v100_name "tm_my_api"
  552. // #define tm_my_api_v100_version TM_VERSION(1, 0, 0)
  553. //
  554. // tm_set_or_remove_old_api_version(reg, load, tm_my_api_v100, api);
  555. // ~~~
  556. #define tm_set_or_remove_old_api_version(reg, load, VERSION_TYPE, ptr)                                     \
  557.     do {                                                                                                   \
  558.         if (load) {                                                                                        \
  559.             struct VERSION_TYPE *typed_ptr = ptr;                                                          \
  560.             reg->set(VERSION_TYPE##_name, VERSION_TYPE##_version, typed_ptr, sizeof(struct VERSION_TYPE)); \
  561.         } else                                                                                             \
  562.             reg->remove(ptr);                                                                              \
  563.     } while (0)
  564.  
  565. // Adds or removes an implementation of the interface type `TYPE` based on the `load` flag.
  566. //
  567. // Relies on a `TYPE_version` define to get the version of the interface.
  568. #define tm_add_or_remove_implementation(reg, load, TYPE, ptr)                                                  \
  569.     do {                                                                                                       \
  570.         TYPE *p = ptr;                                                                                         \
  571.         (load ? reg->add_implementation : reg->remove_implementation)(#TYPE, TYPE##_version, (const void *)p); \
  572.     } while (0)
  573.  
  574. // Returns [[num_implementations()]] for the interface `TYPE`.
  575. #define tm_num_implementations(reg, TYPE) \
  576.     reg->num_implementations(#TYPE, TYPE##_version)
  577.  
  578. // Returns [[implementations()]] for the interface `TYPE`.
  579. #define tm_implementations(reg, TYPE) \
  580.     (TYPE *const *)reg->implementations(#TYPE, TYPE##_version)
  581.  
  582. // Returns [[single_implementation()]] for the interface `TYPE`.
  583. #define tm_single_implementation(reg, TYPE) \
  584.     (TYPE *)reg->single_implementation(#TYPE, TYPE##_version)
  585.  
  586. // Returns [[first_implementation()]] for the interface `TYPE`.
  587. #define tm_first_implementation(reg, TYPE) \
  588.     (TYPE *)reg->first_implementation(#TYPE, TYPE##_version)
  589.  
  590. // Convenience macro for looping over all implementations of the interface `TYPE`.
  591. //
  592. // Use like this:
  593. //
  594. // ~~~c
  595. //  for (TM_EACH_IMPLEMENTATION(tm_the_truth_create_types_i, create_types))
  596. //      create_types(tt);
  597. // ~~~
  598. #define TM_EACH_IMPLEMENTATION(TYPE, VAR)                                                                                   \
  599.     TYPE **implementations = (TYPE **)tm_global_api_registry->implementations(#TYPE, TYPE##_version), **iter = 0, *VAR = 0; \
  600.     (iter = (iter ? iter : implementations)) != 0 && (iter != tm_carray_end(implementations)) && (VAR = *iter) != 0;        \
  601.     ++iter
  602.  
  603. // As [[TM_EACH_IMPLEMENTATION()]], but allows you to specify the interface name, instead of using
  604. // `#TYPE`.
  605. #define TM_EACH_IMPLEMENTATION_NAME(name, TYPE, VAR)                                                                       \
  606.     TYPE **implementations = (TYPE **)tm_global_api_registry->implementations(name, TYPE##_version), **iter = 0, *VAR = 0; \
  607.     (iter = (iter ? iter : implementations)) != 0 && (iter != tm_carray_end(implementations)) && (VAR = *iter) != 0;       \
  608.     ++iter
  609.  
  610. // As [[TM_EACH_IMPLEMENTATION()]], but allows you to specify the interface name and version, instead
  611. // of using `#TYPE` and `TYPE##_version`.
  612. #define TM_EACH_IMPLEMENTATION_NAME_VERSION(name, version, TYPE, VAR)                                                \
  613.     TYPE **implementations = (TYPE **)tm_global_api_registry->implementations(name, version), **iter = 0, *VAR = 0;  \
  614.     (iter = (iter ? iter : implementations)) != 0 && (iter != tm_carray_end(implementations)) && (VAR = *iter) != 0; \
  615.     ++iter
  616.  
  617. // Helper macro to be used together with [[TM_EACH_IMPLEMENTATION()]]. Within a
  618. // [[TM_EACH_IMPLEMENTATION()]] loop, it returns the loop index.
  619. #define TM_EACH_IMPLEMENTATION_INDEX (iter - implementations)
  620.  
  621. #if defined(TM_LINKS_FOUNDATION)
  622.  
  623. // Extern variable holding the global plugin registry.
  624. extern struct tm_api_registry_api *tm_global_api_registry;
  625.  
  626. // Inits the global registry. You must call this before using the
  627. // [[tm_global_api_registry]] variable.
  628. void tm_init_global_api_registry(struct tm_allocator_i *a);
  629.  
  630. // Shuts down the global registry. You must call this to free the resources
  631. // allocated by [[tm_init_global_api_registry()]].
  632. void tm_shutdown_global_api_registry(struct tm_allocator_i *a);
  633.  
  634. // Convenience function to register all foundation APIs in the specified
  635. // registry.
  636. void tm_register_all_foundation_apis(struct tm_api_registry_api *pr);
  637.  
  638. #endif
  639.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement