Conversation
* Allow including redismodule.h in several source files, by declaring API function vars weak [i.e., __attribute__ ((weak))]. * Avoid double-mentions of APIs by using X-macro acrobatics * Allow for API extensions via REDISMODULE_XAPI_EXTENSIONS macro * Removed explicit REGISTER_API-s from module.c * Support inclusion from C++ sources See also redis#6330
|
However @rafie, do I understand correctly that this no longer uses the trick of using function pointers that we fill with our implementations? So it's now the linker that will bind the symbols? Are you sure this is as flexible and future-proof as the original approach? We guarantee ABI compatibility in the future. By filling the function pointers manually, we will be able to do many tricks to guarantee the compatibility in the future, I'm not sure if we'll be able to do the same with this new way. |
|
Btw it should be possible to have the same advantages without removing the function pointer approach, just by declaring them extern in the successive inclusions, but not the first. |
|
Oh probably it still uses the function pointers, I just don't understand the popmacro/pushmacro stuff, I'll have to check better. |
|
Wasn't sure i wanted to comment, but since you asked...
Now to the elephant in the room: instead we can keep the old way, and just add macros around the function declarations so that we can control their attributes before including the header file, i think this is the common way this type thing are handled. i.e. i would also argue that any attributes like for instance, change redismodule.h to something the lines of: i.e. dropping the REDISMODULE_API_FUNC macro too (which also bothers me causing the function declarations to be non C syntax). so then, when including the header, you can do: p.s. maybe we can unify the list that uses REDISMODULE_GET_API and REGISTER_API into one list using some macro, one that just lists the API names and doesn't mess with their prototypes. |
|
Ok @oranagra, I completely trust your opinion here. So... let's try with baby steps. What about resorting to the PR from @mnunberg, but perhaps also dumbing it down more to just have a way to include the file as extern without the other changes? So that we incrementally start to get better without doing very hard changes. |
|
@antirez It still use same method of assigning function pointers. (which causes the linked to complain about multiple symbol definitions.) The PR is suggesting (for each C file including redismodule.h): The As for the pragma push_macro, this is not essential. We can #define __funny_name_X instead of #define X, and undefine it afterwards. The push/pop-macro just makes it cleaner. As far as aesthetics go, I find it quite reasonable to use a mata-macro that is given type, function name, and argument list, and applies them in different forms. |
|
We can devide-and-conquer as follows: In the .c file that's calling RedisModule_OnLoad(), we define REDISMODULE_API_DEFS as follows: For this .c file, API vars are defined, and a locally-defined For platforms that support weak symbols, we go on with the suggested scheme. We can also encourage all users to convert to the |
|
@rafie I'm not a fan of having something defined before including a header file.. it results in more cruft/clutter, or it requires a dedicated file whose purpose it is to only include that file. @oranagra 's solution is probably the cleanest. I'm not a fan of having all the API declarations as X-macros either. It does reduce redundancy, but at the cost of making it even more difficult to read the API. I think it should be good if we have at least one "proper" declaration. I am a bit partial to my own implementation as I think it provides a happy medium between readability and eliminating redundancy. Not a fan of using |
|
for what it worth, i support the concept of i'm personally against the X-macros, i think the prototype declarations should include pre-processor replacements, but not macro (functions). i suggest to consider using such macro to just reduce the repetition of REDISMODULE_GET_API and REGISTER_API, but not the prototypes. I also feel that we should avoid using attributes and pragmas in the header since they may be non-standard, but i do think we must allow these to be added by pre-processor at the control of whoever includes the header file. so i would go with something like this way the caller can either add extern (rather easily), or add whatever attribute he wants even if non-standard. @mnunberg can you post a link to your approach, i don't recall what it was. |
|
@mnunberg In a Redis module, there's a single file that's defines RedisModule_OnLoad(). I think it's no big burden to have that file to define REDISMODULE_API_DEFS. I consider that more elegant than having RedisModule_Init() compiled into every compilation unit. @oranagra Wouldn't adding REDIS_MODULE_PRE_API and REDIS_MODULE_PRE_API for more than 200 APIs harm readability? |
|
From my experience the pre/post preprocessor defs is a common pattern and I ignore them when reading since I know they're adding minor modifier keywords, so they don't affect the readability for me. (maybe also because I have a wide monitor?) What I didn't like in the current PR (and even the original code) is the extra parenthesis and commas that make it looks unlike a C syntax, and in my eyes the PRE and POST preprocessor are not bothering me as much since i can ignore them as if undefined and the syntax is still C. Regarding dropping the I guess all the arguments on these two approaches are already here, I thinks it's time to either hear some new options, or wait for Salvatore to decide. |
|
The way I see it, the core problems we're trying to solve here are:
I think the simplest fix for these issues is clear, i.e. My suggestion is to apply the simplest fix which is less likely to have compatibility issues (weak symbols) or break existing modules. I do think there are possible major improvements for In a nutshell, what I'd be happy to see is this:
But again, I think this is a separate future discussion. |
|
@yossigo The all-containing-struct options was considered - it adds an extra dereference for every API call, and does not affect the question of initializing API vars: Here's a refinement: If Thus, for most users, no code change is required and Bottom line:
The decision regarding X-macros is orthogonal to this. Using them, however, will make implementation easier. |
|
@rafie I think the additional pointer de-reference is negligible, and it does change the initialization matter because struct RedisModuleAPI {
FunctionType MyFunction();
};
extern struct RedisModuleAPI *RedisModule_Init(...);But as I stated before, I would raise this only as a far future option. Why go through the trouble of supporting both weak symbols and no weak symbols? What do we tell module developers to use, in particular if we want the module to compile in both cases? Feels like it's just pushing the problem to the module. |
See also #6330