On 18 August 2011 04:34, Matthew Brush mbrush@codebrainz.ca wrote:
On 08/16/2011 10:37 PM, Lex Trotman wrote:
Hi All,
Given that the previous thread had moved way off topic, I started this one.
After thinking about it a bit, considering Matthew's, Colomban's and Thomas's comments and looking at other systems I came to the understanding documented below. Frank probably already knew all this, but I have saved him writing lots of English. If its right and you think its worth using in the newsletter or anywhere else feel free to copy it.
Great information, this should most certainly go into the Wiki (as well as any other useful places).
Oh yeah the wiki, I forgot about that :-)
@Enrico, When will it be declared online, ie prominently pointed to from the website?
[...]
Actually, I think usually the checks end up being done by GModule/dlopen since they try to load the plugin and bail-out when they get undefined symbol errors. Only after that is when the PLUGIN_VERSION_CHECK macro would have a chance to do it's checks. I could be completely wrong on this, but I think it's what I've observed.
You are correct that the sentence above should say "done at runtime by"...
And my next sentence is only partly correct, you may get compile time errors, unfortunately it is not guaranteed. See below for why.
Plugins that wish to use newer features but still support older versions can only make the decision at compile time because the API number of the Geany that the plugin is being loaded into isn't available at runtime. Making the API number available at runtime may be a potential enhancement of the interface and allow plugins to support a wider range of versions.
Wouldn't run-time be too late anyway, since the undefined functions didn't have a chance to get pre-processed out according the version?
Like:
void some_func(void) { if (geany_check_api_version(189)) undefined_function_here(); /* dlopen error */ }
You may be using functions that are not in the API? I see some more detail is needed, the way I understand it is meant to work is:
geanyplugin.h includes geanyfunctions.h which defines a macro for each function in the interface, thus hiding the actual function. Those macros map the function name to a call via a pointer member of the geanyfunctions structure. Since structure members are not checked by dlopen this means it should not check for any Geany functions when opened. If it is telling you that there are undefined functions then you are using functions directly not via the geanyfunctions structure. It may also be possible that there are functions documented as being in the API that are not in geanyfunctions, but thats a bug (either undocument or add to geanyfunctions). The pointers in the geany functions structure have full prototypes so the compiler can check them.
To get the structure declarations, geanyplugin.h includes Geany headers, so functions that are not in the interface are unfortunately made visible to the compiler. This is why the insistence on only using the things documented (and so in geanyfunctions), even if others are visible. This can't be fixed until we remove access to structures eg using getter/setter functions instead, and so remove the requirement for the Geany headers.
This means that your plugin must be compiled against a version of Geany that has the functions you use in the interface, so the compiler can resolve the structure offsets. But so long as it is ABI compatible it can be loaded into any version of Geany with at least your *minimum* API, which you set to not include those functions that you can avoid using, based on the runtime version of the API. If only you could get the runtime version of the API.
The API and ABI number pair should be published (in release notes?) when a release is made for use by plugin packagers.
+1 ... Franks list on the Wiki is an great start!
agree.
IMO, it's quite useful/important that each function/member's doc-comments be annotated with the API version number as well, as has been discussed. This would let developers reading the API docs know *exactly* which API version they should check against to use a certain thing, even between releases.
I agree, I find that the "since" info in things like GTK is invaluable, and I swear at any entries without it.
This means that:
- the interface should be as small as possible so that the least
possible changes impact the ABI.
- structures should not be visible in the interface, instead they
should be hidden behind getter/setter functions, and
++1 ... This would be great.
Most importantly (I think) is that you could do whatever you want with the struct members without breaking API/ABI compatibility and such, since they would be opaque.
Yep, my argument exactly, in fact members could even go away so long as the getter could calculate the value and the setter could change whatever was needed to get the same effect.
- interface wrapper functions should be used to hide the
implementation and side effects.
+1
The last two have not been Geany policy to date but I now propose that they be.
I agree wholeheartedly.
A bit O/T, but it would also be great to get rid of the need for those pesky globals `geany_plugin`, `geany_data`, and `geany_functions` required to defined/included in each TU of the plugin. At least for the first two, they could (one already is) be just passed into the plugin's entry-point (plugin_init()). As for `geany_functions`, I'm not sure, since all those weird macros in `geanyfunctions.h` require it.
See above for geanyfunctions, all these names are needed for the various re-direction macros to work. It is a bit of a fragile part of the interface, but it is compile time detected.
But perhaps they could be wrapped in a PLUGIN_DECLARE macro or similar.
Cheers Lex