Правильный тип для ручек в интерфейсах 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;
Ответ 4
Похожие темы:
Сравнение ручек: пустые классы против классов undefined против void *