Search code examples
c++macostemplatesundefined-referencegcc4

C-callback to function template: explicitly instantiate template


Premise

I’m using a C library (from C++) which provides the following interface:

void register_callback(void* f, void* data);
void invoke_callback();

Problem

Now, I need to register a function template as a callback and this is causing me problems. Consider the following code:

template <typename T> void my_callback(void* data) { … }

int main() {
    int ft = 42;
    register_callback(reinterpret_cast<void*>(&my_callback<int>), &ft);
    invoke_callback();
}

This gives me the following linker error (using g++ (GCC) 4.5.1 on OS X but works on most other combinations of compiler version / platform):

Undefined symbols for architecture x86_64:

"void my_callback<int>(void*)", referenced from:  
  _main in ccYLXc5w.o

which I find understandable.

First “solution”

This is easily fixed by explicitly instantiating the template:

template void my_callback<int>(void* data);

Unfortunately, this isn’t applicable in my real code since the callback is registered inside a function template, and I don’t know for which set of template arguments this function will be called, so I can’t provide explicit instantiations for all of them (I’m programming a library). So my real code looks a bit like this:

template <typename T>
void do_register_callback(T& value) {
    register_callback(reinterpret_cast<void*>(my_callback<T>), &value);
    // Other things …
}

int main() {
    int ft = 42;
    do_register_callback(ft);
    invoke_callback();
}

Second “solution”

A function template is implicitly instantiated by calling the function. So let’s do that, but make sure that the call isn’t actually performed (the function has got side-effects):

template <typename T>
void do_register_callback(T& value) {
    if (false) { my_callback<T>(0); }
    register_callback(reinterpret_cast<void*>(my_callback<T>), &value);
}

This seems to work, even with optimisations enabled (so that the dead branch is removed by the compiler). But I’m not sure if this won’t some day break down. I also find this a very ugly solution that requires a length explanatory comment lest some future maintainer remove this obviously unnecessary code.

Question

How do I instantiate a template for which I don’t know the template arguments? This question is obviously nonsense: I can’t. – But is there a sneaky way around this?

Barring that, is my workaround guaranteed to succeed?

Bonus question

The code (specifically, the fact that I cast a function pointer to void*) also produces the following warning:

ISO C++ forbids casting between pointer-to-function and pointer-to-object

when compiling with -pedantic. Can I somehow get rid of the warning, without writing a strongly-typed C wrapper for the library (which is impossible in my situation)?

Running code on ideone (with an added cast to make it compile)


Solution

  • POSIX recommends the following way to cast between function pointer types and object pointer types (which is undefined in C99):

    typedef void function_type(void*);
    function_type *p_to_function = &my_callback<T>;
    void* p_to_data = *(void**)&p_to_function;
    
    // Undefined:
    // void* p_to_data = (void*)p_to_function;
    

    Notice that in C++-land, this would perform a reinterpret_cast<void**>(&p_to_function) from function_type**. This is not undefined but instead implementation-defined, unlike reinterpret_cast<void*>(p_to_function). So it's probably your best bet to write C++-conformant code that relies on the implementation.