【问题标题】:Evaluate a Function Pointer on Structure Member Access in C?评估 C 中结构成员访问的函数指针?
【发布时间】:2019-08-01 07:07:22
【问题描述】:

我有一个问题要解决,如果我可以让结构的成员在访问时对函数的结果进行评估,那么它基本上就会消失。我认为我从未见过这种行为的任何例子——事实上,我怀疑我正在寻找的东西如果不是一般编程的话,会违反 C 的一些深层规则。如果是这样的话,我当然会很高兴听到有更多证据/经验的人来解释原因。

以下是一些简化的代码示例:

/* 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 的结果,我会欣喜若狂。有人对如何轻松完成此任务有任何建议吗?

如果没有办法做到这一点,那么我当然很想听听一些关于为什么的理论......或者它是干脆的“这不是 C 的一个特性?”

最后,如果没有聪明的技巧,那么改变这些依赖于当前状态的变量的正确方法是什么?我是否需要编写一个函数来设置每一个上下文变化的时间?

【问题讨论】:

  • 您似乎选择了错误的语言。试试 C++。
  • 定义func_to_get_foo 来接收state_t* 参数有什么问题? func_to_get_foo(&bar.baz)

标签: c patch theory


【解决方案1】:

假设我正确地遵循了这一点,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 */
}

【讨论】:

  • 有趣。您还可以将大于 100 (101, 102, ...) 的优先级分配给构造函数/析构函数序列(gcc 保留 100 及以下)。 Linux 上有一个稍微激进的elf_init hack,它允许您使用内联汇编将初始化函数的名称直接写入可执行文件的.init 部分,该汇编在所有其他构造函数之前调用,这将是一个很好的地方确保全局已初始化——但这将更不便携。
  • 如果 I 理解正确,attribute((constructor)) 会向编译器发出信号以执行初始化列表中的函数?也许有一天我会找到一个使用它的地方......我有点期待你说在便携式 C 中这是不可能的。无论如何,这肯定是一些很好的东西——感谢分享!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-22
  • 1970-01-01
  • 1970-01-01
  • 2010-11-23
  • 1970-01-01
  • 2021-12-17
  • 1970-01-01
相关资源
最近更新 更多