I have the following template
template void f(T t) { }
And I want to pass the address of a specific specialization of it
I don't know the reason for the restriction, but can't you use a wrapper extern "C"
function that calls the specific template instantiation you care about?
...whether there is an easy work around to achieve my initial goal?
you can approach it from a few angles, depending on how you want to configure and declare them.
four approaches follow, one namespace demonstrates each. 'Type' is probably the simplest for your usage.
#include <stdio.h> // favored to reduce exports in later demonstration
#define FUNC_NAME __PRETTY_FUNCTION__
// or __func__ or __FUNCTION__ or...
extern "C" {
/* C prototype */
typedef void ftype(int a);
/* the c function all our functions are piped into */
void call(ftype* a);
/* helper which serves as the implementation for our functions */
void print(int a, const char* const func);
/* C definitions (used in namespace examples below) */
static void static_float(int a) {
print(a, FUNC_NAME);
}
static void static_double(int a) {
print(a, FUNC_NAME);
}
void extern_float(int a);
void extern_float(int a) {
print(a, FUNC_NAME);
}
void extern_double(int a);
void extern_double(int a) {
print(a, FUNC_NAME);
}
static void static_function_float(int a) {
print(a, FUNC_NAME);
}
static void static_function_double(int a) {
print(a, FUNC_NAME);
}
} /* << extern C */
namespace Extern {
/**
interface demonstrates C functions as template arguments
*/
template<ftype Fn>
struct t_func {
static ftype* Function() {
return Fn;
}
};
template<typename T> struct bind;
template<> struct bind<float> {
typedef t_func<extern_float> F;
};
template<> struct bind<double> {
typedef t_func<extern_double> F;
};
template<typename T>
void Call(T a) {
(void) a;
call(bind<T>::F::Function());
}
} /* << Extern */
namespace Static {
/**
interface demonstrates template types wrapping static C functions
*/
template<typename T> struct bind;
template<> struct bind<float> {
static ftype* F() {
return static_float;
}
};
template<> struct bind<double> {
static ftype* F() {
return static_double;
}
};
template<typename T>
void Call(T a) {
(void) a;
call(bind<T>::F());
}
} /* << Static */
namespace Function {
/**
interface demonstrates template functions wrapping static C functions
*/
template<typename T> ftype* bind();
template<> ftype* bind<float> () {
return static_function_float;
}
template<> ftype* bind<double> () {
return static_function_double;
}
template<typename T>
void Call(T a) {
(void) a;
call(bind<T> ());
}
} /* << Function */
namespace Type {
/**
interface demonstrates template types implementing static functions.
although gcc4.2 and clang both compile it, i'm uncertain that this is conforming.
*/
template<typename T> struct bind {
static void F(int a);
};
template<> void bind<float>::F(int a) {
print(a, FUNC_NAME);
}
template<> void bind<double>::F(int a) {
print(a, FUNC_NAME);
}
template<typename T>
void Call(T a) {
(void) a;
call(bind<T>::F);
}
} /* << Type */
int main(int argc, const char * argv[]) {
(void) argc;
(void) argv;
const float f(1.0f);
const double d(5.0);
Extern::Call(f);
Extern::Call(d);
Static::Call(f);
Static::Call(d);
Function::Call(f);
Function::Call(d);
Type::Call(f);
Type::Call(d);
return 0;
}
void call(ftype* a) {
a(11);
}
void print(int a, const char* const func) {
printf("%i: %s\n", a, func);
}
outputs:
11: void extern_float(int)
11: void extern_double(int)
11: void static_float(int)
11: void static_double(int)
11: void static_function_float(int)
11: void static_function_double(int)
11: static void Type::bind<T>::F(int) [with T = float]
11: static void Type::bind<T>::F(int) [with T = double]
producing:
nm unstripped:
0000000100000daf s stub helpers
0000000100001048 D _NXArgc
0000000100001050 D _NXArgv
0000000100000bde T __ZN4Type4bindIdE1FEi
0000000100000bc0 T __ZN4Type4bindIfE1FEi
0000000100000d98 s __ZZ12extern_floatE19__PRETTY_FUNCTION__
0000000100000c98 s __ZZ12static_floatE19__PRETTY_FUNCTION__
0000000100000d80 s __ZZ13extern_doubleE19__PRETTY_FUNCTION__
0000000100000cb0 s __ZZ13static_doubleE19__PRETTY_FUNCTION__
0000000100000d60 s __ZZ21static_function_floatE19__PRETTY_FUNCTION__
0000000100000d38 s __ZZ22static_function_doubleE19__PRETTY_FUNCTION__
0000000100000cc8 s __ZZN4Type4bindIdE1FEiE19__PRETTY_FUNCTION__
0000000100000d00 s __ZZN4Type4bindIfE1FEiE19__PRETTY_FUNCTION__
0000000100001060 D ___progname
0000000100000000 A __mh_execute_header
0000000100001058 D _environ
U _exit
0000000100000c00 T _extern_double
0000000100000b20 T _extern_float
0000000100000c20 T _main
U _printf
0000000100000b60 t _static_double
0000000100000b40 t _static_float
0000000100000ba0 t _static_function_double
0000000100000b80 t _static_function_float
U dyld_stub_binder
0000000100000ae0 T start
nm stripped:
0000000100000000 A __mh_execute_header
U _exit
U _printf
U dyld_stub_binder
sorry, i'm not poring over standards tonight -- hope that helps. good luck!
What does the C header look like? Somewhere, the C source must enumerate the callback types allowed. You should take that opportunity to have a series of macros that generate prototypes to individual stub functions, with a corresponding sequence of macros in the C++ source generating extern "C"
stubs.
As to the second question: Yes, that works, but the typedef
is not inside a template. I attempted to put such a typedef inside a class, but it turns out that even class templates are not allowed inside the extern "C"
. So you can have a function template, but no parameters of dependent type.
Merely defining that function is easy:
extern "C" typedef void ftype(int);
template<typename T>
static ftype f; // <- added "static" here
template< typename T >
void f(int q) {}
Aha, variadic functions!
extern "C" typedef void ftype( int, ... );
template<typename T>
static ftype f;
template< typename T >
static void f( int z, ... ) {
va_list va;
va_start( va, z );
T v = va_arg( va, T );
va_end( va );
std::cout << v;
}
You don't really need type deduction since it's just a callback, so you can pass this & f<int>
to the C code, all callbacks having the same type, and it can make the type determination at runtime and pass whatever it wants through the varargs.