У меня есть такая структура данных:
struct foo { int id; int route; int backup_route; int current_route; }
и функция с именем update (), которая используется для запроса изменений в нем.
обновление (42, dont_care, dont_care, new_route);
это действительно долго, и если я добавлю что-то в структуру, мне придется добавить 'dont_care' к КАЖДОМУ вызову update (...).
Я думаю о том, чтобы передать ему структуру, но заранее заполнить структуру dont_care еще более утомительно, чем просто прописать это в вызове функции. Могу ли я создать структуру где-нибудь со значениями по умолчанию dont care и просто установить поля, которые мне нужны, после того, как я объявлю ее как локальную переменную?
struct foo bar = {.id = 42, .current_route = new_route}; обновить (& бар);
Каков наиболее элегантный способ передать только ту информацию, которую я хочу передать функции обновления?
и я хочу, чтобы все остальное по умолчанию было -1 (секретный код для «не волнует»)
источник
PTHREAD_MUTEX_INITIALIZER
также использует это.Вы можете изменить свое секретное специальное значение на 0 и использовать семантику члена структуры C по умолчанию
struct foo bar = { .id = 42, .current_route = new_route }; update(&bar);
затем передаст 0 как члены bar, не указанные в инициализаторе.
Или вы можете создать макрос, который будет выполнять инициализацию по умолчанию за вас:
#define FOO_INIT(...) { .id = -1, .current_route = -1, .quux = -1, ## __VA_ARGS__ } struct foo bar = FOO_INIT( .id = 42, .current_route = new_route ); update(&bar);
источник
<stdarg.h>
позволяет вам определять функции с переменным числом аргументов (которые принимают неопределенное количество аргументов, напримерprintf()
). Я бы определил функцию, которая принимает произвольное количество пар аргументов, одна из которых определяет свойство, которое нужно обновить, а другая - значение. Используйтеenum
строку или, чтобы указать имя свойства.источник
enum
переменные сstruct
полем? Это жестко закодировать? Тогда эта функция будет применима только к конкретнымstruct
.Возможно, рассмотрите возможность использования вместо этого определения макроса препроцессора:
#define UPDATE_ID(instance, id) ({ (instance)->id= (id); }) #define UPDATE_ROUTE(instance, route) ({ (instance)->route = (route); }) #define UPDATE_BACKUP_ROUTE(instance, route) ({ (instance)->backup_route = (route); }) #define UPDATE_CURRENT_ROUTE(instance, route) ({ (instance)->current_route = (route); })
Если ваш экземпляр (struct foo) является глобальным, то, конечно, вам не нужен этот параметр. Но я предполагаю, что у вас, вероятно, больше одного экземпляра. Использование блока ({...}) - это GNU-изм, который применим к GCC; это хороший (безопасный) способ сохранить строки вместе как блок. Если позже вам потребуется добавить в макросы что-то еще, например, проверку диапазона, вам не придется беспокоиться о нарушении таких функций, как операторы if / else и т. Д.
Я бы так поступил, исходя из указанных вами требований. Подобные ситуации - одна из причин, по которой я начал много использовать Python; обработка параметров по умолчанию, и это становится намного проще, чем когда-либо с C. (я предполагаю, что это плагин python, извините ;-)
источник
Один шаблон, который использует gobject, - это вариативная функция и перечисляемые значения для каждого свойства. Интерфейс выглядит примерно так:
update (ID, 1, BACKUP_ROUTE, 4, -1); /* -1 terminates the parameter list */
Написать функцию varargs просто - см. Http://www.eskimo.com/~scs/cclass/int/sx11b.html . Просто сопоставьте пары ключ -> значение и установите соответствующие атрибуты структуры.
источник
Поскольку похоже, что вам нужна эта структура только для
update()
функции, не используйте структуру для этого вообще, это только запутает ваше намерение, стоящее за этой конструкцией. Возможно, вам стоит переосмыслить, почему вы изменяете и обновляете эти поля, и определять отдельные функции или макросы для этих «маленьких» изменений.например
#define set_current_route(id, route) update(id, dont_care, dont_care, route) #define set_route(id, route) update(id, dont_care, route, dont_care) #define set_backup_route(id, route) update(id, route, dont_care, dont_care)
Или еще лучше написать функцию для каждого случая изменения. Как вы уже заметили, вы не меняете все свойства одновременно, поэтому дайте возможность изменять только одно свойство за раз. Это не только улучшает читаемость, но также помогает справляться с различными случаями, например, вам не нужно проверять все "dont_care", потому что вы знаете, что изменяется только текущий маршрут.
источник
Как насчет чего-то вроде:
struct foo bar; update(init_id(42, init_dont_care(&bar)));
с участием:
struct foo* init_dont_care(struct foo* bar) { bar->id = dont_care; bar->route = dont_care; bar->backup_route = dont_care; bar->current_route = dont_care; return bar; }
а также:
struct foo* init_id(int id, struct foo* bar) { bar->id = id; return bar; }
и соответственно:
struct foo* init_route(int route, struct foo* bar); struct foo* init_backup_route(int backup_route, struct foo* bar); struct foo* init_current_route(int current_route, struct foo* bar);
В C ++ подобный шаблон имеет название, которое я сейчас не помню.
РЕДАКТИРОВАТЬ : это называется идиомой именованных параметров .
источник
У меня проблемы со структурами, поэтому мне, вероятно, не хватает нескольких ключевых слов. Но почему бы не начать с глобальной структуры с инициализированными значениями по умолчанию, скопировать ее в локальную переменную, а затем изменить?
Инициализатор вроде:
void init_struct( structType * s ) { memcopy(s,&defaultValues,sizeof(structType)); }
Затем, когда вы захотите его использовать:
structType foo; init_struct( &foo ); // get defaults foo.fieldICareAbout = 1; // modify fields update( &foo ); // pass to function
источник
Вы можете решить проблему с помощью X-Macro
Вы бы изменили определение своей структуры на:
#define LIST_OF_foo_MEMBERS \ X(int,id) \ X(int,route) \ X(int,backup_route) \ X(int,current_route) #define X(type,name) type name; struct foo { LIST_OF_foo_MEMBERS }; #undef X
И тогда вы сможете легко определить гибкую функцию, которая устанавливает для всех полей значение
dont_care
.#define X(type,name) in->name = dont_care; void setFooToDontCare(struct foo* in) { LIST_OF_foo_MEMBERS } #undef X
После обсуждения здесь можно также определить значение по умолчанию таким образом:
#define X(name) dont_care, const struct foo foo_DONT_CARE = { LIST_OF_STRUCT_MEMBERS_foo }; #undef X
Что означает:
const struct foo foo_DONT_CARE = {dont_care, dont_care, dont_care, dont_care,};
И используйте его, как в ответе hlovdal , с тем преимуществом, что здесь обслуживание проще, т.е. изменение количества членов структуры будет автоматически обновляться
foo_DONT_CARE
. Обратите внимание, что последняя «ложная» запятая допустима .Я впервые познакомился с концепцией X-Macros, когда мне пришлось решать эту проблему. .
Он чрезвычайно гибок для добавления новых полей в структуру. Если у вас разные типы данных, вы можете определить разные
dont_care
значения в зависимости от типа данных: отсюда вы можете почерпнуть вдохновение из функции, используемой для печати значений во втором примере.Если у вас все в порядке со
int
структурой all , вы можете опустить тип данных изLIST_OF_foo_MEMBERS
и просто изменить функцию X определения структуры на#define X(name) int name;
источник
X
и обычно добавляемые_ENTRY
к имени списка, например#define LIST_SOMETHING LIST_SOMETHING_ENTRY(a1, a2, aN) \ LIST_SOMETHING_ENTRY(b1, b2, bN) ...
.Самый элегантный способ - обновить поля структуры напрямую, без использования
update()
функции, но, возможно, есть веские причины для ее использования, которые не встречаются в вопросе.struct foo* bar = get_foo_ptr(); foo_ref.id = 42; foo_ref.current_route = new_route;
Или вы можете, как предложил Пукку, создать отдельные функции доступа для каждого поля структуры.
В противном случае лучшим решением, которое я могу придумать, является обработка значения «0» в поле структуры как флага «не обновлять» - поэтому вы просто создаете функцию для возврата обнуленной структуры, а затем используете ее для обновления.
struct foo empty_foo(void) { struct foo bar; bzero(&bar, sizeof (struct bar)); return bar; } struct foo bar = empty_foo(); bar.id=42; bar.current_route = new_route; update(&bar);
Однако это может быть неосуществимо, если 0 является допустимым значением для полей в структуре.
источник