2017-03-16 2 views
1

Nehmen wir an, Sie schreiben eine Cstruct, die einen Kurs in einer Mahlzeit darstellt. Eines der Felder im Verlauf struct ist vom Typ:Schreiben einer sicheren markierten Union in C

enum TP_course {STARTER, MAINCOURSE, DESSERT}; 

Dann je nach Art des Kurses Sie einen Subtyp haben:

enum TP_starter {SALAD, GRILLEDVEGETABLES, PASTA}; 
enum TP_maincourse {BEEF, LAMB, FISH}; 
enum TP_dessert {APPLEPIE, ICECREAM, MOUSSE}; 

Da nur ein einziger solcher Aufzählungen sein zu einer Zeit verwendet wird (je nach Art des Kurses), macht es Sinn, sie in einem aggregieren union:

union U_subtype { 
    enum TP_starter s; 
    enum TP_maincourse m; 
    enum TP_dessert d; 
}; 

So ist der Kurs struct würde li aussehen diese ke:

struct S_course { 
    enum TP_course type; 
    union U_subtype stype; 
    float price_in_USD; 
    int availability; 
    ...and all the rest of data would follow... 
}; 

Ok, alles klar, aber ... gibt es eine Codierung Strategie, die ich folgen könnte, um zu versuchen sicheren Zugang zur stype getaggt Vereinigung oben zu erzwingen? Vielleicht undurchsichtig machen?

Zum Beispiel, wenn ich einen switch/case Block für einen enum schreiben, und ich vergessen, einen case für einen Wert zu schreiben, wird der Compiler eine Warnung auslösen, die für die Aufrechterhaltung des Codes in der Zukunft eine große Hilfe ist. Aber wenn ich auf stype.s zugreifen, ohne zuerst zu überprüfen, ob type==STARTER, kann der Compiler nicht intelligent genug sein, um die riskante Codierung zu realisieren, und wird überhaupt nicht warnen.

Kann ich den Code in irgendeiner Weise organisieren, so dass es nicht möglich ist, auf die Mitglieder der U_subtype Union zuzugreifen, außer an einem sehr begrenzten Ort, wo ich klar dokumentiere, wie der Zugang zu solchen Mitgliedern gemacht werden muss?

+0

Im Wesentlichen sind alle Union-Mitglieder effektiv "int", und die Werte der verschiedenen Enum-Typen wirken auch wie "int" (ihre Verwendung ist nicht auf einen bestimmten Enum-Typ beschränkt , einfach irgendwo, wo ein 'int' funktionieren würde. Wenn Sie jedoch die Vereinigung mit den drei Aufzählungstypen verwenden, können Sie Ihre Absicht veranschaulichen. – Dmitri

+1

Bitte definieren Sie "sicher". C ist schwach typisiert. Wenn Sie eine Garantie benötigen, sollten Sie eine andere Sprache, z. Python. – Olaf

+3

Die beste Methode besteht darin, Funktionen zum Speichern und Zugreifen auf die Mitglieder zu schreiben und den direkten Zugriff auf die Mitglieder zu vermeiden. Die Funktionen können Gültigkeitsprüfungen durchführen. – Barmar

Antwort

2

Sie können

  1. ausblenden die ganze Struktur und Zugriffsfunktionen verfügbar machen, die

auf einem Zeiger handeln.

/* header */ 
struct S_course; //forward declaration 
enum TP_starter {SALAD, GRILLEDVEGETABLES, PASTA}; 
enum TP_maincourse {BEEF, LAMB, FISH}; 
enum TP_dessert {APPLEPIE, ICECREAM, MOUSSE}; 
void S_course__set_starter(struct S_course *this, enum TP_starter starter); 

//accessor functions 
void S_course__set_maincourse(struct S_course *this, enum TP_maincourse maincourse); 
void S_course__set_dessert(struct S_course *this, enum TP_dessert dessert); 


/* c file */ 
enum TP_course {STARTER, MAINCOURSE, DESSERT}; 
union U_subtype { 
    enum TP_starter s; 
    enum TP_maincourse m; 
    enum TP_dessert d; 
}; 
struct S_course { 
    enum TP_course type; 
    union U_subtype stype; 
    float price_in_USD; 
    int availability; 
    /*...*/ 
}; 
void S_course__set_starter(struct S_course *this, enum TP_starter starter) 
{ 
    this->type = STARTER; 
    this->stype.s = starter; 
} 
  1. Verwenden Mitgliedernamen, die mich nicht berühren schreien kann, oder einen Namen wie tagged_union, die es offensichtlich machen sollte, wie auf sie zugegriffen werden muss.

    oder

  2. Wechseln Sie zu C++ und seine Zugriffssteuerungsfunktionen nutzen (privat/geschützt) verstecken nur einige Mitglieder, während der Zugriff durch öffentliche Mitglied erlaubt/friend-Funktionen

+0

Wie erwarten Sie, dass die C++ - Zugriffskontrolle dieses Problem löst? Alle Mitglieder der Struktur und ihrer Mitgliedsvereinigung müssen unter Umständen zugänglich sein, und soweit ich weiß, ist die C++ - Zugriffssteuerung statisch und nicht datenabhängig. Ähnliches gilt für die Namensgebung. –

+0

@JohnBollinger Indem Sie Mitglieder selektiv ausblenden können. Wenn Sie die problematischen Elemente ausblenden, können Sie nur über die Elementfunktionen darauf zugreifen. In Ebene C ist Verstecken alles oder nichts, AFAIK. – PSkocik

+0

Ok, aber dann scheint der Hauptpunkt in jedem Fall "Accessor Funktionen bereitzustellen und direkten Zugang zu Mitgliedern zu verhindern". Ihre Antwort lautet so, als ob die Alternativen (2) und (3) etwas ganz anderes vorschlagen sollen, als nur unterschiedliche Geschmacksrichtungen des "Direktzugriff verhindern" -Teils zu sein. –

0

Nach viel darüber nachzudenken, Ich wählte einen Ansatz, der als vierte Option zusätzlich zu den anderen drei von PSkocik vorgeschlagen werden könnte: Um die struct Redesign, so dass es keine Art und Subtyp, sondern nur Untertyp ist. Dann wird der Typ nicht von der struct, sondern von einer Hilfsfunktion bereitgestellt.

Etwas wie folgt aus:

enum TP_course {STARTER, MAINCOURSE, DESSERT}; 
enum TP_subtype {SALAD, GRILLEDVEGETABLES, PASTA, 
       BEEF, LAMB, FISH, APPLEPIE, ICECREAM, MOUSSE}; 

struct S_course { 
    enum TP_subtype stype; 
    float price_in_USD; 
    int availability; 
    /*...*/ 
}; 

enum TP_course getCourse(struct S_course *c) { 
switch(c->stype) { 
    case SALAD: 
    case GRILLEDVEGETABLES: 
    case PASTA: 
     return STARTER; 
    case BEEF: 
    case LAMB: 
    case FISH: 
     return MAINCOURSE; 
    case APPLEPIE: 
    case ICE-CREAM: 
    case MOUSSE: 
     return DESSERT; 
    } 
} 

Dieses Design gewährleistet eine sichere Lese-/Schreibzugriff auf den Typ des struct. Es verhindert, dass Sie das struct in undefiniertem Verhalten verlassen (zum Beispiel, indem Sie den Typ auf STARTER setzen, aber vergessen, den Subtyp entsprechend einzustellen), und Sie können auch kein Mitglied lesen (und schreiben), dass es nicht das aktuelle ist.

Ich bevorzuge diese Art von Designs, und ich gebe zu, ich habe diesen Einfluss von den Apple UI Richtlinien: Erstellen Sie ein Design, das verhindert, dass der Benutzer nicht unterstützte/undefined Daten eingeben; Daten niemals an verschiedenen Orten verbreiten, wenn sie sich an einem einzigen Ort befinden können; vermeiden Sie den absurden/illegalen Datenstatus aus dem Design, so dass Sie nicht überprüfen müssen, ob Daten legal sind: das ist es immer; vermeiden Sie Sonderfälle, wenn Sie dasselbe mit einem einzigen allgemeinen Fall tun können; etc, etc, etc ...