Проблема дизайна, которую я сейчас решаю, состоит в том, чтобы перебирать некоторую область памяти и на каждой такой итерации извлекать из этой памяти некоторые метаданные, которые интересуют клиента. В настоящее время я вижу 2 решения:
Я.
struct queue;
struct queue_element_view{
int id;
char *description;
};
//0 - if ok, -1 - if end of queue reached
int next(struct queue*);
//0 - if ok, -1 - if end of queue reached
int current_element_view(struct queue*, struct queue_element_view *);
Таким образом, непрозрачная структура очереди может быть пройдена с помощью функции next, и, поскольку элементы очереди зависят от платформы, и я хочу сохранить кроссплатформенность библиотеки, я предоставил независимую от платформы struct queue_element_view, которая разумна на всех платформах.
Недостаток:
Если клиент пишет такой код:
struct queue *queue_ptr = //
struct queue_element_view current_out;
current_element_view(queue_ptr, ¤t_out);
//current_out now contains current's element meta data
next(queue_ptr);
//current_out now may contain unspecified data
//since queue's current element changed.
Таким образом, вызов next после вызова current_element_view сбивает current_out.
II.
struct queue;
struct queue_element_view;
struct queue_element_view *allocate_view(void);
int * get_id(struct queue_element_view *);
char * get_description(struct queue_element_view *);
//0 - if ok, -1 - if end of queue reached
int next(struct queue*);
//0 - if ok, -1 - if end of queue reached
int current_element_view(struct queue*, struct queue_element_view *);
В этом случае мы выделили struct queue_element_view и current_element_view копируем данные в объект, на который указывает структура queue_element_view *, чтобы затем не затирать данные.
Недостатки:
Он включает в себя дополнительный вызов функции для простого извлечения полей int и char *.
Это усложняет тестирование публичного API.
Так что я немного запутался, какой из них был бы предпочтительнее / читабельнее? Наверное, есть другая альтернатива?
@AvivGoll В основном устарел, но в случае, если реализация после вызова next восстанавливает память (пока) предыдущего элемента, она будет содержать висячий указатель. Я склонен скрывать детали высвобождения памяти во время обхода от клиента
@St.Antario, если ваша идея состоит в том, что next() вернет память, ранее выделенную current_element_view(), то это действительно должно быть частью вопроса. Я думаю, что это гораздо более потенциальная проблема, чем все, что в настоящее время представлено в описании этой альтернативы.
@JohnBollinger Будет ли разумной альтернативой следующее? Я оставляю struct queue_element_view непрозрачным и добавляю функцию const struct queue_element_view * current_element_view_pointer(struct queue *);. Возвращаемый им указатель всегда содержит релевантные данные текущего элемента и не может быть изменен клиентами, но остается согласованным при вызове next?
Идентификатор не может болтаться. Вы можете скопировать описание и добавить бесплатный метод для представления. Что касается устаревшей проблемы, как ее решает второе решение?





Предполагаемая проблема с альтернативой (I), по-видимому, заключается в том, что вызов next() приведет к тому, что данные, ранее скопированные в struct queue_element_view, станут недействительными в результате next() (возможно) освобождения памяти.
Решение: убедитесь, что next() не делает этого. Это может означать, что вам нужно сделать копию строки описания, чтобы поместить ее в представление, вместо того, чтобы просто предоставить клиенту копию исходного указателя. В этом случае может быть полезно предоставить функцию для освобождения любых внутренних распределений, отраженных в struct queue_element_view, может быть что-то вроде этого:
void queue_element_view_clean(struct queue_element_view *view) {
free(view->description);
}
Это избавляет клиента от необходимости знать подробности того, что и как нужно очищать, а что нет. Затем они могут хранить данные столько, сколько захотят, очищая их, когда решат, что с ними покончено. То, что вызов next() будет означать, что они больше не являются данными для текущего элемента итерации, является характерная черта, а не ошибкой — почему имеет смысл мешать клиентам сохранять данные из предыдущих итераций, если они этого хотят? ?
Предполагаемые проблемы связаны с доступом к членам представления, проходящим через функции. Неясно, как это решает предполагаемую проблему с альтернативой I. Хотя это может быть частью решения этой проблемы, я не вижу причин думать, что это часть необходимо.
Решение: используйте альтернативу I. Серьезно. Если вы собираетесь делать копии данных по мере необходимости, чтобы представление сохранялось должным образом при вызове next(), то я не вижу, как вы что-то выиграете, сделав структуру представления непрозрачной.
Ваши две альтернативы кажутся странно перевернутыми.
Для меня было бы целесообразно использовать функции доступа, если вы хотите избежать отдельной структуры представления или копирования данных. Функции будут возвращать данные, относящиеся к текущему элементу итерации, без использования отдельной структуры представления. Тогда у вас будет альтернатива: заставить средства доступа предоставлять копии данных, за которые берет на себя ответственность вызывающая сторона, или возложить на вызывающую сторону ответственность за копирование любых данных, которые они хотят сохранить, когда итератор шагнет вперед.
С другой стороны, если вы предоставляете отдельную структуру для представления элемента, то кажется странным, что вы сделаете это таким образом, что она станет недействительной при расширении итератора. Отдельный объект представления кажется естественным способом сохранения данных представления до тех пор, пока этого хочет вызывающая сторона.
В любом случае, да, какая-то ответственность возлагается на звонящего. Это естественно — бесплатного обеда не бывает. Четко документируйте, каковы эти обязанности, и постарайтесь спроектировать общий API таким образом, чтобы он чувствовал себя последовательным в отношении того, какие виды ответственности возлагаются на пользователя, при каких обстоятельствах и как эти обязанности должны выполняться.
Создание копии кажется хорошим решением для первого варианта. ТЫ.
Можете ли вы объяснить проблему стирания? Будут ли данные повреждены? Или просто устарел?