C-callback to function template: explicitly instantiate template

依然范特西╮ 提交于 2019-12-17 19:15:45

问题


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)


回答1:


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.




回答2:


Apparently, the real problem was the missing static_cast in my original code:

register_callback(reinterpret_cast<void*>(&my_callback<int>), &ft);

This compiles fine, but triggers the liker error when using GCC 4.5. It doesn’t even compile when using GCC 4.2, instead giving the following compile error:

insufficient contextual information to determine type

Once this “contextual information” is provided, the code compiles and links:

register_callback(reinterpret_cast<void*>(
    static_cast<void(*)(void*)>(my_callback<int>)), &value);

I’ve got no idea whether the cast is actually required and (if so) why GCC 4.5 allows me to leave it off, and then fails to instantiate the template. But at least I got the code to compile without resorting to hacks.




回答3:


This should work:

template <typename T>
void do_register_callback(T& value) {
   void (*callback)(void*) = my_callback<T>;
   register_callback(reinterpret_cast<void*>(callback), &value);
}

The first line forces the compiler to instantiate that function to generate the address - which you can then happily pass.

EDIT: Let me throw another option in to this mix. Make my_callback a static member of a class template - something like the following:

template <typename T>
struct foo
{
static void my_callback(void* data) {
    T& x = *static_cast<T*>(data);
    std:: cout << "Call[T] with " << x << std::endl;
}
};

Now, in your register guy, you don't even need the "cast".

template <typename T>
void do_register_callback(T& value) {
   register_callback(reinterpret_cast<void*>(&foo<int>::my_callback), &value);
}

It appears that the rule for instantiating class templates is different to function templates - i.e. to take the address of a member of a class, the type is instantiated.



来源:https://stackoverflow.com/questions/6734492/c-callback-to-function-template-explicitly-instantiate-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!