假设我们有一个表示某个对象状态的结构,以及一个设置该结构中值的函数。赋值的副作用很重要 - 例如,更改对象的状态会影响硬件 - 这就是赋值是一个函数而不是简单地用'='内联的原因。
typedef struct foo_s {
int a;
int b;
int c;
} foo_t;
void foo_create (foo_id_t* id, ...);
void foo_set (foo_id_t id, foo_t* new_values);
创建之后,也许客户想要改变他们的foo,所以他们填写一个foo_t结构并调用foo_set。问题是C中有哪些优雅的习语允许部分结构分配,更改字段的指定子集并将其余部分保留为之前的状态?
我想到的方式:
1)读 - 修改 - 写:调用get,更改一些字段,调用set。需要执行set来比较每个字段以检测实际更改。读写之间可能存在锁定问题。潜在的性能问题取决于用于foo_t的存储。
2)访问者功能:每个字段一个设置功能可以让你只调用你需要的那些功能。缺点包括个别功能的大量增加;锁定整个交易的问题;如果几个领域必须一起改变才有意义,那就很难协调顺序。
3)字段位图:添加位图作为参数来设置或嵌入foo_t中,以指示哪些字段有效。客户端代码设置适当的位,填写相应的字段,并调用set()。缺点包括每个字段的并行位定义的手动维护;为客户提供一些额外的工作。锁定和序列可以通过set实现来处理。
4)偏移列表:类似于(3),但是将可变长度的offets数组传递给已更改的字段的foo_t(offset_of()派上用场)。 set()的实现在列表中向下迭代,将偏移与结构进行比较以了解哪些字段已更改。消除(3)的手动复制,但需要传递数组(指针和长度)。强制客户端声明或malloc()这样的数组,这有点笨拙。
5)属性列表:对象可以表示为对象属性的名称列表(即枚举),而不是结构中的字段。可以使用类似foo_property_set的函数设置各个属性(foo_id,foo_property,void * property_value,int property_len);此样式允许任意访问单个字段和将来的扩展。当目标是将多个字段一起更改时会出现缺点 - 需要事务锁定;某些操作可能需要将多个相关属性一起更改;在许多属性的重复函数调用中有额外的开销。
您使用什么编码模式来处理此问题?
答案 0 :(得分:0)
访问者宏如何?
#define SETFOO(fooptr, member, value) ((fooptr)->member = (value))
这就像方法2),除了你没有扩散功能,你只有 一个宏。无法帮助您解决锁定问题,您只需提供功能即可 在进行任何更改之前锁定和解锁。至于“协调序列,如果几个字段必须一起改变才有意义”,你不能原子地改变多个字段 无论如何C.甚至整个结构分配基本上都是memcpy()。
答案 1 :(得分:0)
我很惊讶最自然的解决方案不在您的列表中:面向对象。我不是在谈论C ++,我在谈论在C中使用面向对象的范例。
您可以将结构视为一个类,并且可以定义任意数量的方法,以您需要的方式对其进行修改。只需为您需要的每个高级操作定义一种方法,这些方法是唯一直接修改struct foo_s
的方法;所有其他代码只是调用这些方法的序列。您甚至可以使用foo_methodName()
这样的方案来调用您的函数,以指示它们属于哪个“类”。
有了这个,你不需要创建任何复杂的方案来一次修改几个字段。