C Struct中的缺省值

我有这样的数据结构:

     struct foo {
         int id;
         int路由;
         int backup_route;
         int current_route;
     }

和一个叫做update()的函数,用来请求它的变化。

  更新(42,dont_care,dont_care,new_route);

这真的很长,如果我添加一些东西的结构,我必须添加一个'dont_care'每EVERY更新(…)。

我正在考虑传递一个结构,而不是事先在“dont_care”中填充结构,而不是在函数调用中拼写。 我可以创build结构的默认值不考虑的地方,只是设置我关心的领域之后,我声明它作为一个局部variables?

     struct foo bar = {.id = 42,.current_route = new_route};
    更新(巴);

什么是最优雅的方式来传递我想expression更新function的信息?

我希望其他所有的东西都默认为-1('不关心'的密码)

虽然macros和/或函数(如已经提到的)将会工作(并且可能具有其他的积极效果(即,debugging钩子)),但是它们比需要的更复杂; 最简单也可能是最优雅的解决scheme是只定义一个用于variables初始化的常量:

const struct foo FOO_DONT_CARE = { // or maybe FOO_DEFAULT or something dont_care, dont_care, dont_care, dont_care }; ... struct foo bar = FOO_DONT_CARE; bar.id = 42; bar.current_route = new_route; update(&bar); 

这段代码实际上没有理解间接性的精神上的开销,并且非常清楚你明确设置的是什么字段(安全地)忽略那些你没有设置的字段。

您可以将您的秘密特殊值更改为0,并利用C的默认结构成员语义

 struct foo bar = { .id = 42, .current_route = new_route }; update(&bar); 

然后会传递0作为初始化器中未指定的bar的成员。

或者你可以创build一个macros来为你做默认的初始化:

 #define FOO_INIT(...) { .id = -1, .current_route = -1, .quux = -1, ## __VA_ARGS__ } struct foo bar = FOO_INIT( .id = 42, .current_route = new_route ); update(&bar); 

<stdarg.h>允许您定义可变参数函数(接受无限个参数,如printf() )。 我将定义一个函数,它接受任意数量的参数对,其中一个指定要更新的属性,另一个指定值。 使用enum或string来指定属性的名称。

也许考虑使用预处理器macros定义:

 #define UPDATE_ID(instance, id) ({ (instance)->id= (id); }) #define UPDATE_ROUTE(instance, route) ({ (instance)->route = (route); }) #define UPDATE_BACKUP_ROUTE(instance, route) ({ (instance)->backup_route = (route); }) #define UPDATE_CURRENT_ROUTE(instance, route) ({ (instance)->current_route = (route); }) 

如果(struct foo)的实例是全局的,那么当然你不需要这个参数。 但我假设你可能有不止一个实例。 使用({…})块是适用于GCC的GNU主题; 这是一个很好的(安全)的方式来保持线路作为一个块。 如果以后需要向macros中添加更多内容,比如范围validation检查,则不必担心会破坏if / else语句等等。

这就是我要做的,根据你所要求的要求。 像这样的情况是我开始使用python的原因之一; 处理默认参数等变得比以往任何时候都更简单(我想这是一个python插件,对不起;-)

一个gobject使用的模式是一个可变参数函数,枚举每个属性的值。 界面看起来像这样:

 update (ID, 1, BACKUP_ROUTE, 4, -1); /* -1 terminates the parameter list */ 

编写可变参数函数非常简单 – 请参阅http://www.eskimo.com/~scs/cclass/int/sx11b.html 。 只要匹配键 – >值对,并设置适当的结构属性。

因为看起来你只需要这个update()函数的结构,所以根本不要使用这个结构,它只会混淆你构造背后的意图。 你也许应该重新思考为什么你要改变和更新这些字段,并为这个“小”的变化定义单独的函数或macros。

例如

 #define set_current_route(id, route) update(id, dont_care, dont_care, route) #define set_route(id, route) update(id, dont_care, route, dont_care) #define set_backup_route(id, route) update(id, route, dont_care, dont_care) 

或者更好的写一个函数为每个变化的情况。 正如您已经注意到,您不会同时更改每个属性,因此一次只能更改一个属性。 这不仅可以提高可读性,还可以帮助您处理不同的情况,例如,您不必检查所有的“dont_care”,因为您知道只有当前路线发生了变化。

感觉如何?

 struct foo bar; update(init_id(42, init_dont_care(&bar))); 

有:

 struct foo* init_dont_care(struct foo* bar) { bar->id = dont_care; bar->route = dont_care; bar->backup_route = dont_care; bar->current_route = dont_care; return bar; } 

和:

 struct foo* init_id(int id, struct foo* bar) { bar->id = id; return bar; } 

并相应地:

 struct foo* init_route(int route, struct foo* bar); struct foo* init_backup_route(int backup_route, struct foo* bar); struct foo* init_current_route(int current_route, struct foo* bar); 

在C ++中,类似的模式有一个我现在不记得的名字。

编辑 :它被称为命名参数成语 。

我生病了结构,所以我可能在这里错过了几个关键字。 但是,为什么不从初始化默认值的全局结构开始,将其复制到本地variables,然后修改它?

初始化程序如下所示:

 void init_struct( structType * s ) { memcopy(s,&defaultValues,sizeof(structType)); } 

那么当你想使用它:

 structType foo; init_struct( &foo ); // get defaults foo.fieldICareAbout = 1; // modify fields update( &foo ); // pass to function 

你可以用X-Macro来解决这个问题

你会改变你的结构定义为:

 #define LIST_OF_foo_MEMBERS \ X(int,id) \ X(int,route) \ X(int,backup_route) \ X(int,current_route) #define X(type,name) type name; struct foo { LIST_OF_foo_MEMBERS }; #undef X 

然后,您将能够轻松定义一个灵活的function,将所有字段设置为dont_care

 #define X(type,name) in->name = dont_care; void setFooToDontCare(struct foo* in) { LIST_OF_foo_MEMBERS } #undef X 

在这里讨论之后,还可以这样定义一个默认值:

 #define X(name) dont_care, const struct foo foo_DONT_CARE = { LIST_OF_STRUCT_MEMBERS_foo }; #undef X 

其翻译为:

 const struct foo foo_DONT_CARE = {dont_care, dont_care, dont_care, dont_care,}; 

并使用它作为hlovdal答案 , 在这里维护更容易的优势,即改变结构成员的数量将自动更新foo_DONT_CARE 。 请注意, 最后的“虚假”逗号是可以接受的 。

当我必须解决这个问题时,我首先学习了X-Macros的概念。

对于添加到结构中的新字段非常灵活。 如果您有不同的数据types,您可以根据数据types定义不同的dont_care值:从这里 ,您可以从第二个示例中用于打印值的函数获取灵感。

如果您可以使用all int结构,那么您可以省略LIST_OF_foo_MEMBERS的数据types,只需将结构定义的X函数更改为#define X(name) int name;

最优雅的方法是直接更新struct字段,而不必使用update()函数 – 但也许有很好的理由使用它,而不是在问题中遇到。

 struct foo* bar = get_foo_ptr(); foo_ref.id = 42; foo_ref.current_route = new_route; 

或者像Pukkubuild议的那样,可以为结构的每个字段创build单独的访问函数。

否则,我能想到的最好的解决scheme是将结构字段中的值“0”视为“不更新”标志 – 所以您只需创build一个函数来返回零值结构,然后使用它来更新。

 struct foo empty_foo(void) { struct foo bar; bzero(&bar, sizeof (struct bar)); return bar; } struct foo bar = empty_foo(); bar.id=42; bar.current_route = new_route; update(&bar); 

但是,如果0是结构中的字段的有效值,则这可能不是非常可行的。