Дилемма дизайна API: непрозрачные ручки - PullRequest
0 голосов
/ 15 января 2019

Я нахожусь в процессе разработки библиотеки, которая пытается унифицировать специфичные для платформы детали с помощью одного общего дескриптора (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) обеспечивает больше умственной гимнастики с повышенной безопасностью (вы не можете передать ручку неправильного вида функция).

Какой из них вы бы предпочли использовать с точки зрения пользователя?

...