Hi Colomban,
Whilst not Geany specific, we were discussing it on #geany and it shows a use-case for the C++ pointer to member syntax.
Consider interfaces between separately compiled things, like Geany plugins. (I am not proposing changing the Geany plugin interface, just using it as an example).
C interface
struct a{ int b; }; void a_f( struct a* );
C++ interface
struct a{ int b; void f(); };
The requirement is to minimise the need to re-compile the plugin so old plugins will work for as long as possible. In the following "pass to the plugin" may be as parameters and/or table/struct entries, but not as resolvable symbols.
Simplest approach C:
In Geany have a c; a* p = &c; pass p to the plugin In the plugin do p->b and a_f( p ), but a_f needs to be resolved by dlopen when the plugin is loaded (and there are a lot of functions in the Geany interface) and the *definition* of a needs to be visible to define p->b. So the plugin needs recompiling every time a changes, although with C if you only add to the end it will remain binary compatible so long as no compiler options or anything else change the arrangement of the struct (eg packing it).
Simplest approach C++:
in Geany have a c; p = &c; pass p to plugin In the plugin do p->b and p->f(), now dlopen doesn't need to resolve the function names, but the dependence on the definition of a is still there.
Single pointer approach C:
In Geany have a c; int *bp = &c.b; ahhh, there is no way to make a pointer to a_f(b), ie to include the parameter, so this method applies to data members only, pass bp to the plugin In the plugin use *bp to access the data, fine, but we still must do functions differently.
Single pointer approach C++:
In Geany have a c; int* bp = &c.b; ahhh, there is no way to make a pointer to b.f() so this method applies to data members only, pass bp to the plugin. In the plugin use *bp to access the data, fine, but have to do functions differently.
Two part (two pieces of information passed) approach in C:
In Geany have:
a c; size_t bo = offsetof(a, b); a* cp = &c; void (*fp)() = a_f; pass bo, cp and fp to the plugin
In the plugin have *((int*)((char*)cp+bo)) to access the data and (*fp)(cp) to call the function. The plugin no longer needs to know the layout of a so it never needs to be re-compiled unless the types of the members or functions it is using change, but data access is messy and type unsafe and the approach differs between data and functions. And I think it doesn't violate strict aliasing since the int* is a pointer to a type which is a member of a so it can alias safely.
C like two part[1] approach in C++:
In Geany have:
a c; size_t bo = offsetof(a,b), ahhh no, can't guarantee the use of offsetof on a non-POD type, and although a as it stands is ok, add a constructor or destructor or private member or virtual member and boom. So can't use offsetof on data. Still can't make a pointer (as an ordinary pointer) or an offset to a member function, so this method fails for both data and functions.
C++ type two part approach in C++:
In Geany have:
a c; int a::*bp = &a::b; a* cp = &c; void (a::*fp)() = &a::f; pass bp, cp, fp to the plugin
In the plugin use cp->*bp to access the data and (cp->*fp)() to call the function, note that the plugin doesn't need to know *anything* about a except its name, ie the "class a;" forward declaration is all it needs. So the plugin never has to be recompiled unless the type of the members or functions it is using change. Works for all types of class a with virtual, private, multiple bases etc.
Colomban, thanks for your pushing me to get my head around this :)
Cheers Lex
[1] in C++ you can of course fake passing two pieces of information as one, using classes with overloaded operators that make it look like a single pointer, this is left as an exercise for the reader.