Вперед объявить указатели на структуры в C++

Я использую стороннюю библиотеку с таким объявлением:

typedef struct {} __INTERNAL_DATA, *HandleType;

И я хотел бы создать класс, который принимает HandleType в конструкторе:

class Foo
{
    Foo(HandleType h);
}

без включения заголовка, определяющего HandleType. Обычно я бы просто заранее объявил такой тип, но я не могу понять синтаксис для этого. Я очень хочу сказать что-то вроде:

struct *HandleType;

Но это говорит «Ожидаемый идентификатор перед *» в GCC. Единственное решение, которое я вижу, это написать свой класс следующим образом:

struct __INTERNAL_DATA;
class Foo
{
    Foo(__INTERNAL_DATA *h);
}

Но это зависит от внутренних деталей библиотеки. Другими словами, он использует имя __INTERNAL_DATA, которое является деталью реализации.

Похоже, что должна быть возможность предварительно объявить HandleType (часть общедоступного API) без использования __INTERNAL_DATA (часть реализации библиотеки). Кто-нибудь знает, как это сделать?

РЕДАКТИРОВАТЬ: добавлено больше подробностей о том, что я ищу.


person Jesse Rusak    schedule 10.03.2009    source источник


Ответы (5)


Обновление:

Я использую его в реализации .cpp Foo, но не хочу включать его в свой заголовок .h для Foo. Может быть, я просто слишком педантичен? :)

Да, вы :) Продолжайте с предварительным объявлением.

Если HandleType является частью интерфейса, должен быть заголовок, объявляющий это. Используйте этот заголовок.

Ваша проблема все еще неясна. Вы пытаетесь защититься от чего-то, чего не можете.

Вы можете добавить следующую строку в свою клиентскую библиотеку:

typedef struct INTERNAL_DATA *HandleType;

но, если имя/структура изменится, вы можете столкнуться с некоторыми неприятностями при кастинге.

Попробуйте шаблоны:

template <class T>
class Foo
{
    Foo(T h);
};

Форвардная декларация в порядке. Если вы собираетесь использовать указатели или ссылки, вам нужно только объявление класса (__INTERNAL_DATA) в области видимости. Однако, если вы собираетесь использовать функцию-член или объект, вам нужно будет включить заголовок.

person dirkgently    schedule 10.03.2009
comment
но затем мне нужно предварительно объявить __INTERNAL_DATA, имя, которое можно изменить или вообще удалить. (Поскольку это деталь реализации библиотеки.) - person Jesse Rusak; 10.03.2009
comment
Как и HandleType, насколько вам известно. - person dirkgently; 10.03.2009
comment
Ваши требования не ясны, возможно, вы можете добавить больше деталей. - person dirkgently; 10.03.2009
comment
Однако HandleType является частью общедоступного интерфейса. Я должен его использовать. __INTERNAL_DATA нет. - person Jesse Rusak; 10.03.2009
comment
Я использую его в реализации .cpp Foo, но не хочу включать его в свой заголовок .h для Foo. Может быть, я просто слишком педантичен? :) - person Jesse Rusak; 10.03.2009
comment
я думаю, что вы не можете избежать использования __INTERNAL_DATA здесь. вы должны использовать имя, которое имеет связь, а в С++ это первое имя в typedef, которое определено как псевдоним для структуры (то есть __INTERNAL_DATA), если структура не имеет имени, как в вашем случае. - person Johannes Schaub - litb; 10.03.2009
comment
Отливки не требуют времени на обработку. Они синтаксический сахар для компиляторов. Не может быть одновременно затемнения и прозрачности. Либо вам нужно сделать много надоедливого чтения слепков, либо вам нужно объявить внутреннюю структуру так, чтобы все могли видеть. - person jmucchiello; 01.05.2009

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

Если вас беспокоит время компиляции (это большой заголовок), возможно, вы можете поместить его в предварительно скомпилированный заголовок или просто включить соответствующий заголовок из библиотеки.

Например. многие заголовки библиотек выглядят так

// library.h
#include "Library/Something.h"
#include "Library/SomethingElse.h"
person Andrew Grant    schedule 10.03.2009

 typedef struct {} __INTERNAL_DATA, *HandleType;

Если он определен таким образом (все в одной строке), то __INTERNAL DATA является такой же частью открытого интерфейса, как и HandleType.

Однако я не думаю, что __INTERNAL_DATA действительно существует. Более чем вероятно, что HandleType действительно (внутренне) является int. Это странное определение — просто способ определить его так, чтобы он имел тот же размер, что и int, но отличался от него, чтобы компилятор выдал вам ошибку, если вы попытаетесь передать int там, где вы должны передать HandleType. Поставщик библиотеки мог так же легко определить его как "int" или "void*", но таким образом мы получаем некоторую проверку типов.

Следовательно, __INTERNAL_DATA — это просто соглашение, которое не собирается меняться.


ОБНОВЛЕНИЕ: Вышесказанное было чем-то вроде умственной отрыжки... Хорошо, __INTERNAL_DATA определенно не существует. Мы знаем это точно, потому что можем видеть его определение как пустую структуру. Я предполагаю, что сторонняя библиотека использует внешнюю компоновку "C" (без управления именами), и в этом случае просто скопируйте typedef — все будет в порядке.

Внутри самой библиотеки HandleType будет иметь совершенно другое определение; может быть int, может быть "struct MyStruct {.......} *".

person James Curran    schedule 10.03.2009

Если вы очень, очень, очень не хотите раскрывать _INTERNAL_DATA вызывающей стороне, тогда ваш единственный реальный выбор — использовать typedef void* HandleType; Затем внутри вашей библиотеки вы можете делать все, что захотите, включая изменение всей реализации *HandleType.

Просто создайте вспомогательную функцию для доступа к вашим реальным данным.

inline _INTERNAL_DATA* Impl(HandleType h) {
    return static_cast<_INTERNAL_DATA*>(h);
}
person jmucchiello    schedule 10.03.2009

Я не совсем уверен, что вы собираетесь делать, но следующее будет работать без включения фактического файла заголовка:

// foo.h
class Foo
{
    public:
    template<typename T>Foo(T* h) { /* body of constructor */ }
};

Имейте в виду, вам все равно придется иметь доступ к публичным членам __INTERNAL_DATA в теле конструктора.

редактировать: как указал Джеймс Карран, структура __INTERNAL_DATA не имеет элементов, поэтому ее можно использовать, как указано выше, без проблем.

person e.James    schedule 10.03.2009
comment
Внимательно проверьте typedef... __INTERNAL_DATA не имеет НИКАКИХ общедоступных (или частных) членов.... - person James Curran; 10.03.2009
comment
Я предположил, что это всего лишь пример структуры, чтобы не усложнять вопрос. - person e.James; 10.03.2009