Как определить extern, C struct возвращающую функцию в C ++, используя MSVC? - PullRequest
2 голосов
/ 27 апреля 2010

Следующий исходный файл не будет компилироваться с компилятором MSVC (v15.00.30729.01):

/* stest.c */
#ifdef __cplusplus
extern "C" {
#endif

struct Test;

/* NB: This may be extern when imported by another module. */
struct Test make_Test(int x);

struct Test { int x; };

struct Test make_Test(int x)
{
    struct Test r;
    r.x = x;
    return r;
}

#ifdef __cplusplus
}
#endif

Компиляция с cl /c /Tpstest.c приводит к следующей ошибке:

stest.c(8) : error C2526: 'make_Test' : C linkage function cannot return C++ class 'Test'
        stest.c(6) : see declaration of 'Test'

Компиляция без /Tp (которая указывает cl на обработку файла как C ++) работает нормально. Файл также прекрасно компилируется в DigitalMars C и GCC (из mingw) в режимах C и C ++. Я также использовал -ansi -pedantic -Wall с GCC, и он не имел никаких жалоб.

По причинам, о которых я расскажу ниже, нам нужно скомпилировать этот файл как C ++ для MSVC (не для остальных), но с функциями, скомпилированными как C. По сути, нам нужен нормальный компилятор C ... за исключением около шести строк. Можно ли добавить переключатель или атрибут или что-то, что позволит этому работать?


Код, о котором идет речь (хотя и не описанный выше; это всего лишь сокращенный пример), создается генератором кода.

Как часть этого, нам необходимо , чтобы иметь возможность генерировать nans и бесконечности с плавающей запятой в качестве констант (длинный рассказ), что означает, что мы должны скомпилировать с MSVC в режиме C ++, чтобы на самом деле это сделать. Мы нашли только одно решение, которое работает, и оно только работает в режиме C ++.

Мы обертываем код в extern "C" {...}, потому что мы хотим контролировать правила искажения и вызова, чтобы мы могли взаимодействовать с существующим C-кодом. ... также потому, что я доверяю компиляторам C ++ настолько, насколько я могу бросить небольшой универмаг. Я также попытался обернуть просто строку reinterpret_cast в extern "C++" {...}, но, конечно, это не работает. Жалость.

Существует потенциальное решение, которое я нашел, которое требует переупорядочения объявлений таким образом, чтобы полное определение структуры приходилось до функции foward decl., Но это очень неудобно из-за способа выполнения codegen, поэтому Я бы действительно хотел бы избежать необходимости идти по этому пути, если смогу.

Ответы [ 3 ]

3 голосов
/ 27 апреля 2010

Это немного странное сообщение об ошибке, но вызывающая сторона должна знать размер структуры, чтобы можно было сделать вызов. Необходимо зарезервировать место в стеке для возвращаемого значения. Или ожидайте возвращаемое значение в регистрах, если структура достаточно мала.

Вы должны объявить структуру в заголовке. Когда вы это сделаете, ошибка исчезнет:

#ifdef __cplusplus
extern "C" {
#endif

struct Test { int x; };
struct Test make_Test(int x);

struct Test make_Test(int x)
{
    struct Test r;
    r.x = x;
    return r;
}

#ifdef __cplusplus
}
#endif
2 голосов
/ 27 апреля 2010

Это интересный вопрос. Как вы говорите, компиляция кода как кода на C правильно не приводит к ошибкам. И только MSVC, кажется, имеет проблемы с ним при компиляции в виде кода C ++.

Поскольку другие компиляторы C ++ не имеют проблем с кодом, это может быть ошибкой в ​​MSVC, но я могу понять, как MSVC может найти обоснование этой ошибки. Когда компилятор C ++ попадает в строку:

struct Test;

Это неполное объявление struct Test - компилятор не знает, будет ли полное определение struct Test содержать специфичные для C ++ элементы (виртуальные функции, наследование и т. Д.). Обратите внимание, что типы в блоке extern "C" могут по-прежнему использовать все средства C ++; спецификация языковой связи extern "C" применяется только к «типам функций всех описателей функций, имен функций и имен переменных, введенных объявлением (ями)» (7.5 / 4 «Спецификации связи»).

Итак, я мог видеть, как, когда компилятор C ++ MSVC сталкивается с функцией extern "C", которая возвращает неполный тип, он может решить, что ему нужно вернуть ошибку в тот момент, если тип окажется не простым C в стиле POD.

Стандарт C ++ говорит (7.5 / 9 «Спецификации сцепления»):

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

Таким образом, MSVC может иметь некоторую свободу действий (по стандартам), если у него есть причина не разрешать extern "C" функциям возвращать объекты, отличные от POD, хотя я не уверен, почему MSVC будет иметь проблемы, когда другие компиляторы Windows «т. Если кто-то знает подробности (или если они знают, что я просто за пределами базы), я был бы признателен за примечание.

Не то чтобы это вам помогло - это только мое предположение в обосновании.

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

0 голосов
/ 27 апреля 2010

Почему у вас есть экстерьер в

extern struct Test make_Test(int x)
{
    struct Test r;
    r.x = x;
    return r;
}

Это не внешнее, вы определяете это прямо здесь.

...