auto TheFunctionX = &MyClass::Function;
Will yield a variable of a hidden structure type with two fields. These fields are later used to compute the real address of the function in memory. The structure variable might already contain the address if the function is not virtual but knowing that just from the structure requires knowing how the fields in the structure work. Unfortunately the details of how compilers implement this structure varies wildly, but for Gcc at least it is stable in that for a given ABI(Binary Interface), they will not change the details.
Assuming you can rely on the ABI you can then use your own code to interpret the real address of virtual functions and non-virtual member functions, even in interfaces. To do so you need the structure, which you can compute once knowing the type of the class, or just compute it each time you need it, and you also need the instance of the class for which you want the address. The reason you need the instance to get a virtual functions address is because the instance has the vTable. Technically for any given class instance(which is not using multiple inheritance), you can compute the address of any member function and save it.
The point is to understand that because you can compute the address, you can then go on to make other structures of your own to implement something which functions exactly like the events mechanism of Delphi.
This small class here shows an example of such a member function structure as used in the standard Itanium Abi for Gcc. Gcc supports different Abi's so it is not always the same. That requires more understanding of Abi in general between different Abi.
struct TGccMemberFunctionDetails {
union {
intptr_t non_virtual_address; // always even, the actual address of a normal non-virtual instance method
intptr_t virtual_offset; // always odd, the offset into the vtable of the adjusted this pointer
};
intptr_t delta;// The this pointer adjustment. Used for interfaces and multiple inheritance cases
// Given an instance pointer aka "this", compute 2 things.
// 1. The adjusted this pointer.
// 2. The real address of the proc
TComputedPmf Compute(void* cthis) {
TComputedPmf result;
result.inst = cthis + delta;
if (non_virtual_address & 1) result.proc = (void*)(* (intptr_t**)( *(intptr_t**)result.inst + (virtual_offset+1)/2) + sizeof(void*));
else result.proc = (void*)non_virtual_address;
return result;
}
};
The Compute method returns another structure containing the address of the function and the corrected instance pointer(which for normal classes is the same as the input cthis).
Once you have the address and the corrected this you can the save these variables and use them like Delphi events by making a template class to wrap them in. Unfortunately it is not that type safe and you will have to watch your calling conventions but the general idea is to pass the corrected this argument first and then the rest of the parameters after the this. The call will go to a thunk if it was an interface or multiple inheritance method requiring a thunk in either case.
If you are still reading then this is probably new to you, and you probably want Delphi or C# style events. So all you need to take from this brief introduction is faith in the concept and that it does work and that the execution speed is very fast to cal the function(the same as Delphi).
The problem then is how to make the event. It is very simple... This simple C++11 class here can serve you as a basis for all your events. If you need an event that returns void just define another class but omit the TResult type parameter and return nothing from the Call procedure. You can also overload the call operator to make the code look like its calling a function!
template<class TResult, typename... TArgs>
class TFunctionOfObject {
public:
typedef TResult (*TProc)(void*, TArgs...);
TProc proc;
void* inst;
TFunctionOfObject () { }
TFunctionOfObject (const TComputedPmf& pmf) {
proc = pmf.proc;
inst = pmf.inst;
}
inline __attribute((force_inline))__ TResult Call(TArgs... args) {
return proc(inst, args...);
}
};
// Declare an event proc that returns bool and takes one parameter which is an integer
using TMyEvent = TFunctionOfObject<bool,int>;
I leave the rest up to you as to how to derive the TComputedPMF structure that you can pass to the constructor. Or maybe I will edit this some more later.
Hint! I like to use a macro called _PMFX and the decltype variable to compute this on the fly, so I can say...
Font.OnChanged = _PMFX(this, OnFontChanged) ;
Then inside the macro I use the decltype of the this variable to get at the compilers structure for the member function, and then I use the Abi specific structure(like the Gcc example above), to get at the computed PMF and then it just goes into the constructor of the OnChanged field of Font in this example.
No comments:
Post a Comment