Вызов специализации шаблона функции с использованием соглашений о вызовах C - PullRequest
14 голосов
/ 08 апреля 2011

У меня есть следующий шаблон

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) { }
}

Но это дает мне ошибку компилятора, говорящую о том, что шаблоны не могут иметь связи на языке Си, что, как я понимаю, означает, что они жалуются на тип функции шаблона функции. И на самом деле я нашел спецификацию, чтобы сказать

Шаблон, явная специализация шаблона (14.7.3) и частичная специализация шаблона класса не должны иметь связи C

Теперь для меня очевидно, что мы не хотим менять связь name , потому что мы полагаемся на калечащие работы. Но какова причина запрета на изменение связи type ? Кажется, это ограничивает нас в необходимости использовать соглашение о вызовах C ++; кто-то знает причину, и есть ли легкий способ обойти мою первоначальную цель?


Я изменил способ, которым я пытаюсь дать привязку только к типу, следующим образом

extern "C" typedef void ftype(int);

template<typename T>
ftype f;

И это отлично работает. К сожалению, я не вижу способа определить f при использовании этой техники. Но в любом случае, никакой компилятор, который я пробовал, диагностирует это (пробовал EDG / comeau, GCC и clang), даже если это выглядит точно так же, как и раньше: имя не должно иметь связи на языке C, но имеет только тип.

Может кто-нибудь объяснить это?

Ответы [ 3 ]

3 голосов
/ 08 апреля 2011

Как выглядит заголовок 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<int> в код C, все обратные вызовы имеют одинаковый тип, и он может сделать определение типа во время выполнения и передать все, что хочетВарарги.

1 голос
/ 08 апреля 2011

... есть ли легкий способ обойти мою первоначальную цель?

вы можете подойти к нему с нескольких точек зрения, в зависимости от того, как вы хотите настроить и объявить их.

Далее следуют четыре подхода, одно пространство имен демонстрирует каждый. «Тип», вероятно, самый простой для вашего использования.

#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

извините, сегодня вечером я не буду пересматривать стандарты - надеюсь, это поможет. удачи!

1 голос
/ 08 апреля 2011

Я не знаю причину ограничения, но вы не можете использовать функцию-оболочку extern "C", которая вызывает конкретный экземпляр шаблона, который вас интересует?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...