提供其当前元素的 "view" 的迭代器
Iterator providing a "view" of its current element
我目前正在解决的设计问题是遍历某个内存区域,并在每次这样的迭代中从该内存中检索客户感兴趣的一些元数据。我目前看到 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.
所以在调用 current_element_view
之后调用 next
破坏了 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 *
指向的对象,因此 next 不会破坏数据。
缺点:
它涉及一个函数附加调用来简单地检索 int
和 char *
字段
它使测试 public api 更复杂。
所以我有点困惑 preferrable/readable 是哪一个?可能还有另一种选择?
备选方案 I
备选方案 (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()
时适当地保留,那么我看不出通过使视图结构不透明来获得任何好处。
总体
你的两个选择似乎很奇怪。
如果您想避免使用单独的视图结构或复制数据,那么使用访问器函数对我来说是有意义的。这些函数将 return 与当前迭代元素相关的数据——不涉及单独的视图结构。然后,您可以选择让访问者提供调用者负责的数据副本,或者让调用者负责在迭代器向前推进时复制他们想要保留的任何数据。
另一方面,如果您为元素视图提供一个单独的结构,那么您这样做的方式允许它在迭代器前进时变得无效似乎很奇怪。单独的视图对象似乎是允许视图数据被保留的自然方式,只要调用者需要它。
无论如何,是的,调用者承担了某种责任。这很自然——天下没有免费的午餐。清楚地记录这些责任是什么,并尝试以一种在用户承担什么样的责任、在什么情况下以及如何履行这些责任方面感觉一致的方式来设计整体 API。
我目前正在解决的设计问题是遍历某个内存区域,并在每次这样的迭代中从该内存中检索客户感兴趣的一些元数据。我目前看到 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.
所以在调用 current_element_view
之后调用 next
破坏了 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 *
指向的对象,因此 next 不会破坏数据。
缺点:
它涉及一个函数附加调用来简单地检索
int
和char *
字段它使测试 public api 更复杂。
所以我有点困惑 preferrable/readable 是哪一个?可能还有另一种选择?
备选方案 I
备选方案 (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()
时适当地保留,那么我看不出通过使视图结构不透明来获得任何好处。
总体
你的两个选择似乎很奇怪。
如果您想避免使用单独的视图结构或复制数据,那么使用访问器函数对我来说是有意义的。这些函数将 return 与当前迭代元素相关的数据——不涉及单独的视图结构。然后,您可以选择让访问者提供调用者负责的数据副本,或者让调用者负责在迭代器向前推进时复制他们想要保留的任何数据。
另一方面,如果您为元素视图提供一个单独的结构,那么您这样做的方式允许它在迭代器前进时变得无效似乎很奇怪。单独的视图对象似乎是允许视图数据被保留的自然方式,只要调用者需要它。
无论如何,是的,调用者承担了某种责任。这很自然——天下没有免费的午餐。清楚地记录这些责任是什么,并尝试以一种在用户承担什么样的责任、在什么情况下以及如何履行这些责任方面感觉一致的方式来设计整体 API。