Разрешение перегрузки с extern " C " рычагом
в смешанном проекте C / C++ нам нужно сделать вызов из C в функцию C++. Вызываемая функция перегружена как три отдельные функции, но мы можем игнорировать это со стороны C, мы просто выбираем наиболее подходящую и придерживаемся этой.
есть два способа сделать это: (1) написать небольшую оболочку C++ с функцией extern "C", которая перенаправляет вызов выбранной перегруженной функции, или (2) хакерский способ просто объявить одну функцию, которую мы хотим вызвать из C как extern "C".
вопрос в том, есть ли какие-либо недостатки (кроме кошмаров и плохой кармы), чтобы пойти на второй вариант? Другими словами, учитывая три перегруженные функции, где одна объявлена как exern "C", следует ли ожидать проблем со стороной C++, или это хорошо определено в соответствии со стандартом?
4 ответов
Я считаю, что язык в стандарте специально написан, чтобы разрешить ровно одну функцию с связью " C "и произвольное количество других функций с связью" C++", которые перегружают то же имя (§[dcl.Ссылка]/6):
не более одной функции с определенным именем может иметь связь языка C. Два объявления для функции с связью языка C с тем же именем функции (игнорируя имена пространств имен, которые его определяют), которые отображаются в разных пространствах имен области относятся к той же функции. Два объявления для объекта с связью языка C с тем же именем (игнорируя имена пространств имен, которые его квалифицируют), которые появляются в разных областях пространства имен, относятся к одному и тому же объекту.
стандарт показывает следующий пример:
complex sqrt(complex); // C + + linkage by default
extern "C" {
double sqrt(double); // C linkage
}
пока вы следуете другим правилам для функций extern-C (например, их специальным требованиям к имени), указание одной из перегрузок как extern-C прекрасно в соответствии со стандартом. Если вы используете указатели функций для этих функций, имейте в виду, что языковая связь является частью типа функции, и необходимость указателя функции на эту функцию может решить проблему для вас.
в противном случае я не вижу никаких существенных недостатков. Даже потенциальный недостаток копирование параметров и возвращаемого значения может быть смягчено спецификациями компилятора и реализации, которые позволяют встроить функцию, если это определено как проблема.
namespace your_project { // You do use one, right? :)
void f(int x);
void f(char x);
void f(other_overloads x);
}
extern "C"
void f(int x) {
your_project::f(x);
}
даже если это было разрешено стандартом, будущие сопровождающие кода, вероятно, будут чрезвычайно смущены и могут даже удалить extern "C", нарушив код C (возможно, достаточно поздно, чтобы события не были связаны).
просто напишите обертку.
изменить: В C++03 7.5/5:
Если два объявления одного и того же функция или объект укажите другое спецификации рычага (то есть спецификации рычага этих объявления указывают разные строковые литералы), программа плохо сформированные, если декларации появляются в той же единице трансляции, и правило одного определения (3.2) применяется, если объявления появляются в разных единица перевода...
я интерпретирую это как неприменимое, поскольку функции C и C++ с одинаковым именем на самом деле не являются одной и той же функцией, но эта интерпретация может быть неправильной.
затем из C++03 7.5/6:
в большинстве одна функция с определенным имя может иметь связь с языком...
Это означает, что у вас могут быть другие, не-c-linkage, функции с тем же именем. В этом случае, c++ перегрузки.
(этот ответ относится к C++14; другие ответы до сих пор являются C++03).
разрешается использовать перегрузку. Если есть extern "C"
определение функции какого-то конкретного имени, то применяются следующие условия (ссылки на C++14 в скобках):
- декларация
extern "C"
функция должна быть видимой в точке любого объявления или определения перегрузок этого имени функции (7.5/5) - не должно быть никаких других
extern "C"
определение функции или переменной с тем же именем, в любом месте. (7.5/6) - перегруженная функция с тем же именем не должна объявляться в глобальной области. (7.5/6)
- в том же пространстве имен, что и
extern "C"
функция, не должно быть другого объявления функции с тем же именем и списком параметров. (7.5/5)
если любое нарушение вышеуказанных правил происходит в той же единице трансляции, компилятор должен диагностировать его; в противном случае это неопределенного поведения без диагностики.
таким образом, ваш файл заголовка может выглядеть примерно так:
namespace foo
{
extern "C" void bar();
void bar(int);
void bar(std::string);
}
последний пункт маркера говорит, что вы не можете перегрузить только на связь; это плохо сформировано:
namespace foo
{
extern "C" void bar();
void bar(); // error
}
вы можете сделать это в разных пространствах имен:
extern "C" void bar();
namespace foo
{
void bar();
}
в этом случае обычные правила неквалифицированного поиска определяют, является ли вызов bar()
в каком-то коде находит ::bar
, foo::bar
, или неоднозначные.