I have the following template
template<typename T> void f(T t) { }
And I want to pass the address of a specific specialization of it to a C function
g(&f<int>);
But as I want to be portable, I want the calling convention of "f" to match the one of C. So I experimented how language linkage affects calling convention and found
The language linkage section of the C++ spec says
In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names with external linkage, and variable names with external linkage declared within the linkage-specification.
So to prevent disabling of mangling, which is needed for templates to distinguish different specializations from each other in the object file, I went as follows
extern "C" {
/* function name will not be affected */
template<typename T> static void f(T t) { }
}
But it gives me a compiler error saying that templates cannot have C language linkage, which I take it to mean that it complains about the function template's function type. And in fact, I found the spec to say
A template, a template explicit specialization (14.7.3), and a class template partial specialization shall not have C linkage
Now it's obvious to me that we don't want to change the name's linkage, because we rely on mangling to work. But what is the reason for forbidding to change the type's linkage? It seems to restrict us to having to use C++ calling convention; does someone know the reason, and whether there is an easy work around to achieve my initial goal?
I changed the way I try to give linkage to only the type now, as follows
extern "C" typedef void ftype(int);
template<typename T>
ftype f;
And this works fine. Sadly, I don't see a way to define f
when using this technique. But anyway, no compiler I tried diagnoses this (tried EDG/comeau, GCC and clang), even though this looks like exactly the same situation as before: The name should have no C language linkage, but only the type has.
Can anyone explain this?
There are three major calling conventions that are used with the C language on 32-bit x86 processors: STDCALL, CDECL, and FASTCALL. In addition, there is another calling convention typically used with C++: THISCALL.
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.
A function template starts with the keyword template followed by template parameter(s) inside <> which is followed by the function definition. In the above code, T is a template argument that accepts different data types ( int , float , etc.), and typename is a keyword.
__cdecl is the default calling convention for C and C++ programs. Because the stack is cleaned up by the caller, it can do vararg functions. The __cdecl calling convention creates larger executables than __stdcall, because it requires each function call to include stack cleanup code.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With