Функции с C связью могут возвращать тип класса? - PullRequest
5 голосов
/ 20 января 2011

Я наблюдал функцию в DLL, которая имеет связь C.Эта функция возвращает тип класса.Я не уверен, как это стало возможным, поскольку C не понимает класс.
Я сам написал пример dll и запрограммировал и заметил, что компилятор VC ++ показывает предупреждение об этом, но не останавливает вас.Программа может получить GetProcAddress этой функции и вызвать ее для получения возвращенного объекта.Определение класса было сделано доступным для программирования.
Кроме того, если я напишу функцию со связью C, которая возвращает тип класса, где этот класс даже не экспортируется, компилятор не выдаст никакого предупреждения.Программа может использовать эту функцию из dll, если для нее доступно определение класса.
Есть мысли о том, как это работает?Является ли такое поведение компилятора / платформы специфичным?

Ответы [ 3 ]

10 голосов
/ 20 января 2011

Вы неправильно понимаете поведение extern "C".

Это влияет только на имя функции в объектном файле, предотвращая искажение имени .Он не делает функцию ни «больше C», ни «меньше C ++».Единственное дополнительное ограничение, которое extern "C" добавляет к функции C ++, заключается в том, что оно не должно быть перегружено.

5 голосов
/ 20 января 2011

Функции со связью 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: «Я не мог найти лучшую ссылку).

0 голосов
/ 20 января 2011

Объект класса - это в основном просто объект структуры, с некоторыми дополнительными «скрытыми» членами для vtbl и т.

Однако я не уверен, что вы подразумеваете под «программой можно использовать эту функцию, если определение класса доступно для нее». C выдаст ошибку компилятора при просмотре class Blah { ... };.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...