9

I want to add a public typedef to a template for a pointer to a function-taking-one-argument that uses "C" language linkage.

I tried:

extern "C" {
    template <typename return_t_, typename arg1_t_>
    struct test
    {
        typedef return_t_ (*C_fun1_t)(arg1_t_);
    };
}

And:

template <typename return_t_, typename arg1_t_>
struct test
{
    extern "C" {
        typedef return_t_ (*C_fun1_t)(arg1_t_);
    }
};

And:

template <typename return_t_, typename arg1_t_>
struct test
{
    extern "C" typedef return_t_ (*C_fun1_t)(arg1_t_);
};

without success.

Is what I am trying to accomplish possible?

Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193
  • 1
    Isn't this what makes the whole 'extern "C" might use a different calling convention' one of those annoying things that you have to account for but which will almost certainly never happen? Function pointer types don't distinguish between the two, because `extern "C"` isn't part of the type system, it's a linkage specifier. This means calls through the pointer made *from C++* will work regardless of whether the referand of the pointer is `extern "C"` or not. It's calls made from C that might fail, but the type system doesn't check this for you. – Steve Jessop Feb 01 '11 at 18:36
  • 3
    @Steve: It *is* part of the type system; C++03 §7.5p1: "Two function types with different language linkages are distinct types even if they are otherwise identical." §5.2.2p1: "Calling a function through an expression whose function type has a language linkage that is different from the language linkage of the function type of the called function’s definition is undefined." – Fred Nurk Feb 01 '11 at 18:37
  • @Fred: oh, OK. Sorry, not sure where I picked that wrong information up, then. Also, if I declare an extern "C" function that itself takes a function pointer as a parameter, does it take a "C" function or a "C++" function? Because g++ happily lets me use either with `-pedantic`. – Steve Jessop Feb 01 '11 at 18:38
  • @Steve: Section 7.5, Linkage specifications, of the C++ Standard states "Two function types with different language linkages are distinct types even if they are otherwise identical." – Daniel Trebbien Feb 01 '11 at 18:41
  • 1
    @SteveJessop: The default linkage is C++; you must use typedefs to add extern "C" to a function pointer type. Your function could take either, depending on how it is declared. – Fred Nurk Feb 01 '11 at 18:46
  • 1
    @Fred: here's my code: `extern "C" { int func( int (*arg)(int)) { return arg(4); } } \n int a(int x) { return x; } \n extern "C" { int b(int x) { return x; } } \n int main() { func(a); func(b);}`. So (1) do pointers to `a` and `b` have different types, (2) in what circumstances are the two different types incompatible? – Steve Jessop Feb 01 '11 at 18:49
  • Or is this a GCC bug, similar to the one Daniel links to below? – Steve Jessop Feb 01 '11 at 18:55
  • @SteveJessop: Maybe post a question? I'll look for it. – Fred Nurk Feb 01 '11 at 18:56
  • @Steve: (1) They should because function `a` uses "C++" linkage and `b` uses "C" linkage. g++ has a known issue (see, for example, http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29038) which does not treat `func(a)` in your example as an error (the argument to `func` is "C" linkage because linkage specifications "nest"). As far as (2), I think that you were correct to mention when "extern "C" might use a different calling convention". – Daniel Trebbien Feb 01 '11 at 18:57
  • @Fred: actually, testing my code on Comeau gives exactly the error that Daniel refers to in his comments below (Edit: and above). So it's probably (Edit: it is) a different manifestation of the same bug, and GCC just isn't treating the types as different. Naughty GCC. Sort of explains why I thought they were the same, though, serves me right for learning from experience ;-) – Steve Jessop Feb 01 '11 at 18:58
  • @SteveJessop: FWIW, I was trying to say that to take an extern-C function pointer, you'd need: `extern "C" typedef int F(int); void func(F *fp);` (or similar). – Fred Nurk Feb 01 '11 at 19:00

3 Answers3

11

C++03, §7.5p4:

A linkage-specification shall occur only in namespace scope. … A C language linkage is ignored for the names of class members and the member function type of class member functions.

Unfortunately, you simply can't do this in current C++. This text is unchanged in the latest C++0x draft, but "template typedefs" may be able to accomplish it.

Fred Nurk
  • 13,952
  • 4
  • 37
  • 63
  • @DanielTrebbien: "Template aliases" seems to be the new name; §14.5.7 in N3225 plus various papers. – Fred Nurk Feb 01 '11 at 18:57
  • And no, template aliases don't solve this problem. C linkage cannot be applied to templates, including template type aliases. – bames53 Feb 27 '13 at 19:21
1

Consider typedef of a boost::function or the STL function objects ... also you can't define a template inside a extern "C" block for some quite obvious reasons if you think about it.

AJG85
  • 15,849
  • 13
  • 42
  • 50
  • @Fred: When I put the template definition inside an extern "C" block, g++ 4.5.0 emits the error "template with C linkage". – Daniel Trebbien Feb 01 '11 at 18:44
  • this may be compiler specific then ... here's my source http://msdn.microsoft.com/en-us/library/95bhc9c2.aspx – AJG85 Feb 01 '11 at 18:45
  • @DanielTrebbien: There is nothing in the standard, that I can see (but especially looking in §7.5), which excludes them, but it does appear all compilers reject them. (This makes sense, since it would be ineffective given what I quoted in my answer, but that's the the same thing as strictly disallowed.) – Fred Nurk Feb 01 '11 at 18:54
  • @Fred: I think that I will ask a follow-up question on this, because I think that it's interesting. – Daniel Trebbien Feb 01 '11 at 19:00
  • @Fred: Here's the follow-up question: [Why can't templates be within extern "C" blocks?](http://stackoverflow.com/q/4877705/196844) – Daniel Trebbien Feb 02 '11 at 17:39
0

Everything seems to work fine for me if I just omit the extern "C" from your typedef. That is, the following compiles, links, and runs without warnings, errors, or problems:

foo.c:

#include <stdio.h>
int foo(int x) {
    return printf("%x\n", x);
}

test.cpp:

extern "C" int foo(int);

template <typename return_t_, typename arg1_t_>
struct test
{
    typedef return_t_ (*C_fun1_t)(arg1_t_);
    C_fun1_t myFn;
};

int main() {
    test<int, int> t;
    t.myFn = foo;
    return t.myFn(5);
}

For the C++ gurus: I don't know the finer points of what distinguishes C linkage from C++. Are there any hidden problems that wouldn't turn up in a simple example like this?

Karmastan
  • 5,618
  • 18
  • 24
  • 2
    This works with g++ because of a known issue (see, for example, http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29038), but Comeau C/C++ 4.3.10.1 issues the error "error: a value of type 'int (\*)(int) C' cannot be assigned to an entity of type 'int (\*)(int)' " – Daniel Trebbien Feb 01 '11 at 18:48