Скажем, я создаю библиотеку к ложка-вилке quuxes в C.
Quuxes нужны переменные с двумя состояниями, чтобы быть sporked успешно:
static int quux_state;
static char* quux_address;
/* function to spork quuxes found in a file,
reads a line from the file each time it's called. */
void spork_quux(FILE*);
Если я буду хранить те данные как глобальные переменные, то только единственный клиент сможет к ложка-вилке quuxes в единственное время, еще переменные состояния будут искажены второй вызывающей стороной, и авария может последовать.
Вопрос - то, что лучший способ состоит в том, чтобы разработать повторно используемую библиотеку в C?
Я развлек следующие случаи ни к какому удовлетворительному заключению.
В следующем случае вопрос состоит в том, как связать клиент к каждому состоянию?
/* library handles all state data allocation */
static int* quux_state;
static char** quux_address;
В следующем случае клиент может смешать с состоянием, очень нежелательным
/* let each client store state */
typedef struct { int state; char* address; } QuuxState;
QuuxState spork_quux(FILE*);
Так, как сделать это правильно?
Используйте struct, но не раскрывайте его определение клиенту.
т.е. в заголовочном файле .h поставьте:
typedef struct QuuxState QuuxState;
QuuxState *spork_quux(FILE*);
а в файле реализации .c:
struct QuuxState
{
int state;
char* address;
};
QuuxState *spork_quuxFILE *f)
{
QuuxState *quuxState = calloc(1, sizeof(*quuxState));
if (!quuxState)
return NULL;
quuxState->state = ....;
........
return quuxState;
}
Преимущества такого подхода в том, что:
Единственным недостатком является то, что вам нужно выделить блок памяти - однако, если предположить, что ваша библиотека делает что-то нетривиальное (а если она делает файловый ввод-вывод, то это, конечно, нетривиально), то накладные расходы на один malloc пренебрежимо малы.
Возможно, вы захотите переименовать приведенную выше функцию в что-то вроде 'QuuxSpork_create', и добавить больше функций для выполнения построчной работы и уничтожения состояния после завершения работы.
void QuuxSpork_readLine(QuuxState *state)
{
....
}
void QuuxSpork_destroy(QuuxState *state)
{
free(state);
}
Случайным примером библиотеки, которая работает примерно так же, может быть библиотека потоков POSIX, pthreads.
Используйте структуру, но не говорите клиенту, что это структура. Передавайте непрозрачный указатель - void*, а еще лучше указатель на пустой фиктивный struct - и возвращайте его обратно, когда это необходимо.
Большинство библиотечных функций обрабатывают это, возвращая пользователю информацию о состоянии в том типе данных, который ему нужен. В вашем случае struct. (возьмите strtok vs strtok_r). Я считаю, что это создает прецедент, когда вы должны передать его обратно пользователю. Пустота * работает. вы даже можете ввести его, чтобы он выглядел красиво.
Более того, strtok_r делает это, редактируя аргумент командной строки, не возвращая указатель на состояние. Я ожидаю, что любая функция повторного входа, которую я использую, будет следовать аналогичному формату. Конечно, мой мозг был искажен каким-то довольно сумасшедшим кодом C.
void spork_quux(FILE*, QuuxState **);