使用 C 调用约定调用函数模板专门化

发布于 2024-10-31 06:11:14 字数 1190 浏览 1 评论 0原文

我有以下模板

template<typename T> void f(T t) { }

我想将其特定专业化的地址传递给 C 函数

g(&f<int>);

但由于我想要可移植,我希望“f”的调用约定与 C 的调用约定相匹配。所以我尝试了如何语言链接影响调用约定并发现

  • 函数类型的语言链接影响要使用的调用约定
  • 函数名称的语言链接影响重整

C++规范的语言链接部分说

在链接规范中,指定的语言链接适用于链接规范中声明的所有函数声明符的函数类型、具有外部链接的函数名称以及具有外部链接的变量名称。

因此,为了防止禁用重整(模板需要在目标文件中区分不同的专业化),我进行了如下操作,

extern "C" {
    /* function name will not be affected */
    template<typename T> static void f(T t) { }
}

但它给了我一个编译器错误,指出模板不能具有 C 语言链接,我认为这意味着它抱怨函数模板的函数类型。事实上,我发现规范说

模板、模板显式特化 (14.7.3) 和类模板部分特化不得具有 C 链接

现在对我来说很明显我们不想更改名称的链接,因为我们依靠重整来工作。但是禁止更改类型的链接的原因是什么?它似乎限制我们必须使用 C++ 调用约定;有人知道原因吗?是否有简单的解决方法可以实现我最初的目标?


我改变了现在尝试仅向类型提供链接的方式,如下所示

extern "C" typedef void ftype(int);

template<typename T>
ftype f;

并且效果很好。遗憾的是,在使用这种技术时,我没有找到定义 f 的方法。但无论如何,我尝试过的编译器都没有诊断出这一点(尝试过 EDG/comeau、GCC 和 clang),尽管这看起来与以前的情况完全相同:名称应该没有 C 语言链接,但只有类型有。

谁能解释一下吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

千仐 2024-11-07 06:11:14

C 头是什么样子的?在某个地方,C 源代码必须枚举允许的回调类型。您应该利用这个机会使用一系列宏来生成各个存根函数的原型,并在 C++ 源代码中生成相应的宏序列来生成 extern "C" 存根。


关于第二个问题:是的,这是可行的,但是 typedef 不在模板内。我尝试将这样的 typedef 放入类中,但事实证明,即使是类模板也不允许在 extern "C" 内。所以你可以有一个函数模板,但没有依赖类型的参数。

仅仅定义该函数就很容易:

extern "C" typedef void ftype(int);

template<typename T>
static ftype f; // <- added "static" here

template< typename T >
void f(int q) {}

啊哈,可变参数函数!

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;
}

您实际上并不需要类型推导,因为它只是一个回调,因此您可以传递此 & f 到 C 代码,所有回调都具有相同的类型,并且它可以在运行时确定类型并通过可变参数传递它想要的任何内容。

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.

看春风乍起 2024-11-07 06:11:14

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

I don't know the reason for the restriction, but can't you use a wrapper extern "C" function that calls the specific template instantiation you care about?

爱她像谁 2024-11-07 06:11:14

...是否有一个简单的解决方法可以实现我最初的目标?

您可以从几个角度来处理它,具体取决于您想要如何配置和声明它们。

接下来有四种方法,一个命名空间演示了每一种方法。 “类型”可能是最适合您使用的。

#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);
}

输出:

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]

制作:

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

抱歉,今晚我不会仔细研究标准——希望有所帮助。祝你好运!

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文