CS 228 meeting -*- Outline -*- * variants (HR 5.4-5) ** syntax and examples (5.4) unions often arise in dealing with grammars, where they represent the concept of a nonterminal that is one of a few alterrnatives ----------------------------- C++ unions IMPLEMENT NOTION OF "OR" - holds one of its members (not of all of them) - space cost for the largest member union LiteralType { char c; long int i; double d; char str[100]; }; enum ArticleType {the, a, an}; union PartOfSpeechType { char noun[30]; char verb[30]; ArticleType article; }; ----------------------------- storage allocated according to size of largest member, so LiteralType takes up 100 bytes (not 103), PartOfSpeech type takes up 30 bytes (not 61) (something like EQUIVALENCE in FORTRAN) draw pictures ** client code ------------------ DECLARING union OBJECTS IN CLIENT CODE LiteralType lit1; PartOfSpeechType part1, part2; // cannot use initializer like: PartOfSpeechType art = {the}; // error! ----------------------------- the last is somewhat misleading, but a useful lie you can actually initialize the *first* member of a union with an initializer it may sometimes be very useful to have constructors for unions! -------------------------------- USING union OBJECTS IN CLIENT CODE lit1.i = 7; lit1.d = 3.14159; part1.article = a; for (int i = 0; i < 30; i++) { part2.verb[i] = '\0'; } strcpy(part2.verb, "runs"); if (lit1.d < 4.0) { cout << "lit1.d is less than 4" << endl; } ----------------------------------- *** summary ------------------------------ MANIPULATING union INSTANCES Can do: lit1.i lit2.d = 2.73; lit1 = lit2; // which means: lit1.d = lit2.d; -------------------------------- the latter copies the data members of worker1 into manager (by memberwise assignment) --------------------------------- Cannot do: part1.article() part1 == part2 lit1 + lit2 ------------------------------ you could overload the operators, but these aren't built-in *** example now write this... ----------------------------------- PROBLEM Implement the following specification #include "LiteralType.h" LiteralType ParseLiteral(); // PRE: the literal on cin doesn't // have any escape sequences // MODIFIES: cin // POST: FCTVAL is a literal from cin -------------------------------- type in the following on-line. { LiteralType result; switch (cin.peek()) { case '\'': cin.ignore(1,'\''); result.c = cin.get(); cin.ignore(1,'\''); break; case '"': cin.ignore(1,'"'); int count = 0; // INV: count chars and no '"' have been read and count <= 99 while (count < 99 && cin.peek() != '"') { result.str[count] = cin.get(); count++; } result.str[count] = '\0'; cin.ignore(100,'"'); break; default: cin >> result.d; if (IsInteger(result.d)) { result.i = result.d; } } return result; } note: this doesn't tell the caller which one you saw... but to fix that need to look at: ** syntactic variations on union declarations -------------------------------- SYNTACTIC VARIATIONS Unnamed union types: union { char input[50000]; int output[10000]; } myCharOrInt; myCharOrInt.output[0] = 'a'; -------------------------------- this allows you to save space (:-( but it's not usually worth it, unless it's really big like this ------------------------------- Anonymous union: union { int myInts[10000]; double myDoubles[1000]; }; myInts[3] = 7; ------------------------------- this allows variables to share space (exactly like FORTRAN EQUIVALENCE) Note that in an anonymous union, member names are *not* local to the union, but are part of the surrounding scope! ** variant records (5.5) *** motivation ------------------------------ PROBLEM: UNIONS ARE UNSAFE - C++ prevents: struct MilesType { float value; }; struct KmType { float value; }; MilesType distance = 5.7; KmType length; length = distance; cout << "length is " << length; - C++ allows: union MilesOrKm { MilesType m; KmType km; }; MilesOrKm distance; distance.m.value = 5.7; MilesOrKm length; length.m = distance.m; cout << "length is " << length.km.value << endl; ------------------------------ Imagine you're programming the Scholar system -------------------------------------- PROBLEM: EFFICIENT STORAGE OF MIXED TYPES Suppose want to store 1000 bibliographic citations all: author, title, year books: publisher, address journal: name, volume, number, page FIRST ATTEMPT enum BibTag {book, journal}; struct CitationType { char author[100]; char title[100]; int year; BibTag tag; char publisher[100]; char address[100]; char name[100]; int volume; int number; int page; }; CitationType ScholarDB[1000]; -------------------------------------- draw picture like Figure 5.11 -------------------------------------- 0 1 2 3 4 999 _____________________ _____ ScholarDB |___|___|___|___|___|_ ... |___| author [B|j|a|...] title [C|+|+...] year [1994] tag [book] publisher [A|d|d|...] address [N|e|w|...] name [ | | |...] volume [ ] number [ ] page [ ] author [G|a|r|...] title [S|p|e...] year [1991] tag [journal] publisher [ | | |...] address [ | | |...] name [I|E|E|...] volume [ 8] number [ 4] page [ 72] -------------------------------------- note the great waste in having all hte extra unused storage, also the danger to save storage, use a variant *** solution -------------------------------------- USE OF VARIANTS TO SAVE SPACE struct BookInfo { char author[100]; char title[100]; int year; char publisher[100]; char address[100]; }; struct JournalInfo { char author[100]; char title[100]; int year; char name[100]; int volume; int number; int page; }; enum BibTag {book, journal}; struct CitationType { BibTag tag; union { BookInfo b; JournalInfo j; }; }; CitationType ScholarDB[1000]; -------------------------------------- picture like Figure 5.12 -------------------------------------- 0 1 2 3 4 999 _____________________ _____ ScholarDB |___|___|___|___|___|_ ... |___| tag [book] b|---------------------| |author [B|j|a|...]| |title [C|+|+...] | |year [1994] | |publisher [A|d|d|...]| |address [N|e|w|...]| |---------------------| tag [journal] j|--------------------| |author [G|a|r|...]| |title [S|p|e...] | |year [1991] | |name [I|E|E|...]| |volume [ 8] | |number [ 4] | |page [ 72] | |--------------------| -------------------------------------- *** practice ------------------------------------ FOR YOU TO DO What in C++ do you write to: 1. test what kind of citation the ith element of ScholarDB is? 2. assuming the ith entry is a journal set the volume to 51? 3. assuming the ith entry is a book set the publisher to "Books R Us"? ------------------------------------ *** summary ------------------------------------ VARIANT RECORDS def: a *variant record* is implemented in C++ using a - tag field (also called type field) - and one or more anonymous unions example: enum Type {anInt, aFloat}; struct NumType { Type whichType; union { int intVal; float floatVal; }; }; ------------------------------------ ** an ADT for variants might want to make a class that has safe access to a variant, as in CLU ------------------------------------- PROBLEM How would you design something in C++ that enforced only accessing the right field for a given tag? ------------------------------------- use a class, with constructor for each tag, and get_ and set_ functions for each field. don't want to have preconditions for the get_ and set_ functions, want them to check the tag. trades time for safety