在 C 中评估结构成员访问的函数指针?
Evaluate a Function Pointer on Structure Member Access in C?
我有一个问题要解决,如果我可以让一个结构的成员在访问时评估函数的结果,这个问题基本上就会消失。我认为我从未见过这种行为的任何例子——事实上,我怀疑我正在寻找的东西如果不是一般的编程,就会违反 C 的一些深层规则。如果是这样的话,我当然很乐意听到 evidence/experience 多一点的人来解释原因。
这里有一些简化的代码作为例子:
/* state.c */
#include "state.h"
state_t state_ctx;
/* state.h */
typedef struct _state_t {
foo_t foo;
}state_t;
extern state_t state_ctx;
#define ACCESS_STATE(x) (state_ctx.x)
/* main.c */
const bar_t bar{
.baz = ACCESS_STATE(foo); // Types are compatible
}
在英语中,有一个全局状态变量可以方便地重新定义访问,并且该访问方法在感兴趣的 .c 文件中的全局变量的初始值设定项列表中使用。
该代码有效,但我的任务是允许将上下文从一个状态变量切换到另一个。我可以轻松地将状态定义更改为:
/* state.c */
#include "state.h"
state_t* p_current_state_ctx; // Now a pointer id's the current state structure
/* state.h */
typedef struct _state_t {
baz_t foo;
}state_t;
extern state_t* p_current_state_ctx;
#define ACCESS_STATE(x) (p_current_state_ctx->x)
切换上下文所需要做的就是设置当前状态指针。好的。但是有一个问题——初始化列表需要 ACCESS_STATE(x) 宏来计算一个常量。我认为定义一个像这样的函数很棒:
foo_t func_to_get_foo( void ){
return p_current_state_ctx->foo;
}
因此 main.c
初始化程序可以重写为:
/* main.c */
const bar_t bar{
.baz = (foo_t)&func_to_get_foo; // Trying to get current state's foo
// Obviously this cast isn't generally correct
// and only compiles if the types are pointers
// but still the behavior is wrong
}
因为函数指针是一个常量表达式。但是当我把它写出来时,我的心泄气了,因为我意识到现在 baz
当然只是指向 func_to_get_foo
的指针,而不是像我幻想的那样 foo 的值。
我正在使用的实际程序非常复杂,我仍在学习它的来龙去脉。我想在获得多状态能力的同时尽可能少地进行修改。初始化列表变量有很多实例,例如 bar
示例,因此我宁愿避免为每个实例编写上下文切换代码。
因此,如果有 一些魔法可以导致 func_to_get_foo()
的结果作为访问 bar.baz
的结果出现,我会欣喜若狂。有没有人对如何轻松完成此任务有任何建议?
如果没有办法做到这一点,那么我当然有兴趣听听一些关于为什么的理论......或者它是干的 'thats just not a feature of C?'
最后,如果没有巧妙的技巧,那么 根据当前状态更改这些变量的正确方法是什么?我是否需要编写一个函数来设置每次上下文改变时的每一个?
假设我没看错,bar
是一个全局变量,func_to_get_foo
不是你可以手动折叠的东西。这确实让它变得艰难。事实上,在可移植 c
代码中无法做到这一点。在过去,我们把这些东西放在 main()
的早期,效果很好。
有了 gcc
我们现在可以使用 attribute((constructor))
bar_t bar; /* cannot declare this const as this might place it in readonly memory */
attribute((constructor))
static void init_bar(){
bar.baz = func_to_get_foo();
}
小心;这仅在 state_t state_ctx;
使用 const
初始化器初始化时有效,否则该技术完全不可靠。属性初始值设定项按顺序执行 运行,但这不是您想要的顺序。在第二种情况下,我们必须进一步依赖 gcc
的扩展来复制 c++
的 iostream
魔法,如下所示:
attribute((constructor))
static void init_bar(){
init_state();
bar.baz = func_to_get_foo();
}
/* ... */
state_t state;
static char state_initialized;
attribute((constructor))
void init_state()
{
if (state_initialized) return;
state_initialized = 1;
/* do whatever to fill out state */
}
我有一个问题要解决,如果我可以让一个结构的成员在访问时评估函数的结果,这个问题基本上就会消失。我认为我从未见过这种行为的任何例子——事实上,我怀疑我正在寻找的东西如果不是一般的编程,就会违反 C 的一些深层规则。如果是这样的话,我当然很乐意听到 evidence/experience 多一点的人来解释原因。
这里有一些简化的代码作为例子:
/* state.c */
#include "state.h"
state_t state_ctx;
/* state.h */
typedef struct _state_t {
foo_t foo;
}state_t;
extern state_t state_ctx;
#define ACCESS_STATE(x) (state_ctx.x)
/* main.c */
const bar_t bar{
.baz = ACCESS_STATE(foo); // Types are compatible
}
在英语中,有一个全局状态变量可以方便地重新定义访问,并且该访问方法在感兴趣的 .c 文件中的全局变量的初始值设定项列表中使用。
该代码有效,但我的任务是允许将上下文从一个状态变量切换到另一个。我可以轻松地将状态定义更改为:
/* state.c */
#include "state.h"
state_t* p_current_state_ctx; // Now a pointer id's the current state structure
/* state.h */
typedef struct _state_t {
baz_t foo;
}state_t;
extern state_t* p_current_state_ctx;
#define ACCESS_STATE(x) (p_current_state_ctx->x)
切换上下文所需要做的就是设置当前状态指针。好的。但是有一个问题——初始化列表需要 ACCESS_STATE(x) 宏来计算一个常量。我认为定义一个像这样的函数很棒:
foo_t func_to_get_foo( void ){
return p_current_state_ctx->foo;
}
因此 main.c
初始化程序可以重写为:
/* main.c */
const bar_t bar{
.baz = (foo_t)&func_to_get_foo; // Trying to get current state's foo
// Obviously this cast isn't generally correct
// and only compiles if the types are pointers
// but still the behavior is wrong
}
因为函数指针是一个常量表达式。但是当我把它写出来时,我的心泄气了,因为我意识到现在 baz
当然只是指向 func_to_get_foo
的指针,而不是像我幻想的那样 foo 的值。
我正在使用的实际程序非常复杂,我仍在学习它的来龙去脉。我想在获得多状态能力的同时尽可能少地进行修改。初始化列表变量有很多实例,例如 bar
示例,因此我宁愿避免为每个实例编写上下文切换代码。
因此,如果有 一些魔法可以导致 func_to_get_foo()
的结果作为访问 bar.baz
的结果出现,我会欣喜若狂。有没有人对如何轻松完成此任务有任何建议?
如果没有办法做到这一点,那么我当然有兴趣听听一些关于为什么的理论......或者它是干的 'thats just not a feature of C?'
最后,如果没有巧妙的技巧,那么 根据当前状态更改这些变量的正确方法是什么?我是否需要编写一个函数来设置每次上下文改变时的每一个?
假设我没看错,bar
是一个全局变量,func_to_get_foo
不是你可以手动折叠的东西。这确实让它变得艰难。事实上,在可移植 c
代码中无法做到这一点。在过去,我们把这些东西放在 main()
的早期,效果很好。
有了 gcc
我们现在可以使用 attribute((constructor))
bar_t bar; /* cannot declare this const as this might place it in readonly memory */
attribute((constructor))
static void init_bar(){
bar.baz = func_to_get_foo();
}
小心;这仅在 state_t state_ctx;
使用 const
初始化器初始化时有效,否则该技术完全不可靠。属性初始值设定项按顺序执行 运行,但这不是您想要的顺序。在第二种情况下,我们必须进一步依赖 gcc
的扩展来复制 c++
的 iostream
魔法,如下所示:
attribute((constructor))
static void init_bar(){
init_state();
bar.baz = func_to_get_foo();
}
/* ... */
state_t state;
static char state_initialized;
attribute((constructor))
void init_state()
{
if (state_initialized) return;
state_initialized = 1;
/* do whatever to fill out state */
}