Standardní hodnoty v C Struct

hlasů
88

Mám datovou strukturu, jako je tento:

    struct foo {
        int id;
        int trasa;
        int backup_route;
        int current_route;
    }

a funkce nazvaná aktualizaci (), který se používá k požadovat změny v něm.

  aktualizace (42, dont_care, dont_care, new_route);

je to opravdu dlouho, a když jsem něco přidat do struktury musím přidat ‚dont_care‘ pro každou výzvu k aktualizaci (...).

Přemýšlím o absolvování jej struct místo, ale vyplnění struct s ‚dont_care‘ předem je ještě více únavné než jen hláskování ve volání funkce. Mohu vytvořit struct někde s výchozími hodnotami dont péče a jen nastavit pole Záleží mi poté, co jsem deklarovat jako lokální proměnné?

    struct foo bar = {.id = 42, .current_route = new_route};
    Aktualizace (a bar);

Jaký je nejelegantnější způsob, jak předat jen informace, které jsem chtěl vyjádřit k aktualizační funkcí?

A já chci všechno ostatní na výchozí -1 (tajného kódu ‚dont péče‘)

Položena 14/04/2009 v 21:13
zdroj uživatelem
V jiných jazycích...                            


10 odpovědí

hlasů
149

Zatímco makra a / nebo funkce (jak již bylo navrženo) bude pracovat (a může mít další pozitivní účinky (tj ladící háky)), které jsou složitější, než je nutné. Nejjednodušší a možná nejelegantnější řešení je jen definovat konstanta, která používáte pro variabilní inicializace:

const struct foo FOO_DONT_CARE = { // or maybe FOO_DEFAULT or something
    dont_care, dont_care, dont_care, dont_care
};
...
struct foo bar = FOO_DONT_CARE;
bar.id = 42;
bar.current_route = new_route;
update(&bar);

Tento kód nemá prakticky žádnou duševní režii pochopení indirection, a to je velmi jasné, která pole v barnastavení explicitně while (bezpečně) ignorují ty, které nenastavíte.

Odpovězeno 15/04/2009 v 00:15
zdroj uživatelem

hlasů
15

Můžete změnit svůj tajný speciální hodnotu 0 a využívat standardní struktury a členské sémantiku c je

struct foo bar = { .id = 42, .current_route = new_route };
update(&bar);

pak bude předávat 0 jako členové baru nespecifikované v inicializátor.

Nebo si můžete vytvořit makro, které bude dělat výchozí inicializaci pro vás:

#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);
Odpovězeno 14/04/2009 v 21:38
zdroj uživatelem

hlasů
7

<stdarg.h>umožňuje definovat variadic funkce (které přijímají neurčitý počet argumentů, jako je printf()). Já bych definovat funkci, která vzala libovolný počet párů argumentů, ten, který určuje vlastnost být aktualizována, a jeden, který určuje hodnotu. Použijte enumnebo řetězec zadat název objektu.

Odpovězeno 14/04/2009 v 21:22
zdroj uživatelem

hlasů
5

Třeba zvážit použití definice preprocesoru makro namísto:

#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); })

Pokud váš případ (struct foo) je globální, pak nemusíte parametr pro to samozřejmě. Ale já jsem za předpokladu, že budete pravděpodobně mít více než jednu instanci. Pomocí bloku ({...}) je GNU-ismus, že se vztahuje na GCC; to je hezké (bezpečné) způsob, jak udržet vedení dohromady jako blok. Pokud později je třeba přidat více maker, jako je například kontrola validace rozsah, nebudete muset starat o lámání věci, jako je if / else prohlášení a tak dále.

To je to, co bych udělal, založené na požadavcích jste uvedli. Situace, jako je tato, jsou jedním z důvodů, proč jsem začal používat Python hodně; manipulaci s výchozí parametry a tak se stává mnohem jednodušší, než kdy jindy je pomocí C. (myslím, že je to python zástrčka, omlouvám ;-)

Odpovězeno 14/04/2009 v 22:10
zdroj uživatelem

hlasů
4

Vzhledem k tomu, vypadá to, že budete potřebovat pouze tuto strukturu pro update()funkci nepoužívají strukturu pro toto vůbec, bude to poplést pouze svůj záměr za tímto konstruktem. Možná byste měli přehodnotit, proč se mění a aktualizaci těchto polí a definovat samostatná funkce nebo makra pro toto „malé“ změny.

např


#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)

Nebo ještě lépe napsat funkci pro každý případ změn. Jak jste si již všimli, nechcete měnit každou vlastnost ve stejné době, takže aby bylo možné měnit pouze jednu vlastnost najednou. To není jen zlepšit čitelnost, ale také vám pomůže manipulaci s různými případy, např nemusíte kontrolovat všechny „dont_care“, protože víte, že pouze současná cesta se změní.

Odpovězeno 15/04/2009 v 02:12
zdroj uživatelem

hlasů
4

Jeden vzorek GObject používá je variadic funkci a vypočtené hodnoty pro každou vlastnost. Rozhraní vypadá asi takto:

update (ID, 1,
        BACKUP_ROUTE, 4,
        -1); /* -1 terminates the parameter list */

Psaní funkci varargs je snadné - viz http://www.eskimo.com/~scs/cclass/int/sx11b.html . Jen zápas až klíč -> párů hodnot a nastavit správné atributy strukturu.

Odpovězeno 14/04/2009 v 21:25
zdroj uživatelem

hlasů
4

Jak se něco takového:

struct foo bar;
update(init_id(42, init_dont_care(&bar)));

s:

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;
}

a:

struct foo* init_id(int id, struct foo* bar) {
  bar->id = id;
  return bar;
}

a odpovídajícím způsobem:

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);

V jazyce C ++, podobný model má jméno, které nepamatuji si právě teď.

EDIT : Říká se tomu pojmenovaný parametr Idiom .

Odpovězeno 14/04/2009 v 21:21
zdroj uživatelem

hlasů
2

Dalo by se řešit problém s X-makro

Ty by změnit definici struct na:

#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

A pak byste být schopni snadno definovat pružnou funkci, která nastaví všechny pole dont_care.

#define X(type,name) in->name = dont_care;    
void setFooToDontCare(struct foo* in) {
    LIST_OF_foo_MEMBERS 
}
#undef X

V návaznosti na diskusi tady by se dalo také definovat výchozí hodnotu tímto způsobem:

#define X(name) dont_care,
const struct foo foo_DONT_CARE = { LIST_OF_STRUCT_MEMBERS_foo };
#undef X

Která se promítá do:

const struct foo foo_DONT_CARE = {dont_care, dont_care, dont_care, dont_care,};

A použít jej jako v hlovdal odpověď , s tou výhodou, že zde údržba je jednodušší, tj změnou počtu struct členů bude automaticky aktualizovatfoo_DONT_CARE . Všimněte si, že poslední „falešný“ čárka je přijatelné .

Poprvé jsem se naučil koncept X-maker, kdy jsem musel řešit tento problém .

Je velmi flexibilní, aby nová pole byly přidány do struct. Pokud máte různé typy dat, můžete definovat různé dont_carehodnoty v závislosti na typu dat: od tady , mohl si vzít inspiraci z funkce slouží k tisku hodnot v druhém příkladu.

Pokud jste v pořádku s all intstruct, pak byste mohli vynechat typ dat z LIST_OF_foo_MEMBERSa jednoduše změnit funkci X definice struct do#define X(name) int name;

Odpovězeno 12/04/2017 v 13:43
zdroj uživatelem

hlasů
2

Jsem rezavé s structs, takže jsem pravděpodobně chybí několik klíčových slov zde. Ale proč nezačít s globální struktury s inicializaci výchozí hodnoty, zkopírovat do vaší lokální proměnné, pak ji upravit?

Inicializátor jako:

void init_struct( structType * s )
{
   memcopy(s,&defaultValues,sizeof(structType));
}

Pak, když ji chcete použít:

structType foo;
init_struct( &foo ); // get defaults
foo.fieldICareAbout = 1; // modify fields
update( &foo ); // pass to function
Odpovězeno 14/04/2009 v 21:38
zdroj uživatelem

hlasů
1

Nejelegantnější způsob, jak by se aktualizovat struct pole přímo, aniž by bylo nutné použít update()funkci - ale možná existují dobré důvody pro jeho použití, které nejsou narazit v otázce.

struct foo* bar = get_foo_ptr();
foo_ref.id = 42;
foo_ref.current_route = new_route;

Nebo můžete, stejně jako Pukku navrhl vytvoření samostatné funkce přístupu pro každé pole struct.

V opačném případě je nejlepším řešením můžu myslet léčí hodnotu ‚0‘ v struct oboru jako ‚neaktualizují‘ vlajka - takže stačí vytvořit funciton vrátit se vynuluje struct, a pak použít tuto aktualizaci.

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);

Nicméně, mohlo by to nemělo být velmi možné, pokud 0 je platná hodnota pro pole v struct.

Odpovězeno 14/04/2009 v 21:22
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more