Я нахожусь в процессе разработки библиотеки, которая пытается унифицировать специфичные для платформы детали с помощью одного общего дескриптора (1) или их набора (2).
Чтобы сделать вопрос более конкретным, я приведу выдержки из двух реализаций и пример использования.
(1) Tagged union; универсальный тип:
enum tb_handle_kind {};
/* These two are never exposed to the user: */
struct tb_dir {
struct _WIN32_FIND_DATAW find_data;
HANDLE search_handle;
};
struct tb_file {
HANDLE native_handle;
};
struct tb_handle {
enum tb_handle_kind kind;
union {
struct tb_file file;
struct tb_dir dir;
};
};
Что бы вы использовали следующим образом:
file_open(struct tb_handle *, const char *);
handle_close(struct tb_handle *);
struct tb_handle file;
file_open(&file, "path/to/file");
...
handle_close(&file); /* No need for a cast, or a file-specfic function. */
(2) Общий дескриптор как базовый класс для других:
struct tb_handle {
/* Fields shared by each handle. */
};
struct tb_dir {
struct tb_handle base; /* Now a "base class" of tb_dir. */
struct _WIN32_FIND_DATAW find_data;
HANDLE search_handle;
};
Использование:
/* Either a pair of init/close functions. */
int dir_open(struct tb_dir *, const char *);
int dir_close(struct tb_dir *);
/* Or handle_close from the previous example, that now requires a cast: */
handle_close((struct tb_handle *) &some_dir);
Насколько я могу судить, (1) предлагает более простой в использовании API за счет безопасности типов, тогда как (2) обеспечивает больше умственной гимнастики с повышенной безопасностью (вы не можете передать ручку неправильного вида функция).
Какой из них вы бы предпочли использовать с точки зрения пользователя?