Правильный тип для ручек в интерфейсах C

Я создаю C api, который скрывает некоторые функции в DLL файле.

Так как все С++ внутри, большинство функций работает против дескрипторов, которые непосредственно отображаются в этом указателе внутри API.

Чтобы получить некоторую степень безопасности типов для этих ручек, я определяю их следующим образом:

typedef struct MyType1* MyType1Handle;
typedef struct MyType2* MyType2Handle;

На самом деле я не определяю MyType1 или MyType2 в любом месте, так как я использую их только как указатели и выполняю листинг типа внутри api с фактическим типом указателя.

Моя проблема в том, что при использовании моей библиотеки в проекте clr в visual studio я получаю это warning: unresolved typeref token (token) for 'type'; image may not run.

http://msdn.microsoft.com/en-us/library/h8027ys9(VS.80).aspx

Это не имеет большого значения, так как это работает, но выглядит непрофессионально.

Мне не нравится использовать void *:

typedef void* MyType1Handle;
typedef void* MyType2Handle;

Это позволяет вызывать функцию, требующую MyType1Handle с MyType2Handle, поскольку они на самом деле одного и того же типа.

Другой подход, который я не хочу использовать, - это что-то вроде этого

typedef int MyType1Handle;
typedef int MyType2Handle;

Это будет работать нормально, если int и указатели имеют одинаковый размер, но это не всегда так, и кажется, что нет надежного способа получения целочисленного размера определенного указателя. Он также имеет проблемы с безопасностью, такие же, как и void *.

Другой подход, который я пробовал, состоял в том, чтобы сделать это следующим образом:

struct MyType1{};
typedef struct MyType1* MyType1Handle;

Это не сработало в C, так как пустые структуры недействительны C-кодом. Я мог бы, конечно, расширить мою структуру с помощью фиктивного члена, но кажется, что должен быть лучший способ сделать это.

Итак, мой вопрос сводится к следующему:

Как вы обычно указываете этот тип типов наиболее совместимым способом?

Ответ 1

Если вы посмотрите, как Microsoft определяет его winapi handle (winnt.h), он выглядит примерно так:

struct HWND__ { int unused; }; typedef struct HWND__ *HWND

на самом деле у них есть макрос для этого:

#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name

поэтому... это, по-видимому, обычная практика. К сожалению, я не могу сделать другое предложение, кроме этого, которое вы уже но я надеюсь, что это поможет вам в любом случае.

Ответ 2

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

Как сказал JamieH, вы получаете непрозрачную структуру, объявляя - но не определяя - структуру. Это совершенно правильно, чтобы иметь и передавать указатели на непрозрачную структуру в функции библиотек в качестве аргументов и возвращать их как значения. Однако пользователи библиотеки не могут создавать или изменять объекты непрозрачной структуры, поскольку размер и содержимое неизвестны.

Стандартная библиотека C и многие другие уже используют этот подход, чтобы он был знаком. Канноническим примером является использование FILE *: fopen() создает и инициализирует структуру и возвращает указатель на нее, fclose() очищает и освобождает ее, а все другие функции ввода/вывода получают свой контекст из переданного в FILE *.

Ответ 3

Я думаю, вы можете просто объявить, но не определить, исходные структуры:

struct MyType1;

typedef struct MyType1 * MyType1Handle;