Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
module_api.h
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010 Henrik Andersson.
4 Copyright (C) 2010-2012, 2014 johannes hanika.
5 Copyright (C) 2012 Richard Wonka.
6 Copyright (C) 2014, 2016 Tobias Ellinghaus.
7 Copyright (C) 2016 Roman Lebedev.
8 Copyright (C) 2020-2021 Pascal Obry.
9 Copyright (C) 2021 Diederik Ter Rahe.
10 Copyright (C) 2022 Martin Baƙinka.
11
12 darktable is free software: you can redistribute it and/or modify
13 it under the terms of the GNU Lesser General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 darktable is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU Lesser General Public License for more details.
21
22 You should have received a copy of the GNU Lesser General Public License
23 along with darktable. If not, see <http://www.gnu.org/licenses/>.
24*/
25/* NO #include may be added to this file, and it deliberately has NO include guard:
26 * it is an X-macro header, re-included several times with different macros defined
27 * (INCLUDE_API_FROM_MODULE_LOAD, ...) and expanded INSIDE struct bodies to generate
28 * members. An include at the top of it lands inside those structs.
29 * dt_version()/dt_print()/IS_NULL_PTR used below must therefore be provided by each
30 * consuming .c (common/module_versioning.h, common/logging.h, system/macros.h). */
31
32#include <glib.h>
33
34#undef OPTIONAL
35#undef REQUIRED
36#undef DEFAULT
37
38#undef FULL_API_H
39
40#ifdef INCLUDE_API_FROM_MODULE_LOAD
41 #define OPTIONAL(return_type, function_name, ...) \
42 if(!g_module_symbol(module->module, #function_name, (gpointer) & (module->function_name))) \
43 module->function_name = NULL
44 #define REQUIRED(return_type, function_name, ...) \
45 if(!g_module_symbol(module->module, #function_name, (gpointer) & (module->function_name))) \
46 goto api_h_error
47 #define DEFAULT(return_type, function_name, ...) \
48 if(!g_module_symbol(module->module, #function_name, (gpointer) & (module->function_name))) \
49 module->function_name = default_ ## function_name
50
51 dt_print(DT_DEBUG_CONTROL, "[" INCLUDE_API_FROM_MODULE_LOAD "] loading `%s' from %s\n", module_name, libname);
52 module->module = g_module_open(libname, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
53 if(IS_NULL_PTR(module->module)) goto api_h_error;
54 int (*version)();
55 if(!g_module_symbol(module->module, "dt_module_dt_version", (gpointer) & (version))) goto api_h_error;
56 if(version() != dt_version())
57 {
58 fprintf(stderr,
59 "[" INCLUDE_API_FROM_MODULE_LOAD "] `%s' is compiled for another version of dt (module %d (%s) != dt %d (%s)) !\n",
60 libname, abs(version()), version() < 0 ? "debug" : "opt", abs(dt_version()),
61 dt_version() < 0 ? "debug" : "opt");
62 goto api_h_error;
63 }
64 if(!g_module_symbol(module->module, "dt_module_mod_version", (gpointer) & (module->version))) goto api_h_error;
65
66 goto skip_error;
67api_h_error:
68 fprintf(stderr, "[" INCLUDE_API_FROM_MODULE_LOAD "] failed to open `%s': %s\n", module_name, g_module_error());
69 if(module->module) g_module_close(module->module);
70 return 1;
71skip_error:
72 #undef INCLUDE_API_FROM_MODULE_LOAD
73#elif defined(INCLUDE_API_FROM_MODULE_H)
74 #define OPTIONAL(return_type, function_name, ...) return_type (*function_name)(__VA_ARGS__)
75 #define REQUIRED(return_type, function_name, ...) return_type (*function_name)(__VA_ARGS__)
76 #define DEFAULT(return_type, function_name, ...) return_type (*function_name)(__VA_ARGS__)
77 int (*version)();
78 #undef INCLUDE_API_FROM_MODULE_H
79#elif defined(INCLUDE_API_FROM_MODULE_STATIC)
80 /* Statically-linked modules: bind the module's own entry points into its
81 * dt_..._module_so_t. Expanded INSIDE the module's own translation unit, where the
82 * plain API names are in scope and carry this module's asm label (see FULL_API_H
83 * below), so `module->process = process` stores the address of THIS module's process.
84 *
85 * DT_MODULE_HAS_<fn> answers what g_module_symbol() used to answer at dlopen time:
86 * whether the module defines this entry point at all. The generated presence header
87 * defines one for EVERY name in the API, 0 or 1 -- a missing one is a compile error
88 * (DT_MODULE_PICK_DT_MODULE_HAS_foo is not a macro), never a silent NULL.
89 *
90 * DEFAULT's fallback is NOT applied here: the default_<fn> implementations are static
91 * to develop/imageop.c. INCLUDE_API_FROM_MODULE_STATIC_DEFAULTS, expanded there, fills
92 * them in afterwards. REQUIRED takes the symbol unconditionally, so a module missing
93 * one fails to link instead of failing to load. */
94 #define DT_MODULE_PICK_0(fn, fb) fb
95 #define DT_MODULE_PICK_1(fn, fb) fn
96 #define DT_MODULE_PICK_C(h, fn, fb) DT_MODULE_PICK_ ## h(fn, fb)
97 #define DT_MODULE_PICK_B(h, fn, fb) DT_MODULE_PICK_C(h, fn, fb)
98 #define DT_MODULE_PICK(fn, fb) DT_MODULE_PICK_B(DT_MODULE_HAS_ ## fn, fn, fb)
99 #define OPTIONAL(return_type, function_name, ...) module->function_name = DT_MODULE_PICK(function_name, NULL)
100 #define REQUIRED(return_type, function_name, ...) module->function_name = function_name
101 #define DEFAULT(return_type, function_name, ...) module->function_name = DT_MODULE_PICK(function_name, NULL)
102 #undef INCLUDE_API_FROM_MODULE_STATIC
103#elif defined(INCLUDE_API_FROM_MODULE_STATIC_DEFAULTS)
104 /* Second half of the static bind, expanded where the default_<fn> fallbacks are in
105 * scope. Only DEFAULT entries have one; OPTIONAL legitimately stays NULL and REQUIRED
106 * was already bound. */
107 #define OPTIONAL(return_type, function_name, ...) (void)0
108 #define REQUIRED(return_type, function_name, ...) (void)0
109 #define DEFAULT(return_type, function_name, ...) \
110 if(IS_NULL_PTR(module->function_name)) module->function_name = default_ ## function_name
111 #undef INCLUDE_API_FROM_MODULE_STATIC_DEFAULTS
112#elif defined(INCLUDE_API_FROM_MODULE_LOAD_BY_SO)
113 #define OPTIONAL(return_type, function_name, ...) module->function_name = so->function_name
114 #define REQUIRED(return_type, function_name, ...) module->function_name = so->function_name
115 #define DEFAULT(return_type, function_name, ...) module->function_name = so->function_name
116 #undef INCLUDE_API_FROM_MODULE_LOAD_BY_SO
117#else
118 #define FULL_API_H
119 /* Symbol namespacing for statically-linked modules.
120 *
121 * Every module defines the same ~30 entry points -- `process', `name', `commit_params'.
122 * That sameness IS the API: it is what makes a module read like an implementation of an
123 * abstract class, and it is not negotiable. It is also, once the modules stop being one
124 * shared object each and land in a single link, ~30 duplicate symbols per module.
125 *
126 * An asm label renames the emitted SYMBOL and leaves the identifier alone, so the module
127 * source keeps writing `int process(...)' and nothing substitutes tokens: struct members,
128 * locals and `->name' are untouched by construction, which a `#define process ...' could
129 * not promise. The label attaches here, on the first declaration, and the definition in
130 * the module source inherits it. Re-declaring it identically (iop_api.h is re-included by
131 * most modules, by design -- it has no guard) is accepted by GCC and Clang alike.
132 *
133 * DT_MODULE_SYMBOL_PREFIX is set per module by the build. Undefined -- for src/libs and
134 * src/views, still one shared object each and still dlopen'd -- this is a no-op and the
135 * symbols keep their plain names.
136 *
137 * Mach-O prefixes symbols with an underscore and an asm label is emitted VERBATIM, so the
138 * label has to carry it; ELF and PE/COFF x86-64 do not. */
139 #ifdef DT_MODULE_SYMBOL_PREFIX
140 #define DT_MODULE_SYM_STR_(x) #x
141 #define DT_MODULE_SYM_STR(x) DT_MODULE_SYM_STR_(x)
142 #define DT_MODULE_SYM_CAT_(a, b) a ## b
143 #define DT_MODULE_SYM_CAT(a, b) DT_MODULE_SYM_CAT_(a, b)
144 #ifdef __APPLE__
145 #define DT_MODULE_SYM(fn) __asm__("_" DT_MODULE_SYM_STR(DT_MODULE_SYM_CAT(DT_MODULE_SYMBOL_PREFIX, fn)))
146 #else
147 #define DT_MODULE_SYM(fn) __asm__(DT_MODULE_SYM_STR(DT_MODULE_SYM_CAT(DT_MODULE_SYMBOL_PREFIX, fn)))
148 #endif
149 #else
150 #define DT_MODULE_SYM(fn)
151 #endif
152 #define OPTIONAL(return_type, function_name, ...) return_type function_name(__VA_ARGS__) DT_MODULE_SYM(function_name)
153 #define REQUIRED(return_type, function_name, ...) return_type function_name(__VA_ARGS__) DT_MODULE_SYM(function_name)
154 #define DEFAULT(return_type, function_name, ...) return_type function_name(__VA_ARGS__) DT_MODULE_SYM(function_name)
155 #ifdef __cplusplus
156 extern "C" {
157 #endif
158 // these 2 functions are defined by DT_MODULE() macro.
159 #pragma GCC visibility push(default)
160 // returns the version of dt's module interface at the time this module was build
162 // returns the version of this module
164 #pragma GCC visibility pop
165 #ifdef __cplusplus
166 }
167 #endif
168#endif
169
170// clang-format off
171// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
172// vim: shiftwidth=2 expandtab tabstop=2 cindent
173// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
174// clang-format on
175
#define INCLUDE_API_FROM_MODULE_LOAD
@ DT_DEBUG_CONTROL
Definition logging.h:52
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
int dt_module_dt_version()
int dt_module_mod_version()
#define DT_MODULE_SYM(fn)
Definition module_api.h:150
static int dt_version()
The interface version the HOST expects, in the host's own build configuration.