Однако, поскольку безопасность критически важна, изменение является дорогостоящим.Поэтому, не желая исправлять 'wot aint сломано', я открыт для других аргументов.
Парадоксальная смертельная спираль в том, что самый критический код привлекает наименьшее внимание, потому что люди боятся его менять.
То, что вы не решаетесь сделать простой рефакторинг для этого кода, говорит мне, что в коде либо нет тестов, либо вы не доверяете тестам.Когда вы боитесь улучшить код, потому что можете его сломать, это задерживает улучшения в коде.Скорее всего, вы сделаете наименьшую возможную вещь, которая сделает код еще более хрупким и небезопасным.
Я бы посоветовал первым сделать несколько тестов вместе с промежуточной средой для испытаний.Тогда все изменения станут безопаснее.Сначала могут быть некоторые проблемы, когда вы обнаружите все странные и опасные вещи, которые делает этот код, но для этого и нужна область подготовки.В среднесрочной и долгосрочной перспективе каждый будет улучшать этот код быстрее и с большей уверенностью.Делая код проще и безопаснее для изменения, можно сделать его проще и безопаснее для изменения;затем спираль идет вверх, а не вниз.
Техника создания макроса, похожего на одну переменную, - это техника, которую я видел раньше в базе кода Perl 5.Он написан больше на макросах C, чем на C. Например, здесь немного манипулирует стеком вызовов Perl .
#define SP sp
#define MARK mark
#define TARG targ
#define PUSHMARK(p) \
STMT_START { \
I32 * mark_stack_entry; \
if (UNLIKELY((mark_stack_entry = ++PL_markstack_ptr) \
== PL_markstack_max)) \
mark_stack_entry = markstack_grow(); \
*mark_stack_entry = (I32)((p) - PL_stack_base); \
DEBUG_s(DEBUG_v(PerlIO_printf(Perl_debug_log, \
"MARK push %p %" IVdf "\n", \
PL_markstack_ptr, (IV)*mark_stack_entry))); \
} STMT_END
#define TOPMARK S_TOPMARK(aTHX)
#define POPMARK S_POPMARK(aTHX)
#define INCMARK \
STMT_START { \
DEBUG_s(DEBUG_v(PerlIO_printf(Perl_debug_log, \
"MARK inc %p %" IVdf "\n", \
(PL_markstack_ptr+1), (IV)*(PL_markstack_ptr+1)))); \
PL_markstack_ptr++; \
} STMT_END
#define dSP SV **sp = PL_stack_sp
#define djSP dSP
#define dMARK SV **mark = PL_stack_base + POPMARK
#define dORIGMARK const I32 origmark = (I32)(mark - PL_stack_base)
#define ORIGMARK (PL_stack_base + origmark)
#define SPAGAIN sp = PL_stack_sp
#define MSPAGAIN STMT_START { sp = PL_stack_sp; mark = ORIGMARK; } STMT_END
#define GETTARGETSTACKED targ = (PL_op->op_flags & OPf_STACKED ? POPs : PAD_SV(PL_op->op_targ))
#define dTARGETSTACKED SV * GETTARGETSTACKED
Это макросы на макросах на макросах.Источник Perl 5 изобилует ими.Там происходит много непрозрачной магии.Некоторые из них должны быть макросами, чтобы разрешить присваивание, но многие могут быть встроенными функциями.Несмотря на то, что они являются частью общедоступного API , они документально оформлены отчасти потому, что они являются макросами, а не функциями.
Этот стиль очень умный и полезный , если вы уже очень хорошо знакомыс исходным кодом Perl 5 .Для всех остальных это сделало работу с внутренностями Perl 5 чрезвычайно сложной.В то время как некоторые компиляторы предоставляют трассировки стека для расширений макросов, другие сообщают только о развернутом макросе, оставляя одного поцарапанного в голове, что, черт возьми, const I32 origmark = (I32)(mark - PL_stack_base)
, потому что он никогда не появляется в вашем источнике.
Как и многие макро-хаки,в то время как техника очень умна, она также сногсшибательна и незнакома многим программистам. Сгибание разума - это не то, что вам нужно в критическом коде безопасности. Вам нужен простой, скучный код.Одно это - самый простой аргумент, чтобы заменить его хорошо именованными функциями получения и установки.Доверьтесь компилятору, чтобы оптимизировать их.
Хорошим примером этого является GLib, которая тщательно использует хорошо документированные функционально-подобные макросы для создания общих структур данных.Например, добавление значения в массив .
#define g_array_append_val(a,v)
Хотя это макрос, он действует и документируется как функция.Это макрос исключительно как механизм для создания безопасного типа универсального массива.Он не скрывает никаких переменных.Вы можете безопасно использовать его, даже не зная, что это макрос.
В заключение, да, измените его.Но вместо простой замены X_myvar
на inst.a.a
рассмотрите возможность создания функций, которые продолжают обеспечивать инкапсуляцию.
void s1_set_a( s1 *s, int val ) {
s->a.a = val;
}
int s1_get_a( s1 *s ) {
return s->a.a;
}
s1_set_a(&inst, 10);
printf("myvar = %d\n", s1_get_a(&inst) + 1);
Внутренние элементы s1
скрыты, что упрощает последующее изменение внутренних элементов (например,изменение s1.a
на указатель для экономии памяти).С какой переменной вы работаете, становится понятным весь код.Имена функций дают четкое объяснение того, что происходит.Поскольку они являются функциями, у них есть очевидное место для документации.Доверьтесь компилятору, чтобы узнать, как лучше его оптимизировать.