11

Clearly casting between function pointers and object pointers is undefined behaviour in the general sense, but POSIX (see: dlsym) and WinAPI (see: GetProcAddress) require this.

Given this, and given the fact that such code is targeting a platform-specific API anyway, its portability to platforms where function pointers and object pointers aren't compatible is really irrelevant.

But -Wpedantic warns about it anyway, and #pragma GCC diagnostic ignored "-Wpedantic" has no effect:

warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object [enabled by default]

I want to keep -Wpedantic enabled, since it does give good warnings, but I don't want to have real warnings and errors lost amidst a sea of irrelevant warnings about function pointer to object pointer casts.

Is there a way to accomplish this?

Running GCC 4.8.0 on Windows (MinGW):

gcc (rubenvb-4.8.0) 4.8.0

CODE SAMPLE

#include <windows.h>
#include <iostream>


int main (void) {

    std::cout << *reinterpret_cast<int *>(GetProcAddress(LoadLibraryA("test.dll"),"five")) << std::endl;

}

Emits (with -Wpedantic):

warning_demo.cpp: In function 'int main()':
warning_demo.cpp:7:87: warning: ISO C++ forbids casting between pointer-to-funct
ion and pointer-to-object [enabled by default]
  std::cout << *reinterpret_cast<int *>(GetProcAddress(LoadLibraryA("test.dll"),
"five")) << std::endl;

       ^
  • I've never had a problem with casting the result of `GetProcAddress`. – chris Apr 15 '13 at 23:29
  • 2
    Do you have any *code* that generated that warning that you can post? – WhozCraig Apr 15 '13 at 23:32
  • `test.dll` contains `int five=5`. When compiled and run the program simply prints "5". This is a valid use case and is specifically allowed by [_GetProcAddress_: "_Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL)._"](http://msdn.microsoft.com/en-ca/library/windows/desktop/ms683212(v=vs.85).aspx) – Robert Allan Hennigan Leahy Apr 15 '13 at 23:41
  • Possibly relevant: GetProcAddress is intended to work with C symbols, not C++. Could you possibly having some sort of name mangling issues? i.e. what is the exact symbol exported from your DLL? (should be readily identifiable using Dependency Walker). Normally, if it was a name mangling issue, I'd expect GetProcAddress to fail and return NULL, but just a hunch/suggestion. – Nathan Ernst Apr 16 '13 at 00:41
  • 1
    This works fine at runtime. Name mangling issues are avoided by using `extern "C"`. It's only the cast that the compiler has issue with, and that's at compile time. – Robert Allan Hennigan Leahy Apr 16 '13 at 01:24

3 Answers3

4

I think you could use g++'s system_header directive here:

wrap_GetProcAddress.h:

#ifndef wrap_GetProcAddress_included
#define wrap_GetProcAddress_included

#pragma GCC system_header

template <typename Result>
Result GetProcAddressAs( [normal parameters] )
{
    return reinterpret_cast<Result>(GetProcAddressAs( [normal parameters] ));
}

#endif
Mark B
  • 95,107
  • 10
  • 109
  • 188
4

This works fine.

template <typename RESULT, typename ...ARGS>
void * make_void_ptr(RESULT (*p)(ARGS...)) {
    static_assert(sizeof(void *) == sizeof(void (*)(void)),
                  "object pointer and function pointer sizes must equal");
    void *q = &p;
    return *static_cast<void **>(q);
}
jxh
  • 69,070
  • 8
  • 110
  • 193
  • This saved me! If the pointer is already available as a variable and you don't care about the static assertion this can also be a sleek one-liner: `*reinterpret_cast(&your_ptr)` – Salvage Oct 01 '22 at 20:51
2

There's always the memcpy trick you can use:

int (*f)() = 0;
int *o;
memcpy(&o, &f, sizeof(int*));

You can see it on ideone: m is generating warnings, while g is OK.

As to other course of action you might want to take: One obvious possibility would be to "fix" the header defining dlsym to actually return a function pointer (like void (*)()). Good luck with that.

jpalecek
  • 47,058
  • 7
  • 102
  • 144
  • You should at least static_assert that a function pointer is big enough to hold a `void*`. – Mark B Apr 15 '13 at 23:33
  • Well the problem with `GetProcAddress` then becomes what if you want to get the address of something that isn't a function in the library you loaded (see code example I added to the question)? – Robert Allan Hennigan Leahy Apr 15 '13 at 23:41
  • 1
    @MarkB: Given that the OP wants to use POSIX, which actually [requires function-pointers to have the same representation as object-pointers](http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_12), I would consider it a superfluous clutter (similar to handling `sizeof(char)` different than 1). But if you feel you need it, you can add it (in which case I'd rather put it to an autoconf script or somesuch). – jpalecek Apr 15 '13 at 23:45