Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking if a function has C-linkage at compile-time [unsolvable]

Is there any way to check if a given function is declared with C-linkage (that is, with extern "C") at compile-time?

I am developing a plugin system. Each plugin can supply factory functions to the plugin-loading code. However, this has to be done via name (and subsequent use of GetProcAddress or dlsym). This requires that the functions be declared with C-linkage so as to prevent name-mangling. It would be nice to be able to throw a compiler error if the referred-to function is declared with C++-linkage (as opposed to finding out at runtime when a function with that name does not exist).

Here's a simplified example of what I mean:

extern "C" void my_func()
{
}

void my_other_func()
{
}

// Replace this struct with one that actually works
template<typename T>
struct is_c_linkage
{
    static const bool value = true;
};

template<typename T>
void assertCLinkage(T *func)
{
    static_assert(is_c_linkage<T>::value, "Supplied function does not have C-linkage");
}

int main()
{
    assertCLinkage(my_func); // Should compile
    assertCLinkage(my_other_func); // Should NOT compile
}

Is there a possible implementation of is_c_linkage that would throw a compiler error for the second function, but not the first? I'm not sure that it's possible (though it may exist as a compiler extension, which I'd still like to know of). Thanks.

like image 357
scjohnno Avatar asked May 29 '10 19:05

scjohnno


1 Answers

I agree with Jonathan Leffler that this probably is not possible in a standard way. Maybe it would be possible somewhat, depending on the compiler and even version of the compiler, but you would have to experiment to determine possible approaches and accept the fact that the compiler's behavior was likely unintentional and might be "fixed" in later versions.

With g++ version 4.4.4 on Debian Squeeze, for example, you might be able to raise a compiler error for functions that are not stdcall with this approach:

void my_func() __attribute__((stdcall));
void my_func() { }

void my_other_func() { }

template <typename ret_, typename... args_>
struct stdcall_fun_t
{
    typedef ret_ (*type)(args_...) __attribute__((stdcall));
};

int main()
{
    stdcall_fun_t<void>::type pFn(&my_func),
        pFn2(&my_other_func);
}

g++ -std=c++0x fails to compile this code because:

SO2936360.cpp:17: error: invalid conversion from ‘void ()()’ to ‘void ()()’

Line 17 is the declaration of pFn2. If I get rid of this declaration, then compilation succeeds.

Unfortunately, this technique does not work with cdecl.

like image 60
Daniel Trebbien Avatar answered Nov 16 '22 21:11

Daniel Trebbien