Обмениваются ли указатель объекта и пустоты в объявлениях внешнего C? - PullRequest
0 голосов
/ 06 июля 2018

У меня есть внешний интерфейс "C" для некоторого динамически загруженного кода. Для этого кода некоторые объекты являются непрозрачными дескрипторами, выраженными как void*. Интерфейс выглядит так:

extern "C" {
  void* get_foo() { Foo* foo =  /* create and remember object */; return foo; }
  int foo_get_bar(void* Foo) { return ((Foo*)foo)->bar(); }
}

Такой шаблон в значительной степени является стандартным способом взаимодействия с объектами C ++ через C-API, AFAIK. Но мне интересно, могу ли я пропустить приведение указателя?

Вызывающий код генерируется и связывается только с интерфейсом выше (он имеет свои собственные объявления функций). Таким образом, он эффективно генерирует код, подобный следующему:

void foo_get_bar(void*);
void* get_foo();

int do_something() { return foo_get_bar(get_foo()) };

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

Теперь можно изменить интерфейс на (более простой) вариант:

extern "C" {
  int foo_get_bar(Foo* Foo) { return foo->bar(); }
}

Или есть небольшая разница между void* и Foo* на уровне компоновщика (может, например, размеры не совпадают?).

edit: Возможно, я вызвал некоторую путаницу с кодом вызывающей стороны в C. Нет кода C. Компилятора C не существует, и, следовательно, не требуется проверка типа C. Код вызывающего абонента генерируется и использует C-API. Поэтому для использования непрозрачной структуры мне нужно знать, как C-API представляет этот указатель после компиляции.

Ответы [ 2 ]

0 голосов
/ 06 июля 2018

Я нашел вопрос немного сбивающим с толку, но если я вас правильно понимаю, это нормально. Размер указателя на что-либо одинаков на любой современной платформе (к счастью, мы оставили безумие __near, __far и __middling).

Таким образом, я мог бы ввести некоторое понятие безопасности типов, подобное этому, в заголовочном файле, объявляющем get_foo():

// Foo.h
struct Foo;

extern "C"
{
    Foo* get_foo ();
    int foo_get_bar (Foo* Foo);
}

А в реализации класса Foo, который предположительно является C ++, мы могли бы иметь:

// foo.cpp
struct Foo
{
    int get_bar () { return 42; }
};

Foo* get_foo () { return new Foo (); }
int foo_get_bar (Foo *foo) { return foo->get_bar (); }

Подлый, но законный. Вам просто нужно объявить Foo как struct, а не class в foo.cpp, что означает, что если вы хотите что-то private или protected, вы должны явно сказать это.

Если это неприятно, то вместо этого вы можете сделать это в foo.h:

#ifdef __cplusplus
    class Foo;
#else
    struct Foo;
#endif

А затем объявите Foo как class вместо struct в foo.c. Выбери свой яд.

Демонстрационная версия .

0 голосов
/ 06 июля 2018

На практике размеры / представления указателей не должны изменяться на распространенных платформах x86 и ARM, поэтому вы можете избежать использования void *.


В принципе, однако, стандарт не гарантирует, что 2 указателя разных ссылочных типов будут иметь одинаковое представление указателя и даже ширину, за исключением случаев, упомянутых в C11 6.2.5p28 :

  • что указатель на void будет иметь те же требования к представлению и выравниванию, что и указатель на тип символа
  • указатели на совместимые типы будут иметь одинаковые требования к представлению и выравниванию, несмотря на выравнивание
  • все указатели на структуры будут иметь одинаковые требования к представлению и выравниванию между собой
  • все указатели на союзов будут иметь одинаковые требования к представлению и выравниванию между собой

Поэтому самый простой способ представить указатель на класс C ++ в C - это использовать указатель на неполную структуру:

typedef struct Foo Foo;
void foo_get_bar(Foo *);
Foo *get_foo(void);

Еще одним преимуществом является то, что компилятор будет жаловаться, если вы попытаетесь использовать Bar * по ошибке.


P.S .: обратите внимание, что get_foo() не означает то же самое, что get_foo(void) в C.

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