在 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 */
}