3 ms·
When I say in principle, I don't mean according to a strict interpretation of the standard in a vacuum. I mean that even if I were to extend to you a great deal
by Kranar 5y ago
When I say in principle, I don't mean according to a strict interpretation of the standard in a vacuum. I mean that even if I were to extend to you a great deal of liberties and operate at the level of what you're attempting to accomplish, your approach is fundamentally invalid and results in buggy code that will break and is entirely unneccessary.
The way you accomplish dynamically loading member functions is by exporting a plain C function using extern "C" that takes an opaque handle to the object you wish to operate on, and whose implementation wraps the member function whose operation you wish to expose.
Pointers to member functions are fundamentally not compatible with void* and hence may not reliably be returned using dlsym. Only once the member function is bound to an object (using the .* operator, ie. object.*member) is the resulting pointer compatible with a void* (in C++11 it's implementation defined). Until then, they not only have different sizes, their size may even be different within different translation units of the same application!
In practice you are right that DLLs and shared objects communicate through C++ APIs all the time, and the reliable way that they do so is by using extern "C". The article you linked to is exactly the kind of pain, undefined behavior, and buggy problems you will encounter when you try to use any other mechanism than the plain and straight forward mechanism that exists precisely for the purpose of facilitating this kind of communication.
The reason my point is worth making, as opposed to just being a pedantic technicality, is because this approach is precisely what allows compilers to make various optimizations that continue to work safely even in situations where objects, functions, and member functions are used across dynamic boundaries. If you don't follow this approach, then the compiler will make certain optimizations that will result in disastrous behavior.
If you don't want to take my word for it, hopefully you'll take the advice of the ISO CPP [1]:
"do not attempt to “cast” a pointer-to-member-function into a pointer-to-function; the result is undefined and probably disastrous. E.g., a pointer-to-member-function is not required to contain the machine address of the appropriate function."
[1] https://isocpp.org/wiki/faq/pointers-to-members#addr-of-memfn https://isocpp.org/wiki/faq/pointers-to-members#addr-of-memf...
- tcbawo 5y agoI have seen implementations of type erasure/delegates that store an instance pointer and pointer-to-member-function as an instance and pointer-to-member-function of a dummy type with the same calling convention. It's interesting how many different member function pointer sizes Windows has. Undefined behavior and compiler dependent, but consistent.
- pjmlp 5y agoOr COM like APIs.