简体   繁体   中英

Calling function template specialization using C calling conventions

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?

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.

我不知道限制的原因,但是您不能使用包装程序extern "C"函数来调用您关心的特定模板实例吗?

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM