Подтвердить что ты не робот

Специализация шаблона вызывающей функции с использованием соглашений о вызове C

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

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

И я хочу передать адрес конкретной специализации его функции C

g(&f<int>);

Но поскольку я хочу быть переносимым, я хочу, чтобы вызывающее соглашение "f" соответствовало коду C. Поэтому я экспериментировал с тем, как языковая связь влияет на соглашение о вызове и обнаруживает

  • Языковая связь типа функции влияет на соглашение о вызовах для использования
  • Языковая связь имени функции влияет на mangling

В разделе ссылок на язык спецификации С++ указано

В спецификации привязки специфицированная языковая связь применяется к типам функций всех деклараторов функций, имен функций с внешней привязкой и именам переменных с внешней связью, объявленной в спецификации привязки.

Таким образом, чтобы предотвратить отключение манипуляции, которое необходимо для шаблонов, чтобы отличать разные специализации друг от друга в объектном файле, я сделал следующее.

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

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

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

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


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

extern "C" typedef void ftype(int);

template<typename T>
ftype f;

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

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

4b9b3361

Ответ 1

Как выглядит заголовок 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) {}

Aha, вариативные функции!

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

Ответ 2

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

Ответ 3

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

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

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

#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

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