I’ve searched Stack Overflow for an answer, but I get nothing specific to this problem: only general cases about use of various types of cast operators.
So, the case in point is when retrieving a function address with the Windows GetProcAddress()
API call, which returns a function pointer of type FARPROC
, with: typedef INT_PTR (__stdcall *FARPROC)();
.
The trouble is, the actual function sought rarely (if ever) has this actual signature, as shown in the MRCE code, below. In this code, I have shown a variety of different attempts to convert the returned value to a function pointer of the appropriate type, with all but the fourth method commented out:
#include <Windows.h>
#include <iostream>
typedef DPI_AWARENESS_CONTEXT(__stdcall* TYPE_SetDPI)(DPI_AWARENESS_CONTEXT); // Function pointer typedef
static DPI_AWARENESS_CONTEXT __stdcall STUB_SetDpi(DPI_AWARENESS_CONTEXT) { return nullptr; } // Dummy 'stub' function
static DPI_AWARENESS_CONTEXT(__stdcall* REAL_SetDpi)(DPI_AWARENESS_CONTEXT) = STUB_SetDpi; // Func ptr to be assigned
using std::cout; using std::endl;
int main()
{
HINSTANCE hDll = LoadLibrary("User32.dll");
if (!hDll) {
cout << "User32.dll failed to load!\n" << endl;
return 1;
}
cout << "User32.dll loaded succesfully..." << endl;
// (1) Simple assignment doesn't work ...
// REAL_SetDpi = GetProcAddress(hDll, "SetThreadDpiAwarenessContext");
// (2) Using 'C'-style cast does work, but it is flagged as 'evil' ...
// REAL_SetDpi = (TYPE_SetDPI)GetProcAddress(hDll, "SetThreadDpiAwarenessContext");
// (3) Using reinterpret_cast: seems OK with clang-cl but MSVC doesn't like it ...
// REAL_SetDpi = reinterpret_cast<TYPE_SetDPI>(GetProcAddress(hDll,
// (4) Using a temporary plain "void *": OK with MSVC but clang-cl complains ...
void* tempPtr = GetProcAddress(hDll, "SetThreadDpiAwarenessContext");
REAL_SetDpi = reinterpret_cast<TYPE_SetDPI>(tempPtr);
// (5) Using a union (cheating? - but neither clang-cl nor MSVC give any warning!) ...
// union {
// intptr_t(__stdcall* gotProc)(void);
// TYPE_SetDPI usrProc; // This has the 'signature' for the actual function.
// } TwoProcs;
// TwoProcs.gotProc = GetProcAddress(hDll, "SetThreadDpiAwarenessContext");
// REAL_SetDpi = TwoProcs.usrProc;
if (REAL_SetDpi == nullptr) cout << "SetThreadDpiAwarenessContext function not found!" << endl;
else cout << "SetThreadDpiAwarenessContext function loaded OK!" << endl;
FreeLibrary(hDll);
return 0;
}
The various error/warning messages given by the clang-cl
and native MSVC
compiler, for each of the 5 options are as follows:
// (1) Simple assignment doesn't work ...
REAL_SetDpi = GetProcAddress(hDll, "SetThreadDpiAwarenessContext");
clang-cl -> error : assigning to 'DPI_AWARENESS_CONTEXT (*)(DPI_AWARENESS_CONTEXT) __attribute__((stdcall))'
(aka 'DPI_AWARENESS_CONTEXT__ *(*)(DPI_AWARENESS_CONTEXT__ *)') from incompatible type 'FARPROC'
(aka 'long long (*)()'): different number of parameters (1 vs 0)
Visual-C -> error C2440: '=': cannot convert from 'FARPROC' to
'DPI_AWARENESS_CONTEXT (__cdecl *)(DPI_AWARENESS_CONTEXT)'
message : This conversion requires a reinterpret_cast, a C-style cast or function-style cast
This error is (of course) expected, but the one confusing thing to me is why MSVC
shows my function as __cdecl
when I have explicitly declared it __stdcall
?
// (2) Using 'C'-style cast does work, but it is flagged as dangerous ...
REAL_SetDpi = (TYPE_SetDPI)GetProcAddress(hDll, "SetThreadDpiAwarenessContext");
clang-cl -> warning : use of old-style cast [-Wold-style-cast]
Visual-C -> warning C4191: 'type cast': unsafe conversion from 'FARPROC' to 'TYPE_SetDPI'
warning C4191: Calling this function through the result pointer may cause your program to fail
Generally, I endeavour to completely avoid old, ‘C’-style casts in my code! Where I am forced to do cast between ‘unrelated’ objects, I use explicit reinterpret_cast
operators, as these are far easier to track down in code if problems arise. So, for case 3:
// (3) Using reinterpret_cast: seems OK with clang-cl but MSVC doesn't like it ...
REAL_SetDpi = reinterpret_cast<TYPE_SetDPI>(GetProcAddress(hDll, "SetThreadDpiAwarenessContext"));
clang-cl -> No error, no warning!
Visual-C -> warning C4191: 'reinterpret_cast': unsafe conversion from 'FARPROC' to 'TYPE_SetDPI'
Calling this function through the result pointer may cause your program to fail
Here, the MSVC
warning is pretty much the same as for the C-style cast. Maybe I could live with this, but case 4 makes things more interesting:
// (4) Using a temporary plain "void *": OK with MSVC but clang-cl complains ...
void* tempPtr = GetProcAddress(hDll, "SetThreadDpiAwarenessContext");
REAL_SetDpi = reinterpret_cast<TYPE_SetDPI>(tempPtr);
clang-cl -> warning : implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension
[-Wmicrosoft-cast]
warning : cast between pointer-to-function and pointer-to-object is incompatible with C++98
[-Wc++98-compat-pedantic]
Here, MSVC
gives no warning – but I feel I’m simply ‘fooling’ the compiler! I can’t see how this can have any different overall effect than the code in case 3.
// (5) Using a union (cheating? - but neither clang-cl nor MSVC give any warning!) ...
union {
intptr_t(__stdcall* gotProc)(void);
TYPE_SetDPI usrProc; // This has the 'signature' for the actual function.
} TwoProcs;
TwoProcs.gotProc = GetProcAddress(hDll, "SetThreadDpiAwarenessContext");
REAL_SetDpi = TwoProcs.usrProc;
I did post this as an answer (now retracted), to which @formerlyknownas_463035818 pointed out that this is officially Undefined Behaviour and/or disallowed in C++
(the link given by the aforementioned commentator).
Which option do I currently use?
Well, as my software is specifically Windows-oriented, I use the last (option 4) for two reasons: (1) the clang-cl
warning is the ‘least scary’; and (2) I like to think that MSVC
is probably the best ‘mediator’ for compiling/building Windows apps.
EDIT: Since first posting this question, and having 'reviewed' the various comments and suggestions made, I have now changed all instances of this type of cast (that is, from a function pointer loaded via
GetProcAddress
) in my code to using the following conversion 'function', defined in my global header file:template<typename T> T static inline FprocPointer(intptr_t(__stdcall* inProc)(void)) { __pragma(warning(suppress:4191)) // Note: no semicolon after this expression! return reinterpret_cast<T>(inProc); }
This allows for easy/rapid location of any such casts, should I need (or wish) to change the way they work in future.
Why does it matter?
Maybe it doesn’t! However, elsewhere in my code, I have come across an unexpected crash, when using function pointers loaded via GetProcAddress()
- not any standard WinAPI
calls, but functions from my own DLLs, loaded as plug-in modules. The code snippet below shows a potential case-in-point:
// --------------------------------------------------------------------------------------------------------------------
// These two routines are the 'interceptors' for plug-in commands; they check active plug-ins for handlers or updaters:
static int plin; //! NOTA BENE: We use this in the two functions below, as the use of a local 'plin' loop index
// is prone to induce stack corruption (?), especially in MSVC 2017 (MFC 14) builds for x86.
void BasicApp::OnUpdatePICmd(uint32_t nID, void *pUI)
{
//! for (int plin = 0; plin < Plugin_Number; ++plin) { // Can cause problems - vide supra
for (plin = 0; plin < Plugin_Number; ++plin) {
BOOL mEbl = FALSE; int mChk = -1;
if ((Plugin_UDCfnc[plin] != nullptr) && Plugin_UDCfnc[plin](nID, &mEbl, &mChk)) {
CommandEnable(pUI, mEbl ? true : false);
if (mChk >= 0) CmdUISetCheck(pUI, mChk);
return;
}
}
CommandEnable(pUI, false);
return;
}
void BasicApp::OnPluginCmd(uint32_t nID)
{
//! for (int plin = 0; plin < Plugin_Number; ++plin) { // Can cause problems - vide supra
for (plin = 0; plin < Plugin_Number; ++plin) {
piHandleFnc Handler = nullptr; void *pParam = nullptr;
if ((Plugin_CHCfnc[plin] != nullptr) && Plugin_CHCfnc[plin](nID, &Handler, &pParam) && (Handler != nullptr)) {
Handler(pParam);
return;
}
}
return;
}
Note that, Plugin_UDCfnc
and Plugin_CHCfnc
are arrays of function pointers, loaded as described above.
And, finally, what was my question, again?
Two-fold:
std::bind()
?Any help, suggestions or recommendations will be greatly appreciated.
EDIT: I use the native MSVC
compiler for my “Release” builds (with /Wall
), and a few specific warnings explicitly disabled (locally) in code. From time to time, I run my entire code base through the clang-cl
compiler, to look for other warnings of possible dodgy code (very useful, actually).
I think C/C++ are lacking a generic function pointer type, like void*
as a generic object pointer type.
Generally, converting from one function pointer into another is supported, provided that you don't call the wrong function pointer type. See [expr.reinterpret.cast]/6:
A function pointer can be explicitly converted to a function pointer of a different type.
A warning that is issued when casting one function pointer type to another is generally useful. Having such casting might cause a call to a function with the wrong signature. Such a problem might affect only a certain CPU architecture, or be noticed only with certain OS builds, so it might not be apparent after initial testing. The Standard just says unspecified, see [expr.reinterpret.cast]/6:
Except that converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are function types) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.
Whether void*
can be converted to function pointer type and whether it even has enough bits is implementation specific. True for Windows, though. I wouldn't endorse Windows-specific habits for a generic problem. See
[expr.reinterpret.cast]/8:
Converting a function pointer to an object pointer type or vice versa is conditionally-supported.
Type punning with unions raises an issue with strict aliasing (What is the strict aliasing rule?), so not a good way to outsmart a compiler.
So, live with local warning suppression near your GetProcAddress
calls, or in a GetProcAddess
wrapper. Use reinterpret_cast
.
If you're going to use a helper function that casts one function type into another without a warning, be sure to use it only in GetProcAddress
-like scenario, when you use some generic signature to temporary store function pointer, but that signature is not an actual signature -- not to call a function pointer by a not intended type.
Sorry.