Специализация шаблона вызывающей функции с использованием соглашений о вызове 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, но только тип имеет.
Кто-нибудь может это объяснить?
Ответы
Ответ 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
Извините, я сегодня не разбираюсь в стандартах - надеюсь, что это поможет. удачи!