Один вариант, который еще не упомянут, - это создание дескрипторов для полей с использованием макроса offsetof()
из <stddef.h>
. Затем вы можете передать соответствующие дескрипторы функциям модификации.
typedef struct string_descriptor
{
size_t offset;
size_t length;
} string_descriptor;
В C99 вы можете делать такие вещи, как:
int update_string(void *structure, const string_descriptor d, const char *new_val)
{
char *buffer = (char *)structure + d.offset;
size_t len = strlen(new_val);
size_t nbytes = MIN(len, d.length);
// Optionally validate lengths, failing if the new value won't fit, etc
memcpy(buffer, new_val, nbytes);
buffer[nbytes] = '\0';
return(0); // Success
}
void some_function(void)
{
ADDR_A a;
ADDR_B b;
static const string_descriptor a_des =
{ .offset = offsetof(ADDR_A, street), .length = sizeof(a.street) };
static const string_descriptor b_des =
{ .offset = offsetof(ADDR_B, street), .length = sizeof(b.street) };
update_string(&a, a_des, "Regent St."); // Check return status for error!
update_string(&b, b_des, "Oxford St."); // Check return status for error!
}
Очевидно, что для одного типа это выглядит неуклюже, но при тщательном обобщении вы можете использовать несколько дескрипторов, и дескрипторы могут быть больше (более сложными), но передаваться по ссылке, и должны быть определены только один раз и т. Д. дескрипторы, та же самая функция update_string()
может быть использована для обновления любого из полей в любой из двух структур. Хитрый бит - инициализатор длины sizeof(b.street)
; Я думаю, что для предоставления нужной информации требуется переменная правильного типа, хотя я был бы рад принять (Стандарт C99) ревизию, чтобы снять это ограничение. Не минимальные обобщенные дескрипторы могут содержать индикатор типа, имя члена и другую информацию, которая может иметь отношение к делу - я придерживался очень простой структуры, несмотря на соблазны разработки. Вы можете использовать указатели функций для предоставления разных проверок для разных строк символов - проверка для широты и долготы очень отличается от проверки для названия улицы.
Вы также можете написать функции, которые принимают массивы дескрипторов и выполняют несколько обновлений. Например, вы можете иметь массив дескрипторов для полей в ADDR_A и другой для ADDR_B, а затем передавать новые значения для полей в массиве новых строк и т. Д.
На вас лежит ответственность за то, чтобы получить правильные дескрипторы и использовать правильные дескрипторы.
См. Также: Какова цель и тип возврата оператора __builtin_offsetof?