Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling function template specialization using C calling conventions

Tags:

c++

templates

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 of the function type affects calling convention to use
  • The language linkage of the function name affects mangling

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?

like image 902
Johannes Schaub - litb Avatar asked Apr 08 '11 01:04

Johannes Schaub - litb


People also ask

Which are function calling conventions in C?

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.

What is function template specialization?

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.

How do you call a function template?

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.

What is the default calling convention for a compiler in C?

__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.


1 Answers

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.

like image 180
Potatoswatter Avatar answered Oct 05 '22 23:10

Potatoswatter