7

I may be a little late to know this standard statement, after seeing the SO answer:

[C++11: 7.5/1]

Two function types with different language linkages are distinct types even if they are otherwise identical.

which means, given:

void f1();
extern "C" void f2();

decltype(f1) is not the same as decltype(f2)

A reason that I wasn't aware of it until now is that the major compilers (e.g. g++, clang, vc++...) don't respect this rule. See LIVE DEMO.

But I believe most people (include me) will be happier with the current nonconformant behavior. If a compiler follows the standard, many codes that bridge C and C++ would be broken.

Consider this example:

A library provides C API:

#ifdef __cplusplus
extern "C" {
#endif
void registerCallbackInC(void(*callback)(void*));
#ifdef __cplusplus
}
#endif

To use the library in C++:

void f1(void*)
{
    ...
}

extern "C" void f2(void*)
{
    ...
}

registerCallbackInC(f1); // invalid, f1 has C++ linkage
registerCallbackInC(f2); // OK

To use registerCallbackInC, the callback must have C-linkage as well, however, we can't use extern "C" with template:

extern "C"
{
    template<class T>
    void f(void*); // invalid
}

template<class T>
extern "C" void f2(void*); // invalid

template<class T>
struct F
{
    extern "C" static void f(void*); // invalid
};

This makes it impossible to synthesize a C callback using template, should I consider the requirement a standard defect?

Community
  • 1
  • 1
Jamboree
  • 5,139
  • 2
  • 16
  • 36
  • :S works just fine: http://ideone.com/8wu6Mf Are you sure it gives an error for the linkage of the callback to a C++ function? (not template function).. I can't replicate the error you get.. – Brandon Oct 03 '14 at 07:08
  • Regarding GCC, it's a [known bug since 2001](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316) – Anton Savin Oct 03 '14 at 07:10
  • 1
    @Brandon, I know it works just fine, because the compilers aren't standard conformant in this regard. – Jamboree Oct 03 '14 at 07:10
  • I don't know then.. http://ideone.com/2iMxSp works in MSVC 2008, 2010, 2012, 2013.. I have all of them installed. I also tried it in g++ 4.7.2, 4.8.1, 4.9.1.. works in all of them. I did not try clang. But I can't see ALL the compilers somehow getting this wrong.. There has to be something mis-interpreted here.. Can't `extern "C"` the template but you can `extern "C"` it with a parameter. – Brandon Oct 03 '14 at 07:31
  • 1
    @Brandon No, that *is* all the compilers getting it wrong (clang too). The OP is interpreting the standard correctly. One of the compilers that does attempt to follow the rules for language linkage is Sun's (Oracle's), you could give that a try if you're interested. –  Oct 03 '14 at 07:32
  • Well damn.. That sucks. Have to install another compiler :l – Brandon Oct 03 '14 at 07:33

1 Answers1

11

The only restriction on templates is that the name cannot have C linkage, there is no restriction on its type, so you can use a typedef for a C linkage function in the first declaration of the template.

extern "C" typedef void cfunc();
template <typename T> cfunc yourfunc;
template <typename T> void yourfunc() { }
  • Brilliant! didn't know that we can declare a function as in the second line. For completeness, I made this [LIVE DEMO](http://coliru.stacked-crooked.com/a/3e181415b5d3fb80). – Jamboree Oct 03 '14 at 07:58
  • 3
    @Jamboree I actually don't know for sure whether it is supposed to work for static member functions, like you try in your example. Inside a class definition, C linkage is ignored, but I am not sure whether that applies to function types appearing inside the class definition, or functions being declared in the class. Depending on the answer, your `void X::f` might still have a type with C++ linkage. –  Oct 03 '14 at 08:01
  • Here `cfunc` and `yourfunc` take no arguments. How can this work if we want the function to take templated parameters? – mostafa.elhoushi Nov 11 '21 at 16:32