Оценить указатель функции на доступ к элементу структуры в C? - PullRequest
0 голосов
/ 11 марта 2019

У меня есть проблема, которую нужно решить, которая, по сути, просто исчезла бы, если бы при доступе к элементу структуры можно было вычислить результат функции. Я не думаю, что когда-либо видел примеры такого поведения - на самом деле у меня есть подозрение, что то, что я ищу, нарушило бы некоторые глубокие правила 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?

И, наконец, если нет хитрого трюка, то как правильно изменить эти переменные, которые зависят от текущего состояния? Нужно ли мне писать функцию, которая каждый раз устанавливает каждый контекст меняется?

1 Ответ

0 голосов
/ 11 марта 2019

Если я правильно понимаю, 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 */
}
...