Является ли `extern C` частью типа функции?

Я не вижу никаких комментариев в стандарте, кроме вещей, связанных со связью.

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

#include <type_traits>

extern "C" {
  int c_func(int);
}

int cpp_func(int);

static_assert(!std::is_same<decltype(c_func), decltype(cpp_func)>::value,
              "It should not be the same type");

static_assert терпит неудачу, так как GCC считает, что эти функции имеют один и тот же тип.

  • Является ли extern "C" частью типа функции?
  • Как проверить, использует ли функция соглашение о вызовах C или соглашение о вызовах C++?

person Inbae Jeong    schedule 30.10.2014    source источник
comment
С++ поддерживает, например. перегрузка функций. Две функции с разными типами аргументов, но с одним и тем же именем - это невозможно сделать с помощью ссылки на C, потому что имя функции необходимо изменить, чтобы оно оставалось уникальным.   -  person keltar    schedule 30.10.2014
comment
@keltar Да, ты прав, и я не об этом спрашиваю. Речь идет о соглашении о вызовах, а не об искажении имен. Во всех документах, вопросах и ответах о extern "c", которые я нашел в Интернете, говорится об изменении имени, но не о соглашении о вызовах.   -  person Inbae Jeong    schedule 30.10.2014
comment
Поскольку его нельзя использовать в методах (и, следовательно, не может повлиять на этот вызов) - да, он должен только отключать изменение без изменения соглашения, если явно не указано иное (например, с командой __attribute__ для компилятора). Оба типа, которые вы сравниваете, являются int (*)(int).   -  person keltar    schedule 30.10.2014
comment
Может быть, немного менее неясно - C++ уже использует то же соглашение, что и C для обычных функций (однако это не относится к методам), поэтому здесь нечего менять. Однако соглашение C меняется с используемым вами ABI, поэтому оно будет отличаться для 32-битной, 64-битной системы V, 64-битных окон и т. д. как для C, так и для C++.   -  person keltar    schedule 30.10.2014
comment
@keltar большинство компиляторов C ++ искажают все, включая обычные функции, если не сказано не делать этого.   -  person Chris Stratton    schedule 30.10.2014
comment
связанные: stackoverflow.com/questions/26174510   -  person Jamboree    schedule 30.10.2014
comment
@ChrisStratton Я даже не думаю, что «большинство», потому что им приходится калечить; это именно то, что я сказал в первом комментарии.   -  person keltar    schedule 30.10.2014
comment
@keltar Манглинг - не единственное, что здесь задействовано. Я использовал компиляторы, в которых соглашения о вызовах различались для C и C++.   -  person James Kanze    schedule 30.10.2014
comment
@JamesKanze маловероятно, но возможно. Трудно поддерживать несколько ABI без особых причин. Однако хотелось бы знать, что это за компилятор.   -  person keltar    schedule 30.10.2014
comment
@кельтар Зортех. Для MS-DOS под Windows. Есть очень веские причины для этого в архитектуре Intel (16 и 32 бит). И я не вижу никакой реальной проблемы в отношении нескольких ABI.   -  person James Kanze    schedule 30.10.2014
comment
extern предназначен для связи и области видимости. Это не для типа.   -  person ha9u63ar    schedule 30.10.2014


Ответы (1)


Стандарт ясно дает понять, что языковая связь действительно является свойством самой функции type:

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

В случае, если это было недостаточно ясно, есть примечание (выделено мной), которое делает предполагаемое значение недвусмысленным:

[Примечание: Поскольку языковая связь является частью типа функции, при косвенном обращении через указатель на функцию C функция, на которую ссылается результирующее lvalue, считается функцией C . — конец примечания ]

Более того,

Два типа функций с разными языковыми связями являются разными типами, даже если в остальном они идентичны.

Итак, ответ на ваш первый вопрос:

  • Да, extern "C" является частью типа функции.

Однако большинство компиляторов не различают типы функций с языковой компоновкой C и C++. Это, например, давняя ошибка в GCC (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316; см. список дубликатов). Я не читал внимательно всю ветку, но похоже, что большая часть существующего кода сломается, если GCC начнет применять правило, согласно которому они действительно являются разными типами. Возможно, именно поэтому другие компиляторы также не соответствуют стандарту.

Учитывая это, ответ на ваш второй вопрос будет выглядеть так:

  • Вероятно, нет переносимого способа выполнить эту проверку во время компиляции. Конечно, после перевода вы всегда можете зайти и посмотреть объектный файл и посмотреть, искажено ли имя или нет.

Но теоретически ваше статическое утверждение должно работать так, как вы думаете. Вот только на практике это не так.

Дополнение:

Если мое понимание стандарта правильное, то, например, следующий шаблон функции

template <typename R, typename... A>
void f(R(*)(A...));

нельзя создать экземпляр для создания функции, которая принимала бы указатель на функцию с привязкой к языку C в качестве аргумента, поскольку тип R(*)(A...) является "указателем на функцию с привязкой к языку C++, принимающую аргументы типов A... и возвращающую R".

Если бы компиляторы действительно работали так, легко понять, как можно было бы в общем определить, имеет ли функция связь с языком C или C++.

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

person Brian Bi    schedule 30.10.2014