Calling function template specialization using C calling conventions

前端 未结 3 1071
独厮守ぢ
独厮守ぢ 2020-12-05 11:36

I have the following template

template void f(T t) { }

And I want to pass the address of a specific specialization of it

相关标签:
3条回答
  • 2020-12-05 11:48

    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?

    0 讨论(0)
  • 2020-12-05 11:59

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

    0 讨论(0)
  • 2020-12-05 12:09

    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.

    0 讨论(0)
提交回复
热议问题