Функции со связью C могут возвращать объекты, которые не могут быть выражены в C, если ими можно манипулировать в C. Согласно моей копии Design & Evolution of C ++ (раздел 11.3 +0,3):
Мы рассмотрели несколько альтернатив схемам типовобезопасных связей, прежде чем выбрать одну, фактически добавленную в язык [Stroustrup, 1988]: ...
- обеспечивает безопасное для типов соединение только для функций, которые не могут быть функциями C, поскольку у них есть типы, которые не могут быть выражены в C. ...
Функция, объявленная для связи с C, все еще имеет семантику вызова C ++. То есть формальные аргументы должны быть объявлены, а фактические аргументы должны совпадать в соответствии с правилами соответствия C ++ и контроля неоднозначности. ... Если бы мы предоставили специальные сервисы для C, мы были бы обязаны добавить неограниченный набор соглашений о вызовах языка в компиляторы C ++ [для связи с Pascal, Fortran, PL / I и т. Д.]. ...
Связывание, межязыковые вызовы и межязыковая передача объектов по своей природе являются сложными проблемами и имеют много аспектов, зависящих от реализации. ... Я полагаю, мы не слышали об этом последнем.
То есть связь C ++ не основана на том, являются ли используемые типы допустимыми типами C. Это намеренное дизайнерское решение. Это позволяет вам создать DLL, скомпилированную в C ++, но ее можно использовать из C через заголовок:
// in the header
struct Foo; // forward declaration
#ifdef __cplusplus
extern "C" {
#endif
struct Foo* create_foo();
void destroy_foo(struct Foo*);
void foo_bar(struct Foo*);
#ifdef __cplusplus
} // extern "C"
// now declare Foo
struct Foo {
void bar();
};
#endif
// in the implementation file
#include <iostream>
extern "C" {
Foo* create_foo()
{
return new Foo();
}
void destroy_foo(Foo* f)
{
delete f;
}
void foo_bar(Foo* f)
{
f->bar();
}
}
void Foo::bar()
{
std::cout << "Foo::bar() called\n";
}
Обратите внимание на звонки на new
, delete
и std::cout
. Все это требует времени выполнения C ++. Следовательно, файл реализации должен быть скомпилирован с C ++, но, поскольку функции имеют связь с C, заголовок может использоваться либо из C, либо из C ++.
Итак, как вы получаете Foo
в C? В этом случае вы этого не сделаете, потому что нет способа полностью объявить это в заголовке так, как это поймет C. Вместо этого C видит только предварительное объявление, которое создает неполный тип. C не знает, насколько велик неполный тип, поэтому функции C не могут создать его или работать с ним напрямую, но C знает, насколько велик указатель на неполный тип, поэтому C может работать с указателем на неполный тип. тип. Вы можете стать намного более креативными и создавать POD-типы в C ++, над которыми вы можете работать непосредственно в C.
Пока вы используете только Foo*
s в C, вам не нужно фактически определять структуру Foo
в C. Бывает, что APR использует аналогичный дизайн ("Создание Тип APR: «Я не мог найти лучшую ссылку).