Возможная двусмысленность с внешними "C", перегрузками и указателями функций
С нормальными функциями можно написать
extern "C" int Frotz(int); // in a header
int Frotz(int x) { return x; }
С указателями функций, однако, это, похоже, было непоследовательно реализовано между компиляторами.
extern "C" int Klutz(int (*)(int), int);
int Klutz(int (*fptr)(int), int x) { return (*fptr)(x); }
В объявлении аргумент также extern "C"
. В определении большинство компиляторов, похоже, соответствуют этим функциям и выполняют функцию Klutz
a extern "C"
. Однако составители Sun и Cray интерпретируют эти функции как разные, создавая перегруженный int Klutz(int (*fptr)(int), int x)
, который позже генерирует ошибку времени привязки.
Несмотря на то, что раздел 7.5.5 на С++ 98 и С++ 11 гарантирует интерпретацию Frotz
, я не могу сказать, является ли стандарт двусмысленным в отношении того, должно ли соответствие extern "C"
происходить до или после проверки перегрузки.
Если Klutz
выше генерирует искаженный (С++) символ или символ extern "C"
?
EDIT 1
Я мог бы использовать typedef, чтобы устранить смещение указателя функции на C или С++ ABI, но меня интересует, содержит ли здесь код (a) Klutz
для связи С++, (b) определяет его, чтобы иметь C link или (c) является неоднозначным в соответствии со стандартом, поэтому компиляторы могут свободно выбирать, как интерпретировать его.
EDIT 2
Это, по-видимому, известная проблема, по крайней мере, теми компиляторами, в которых есть поисковые трекеры. В моих тестах GCC, Clang, Intel, MSVC, IBM XL, PathScale, PGI и Open64 не различают типы функций, которые идентичны, кроме языковой привязки, как это явно требуется стандартом (см. Раздел 7.5.1, приведенный в принятый ответ). Исправление этого может сломать много существующего кода и потребовать изменения ABI. Я не знаю какого-либо компилятора, который на самом деле использует другое соглашение о вызове для связи языка C и С++.
-
Ошибка GCC: "Поиск причин, чтобы попросить удалить эту функцию из следующего стандарта, относится к релевантным;-)"... "И мы даже можем принять решение о официальном WONTFIX".
-
Ошибка Clang: "Я напугана тем, что на самом деле применяю это правило, потому что это правильно означает создание языковой связки части канонический тип, который сломает тонну кода".
Ответы
Ответ 1
C ABI и С++ ABI не гарантируются одинаковыми. Таким образом, указатель функции extern "C"
- это другой тип из указателя функции С++. Вам нужно что-то вроде этого:
extern "C" {
typedef int (*KlutzFuncType)(int);
int Klutz (KlutzFuncType, int);
}
int Klutz (KlutzFuncType fptr, int x) { return (*fptr)(x); }
Здесь обсуждается этот вопрос .
У меня есть только копия черновик. Из 7.5p1:
Два типа функций с различными языковыми связями - это разные типы, даже если они идентичны.
Мое прочтение этого состоит в том, что первый параметр вашего первого Klutz
имеет другой тип, чем первый параметр вашего второго Klutz
, поэтому ваш второй Klutz
должен иметь ссылку на С++.
Существуют реализации С++, которые не учитывают языковые связи для типов функций, несмотря на то, что говорит стандарт. В следующем фрагменте кода KlutzCxxFuncType
относится к функции с С++-связью, а KlutzCFuncType
относится к функции с C-связью.
typedef int (*KlutzCxxFuncType)(int);
extern "C" {
typedef int (*KlutzCFuncType)(int);
int Klutz (KlutzCFuncType, int);
}
int Klutz (KlutzCxxFuncType fptr, int x) { return (*fptr)(x); }
int Klutz (KlutzCFuncType fptr, int x) { return (*fptr)(x); }
Компилятор, который не различает типы функций, основанные на языковой привязке, генерирует ошибку переопределения этого кода. Например, g++ 4.7.2
будет выдавать:
prog.cpp: In function ‘int Klutz(KlutzCFuncType, int)’:
prog.cpp:9:5: error: redefinition of ‘int Klutz(KlutzCFuncType, int)’
prog.cpp:8:5: error: ‘int Klutz(KlutzCxxFuncType, int)’ previously defined here