What's the best/a good way to access a variable member of an array of structs?

Discussion in 'C++' started by sherifffruitfly, Apr 21, 2006.

  1. Hi,

    I'm just learning cpp, and the exercise I'm working on is basically as
    follows:

    1) Create a struct type with 4 members (char, char, char, int).
    2) Create an array of, say 3 instances of the struct, and populate them
    with data.
    3) cin 1, 2, 3, or 4 from the user
    4) If the user selected, say, 2, display the contents of the 2nd data
    member from each of the structs in the array. Similarly for 1,3, and 4.

    I'm trying to find an elegant way to achieve 4).

    It's easy to achieve the functionality with a set of ifs/for loops (if
    input=1, then for loop thru the struct array, cout <<
    stuctInstance.FirstMember ....)

    But this seems grossly inelegant, as I'm essentially writing the same
    for-loop code 4 times - once for each input possibility. I would like
    NO ifs, and just ONE for loop, whose key statement intuitively looks
    like:

    cout << structInstance.whicheverMemberTheUserSelected

    Does it make sense what I'm looking to do? Is there a standard/elegant
    way to achieve this? If I'm not making sense, I'll just post the
    "inelegant" solution I have...


    tia!

    cdj
     
    sherifffruitfly, Apr 21, 2006
    #1
    1. Advertisements

  2. sherifffruitfly

    Pavel Guest



    Is this similar to what you want?
    Pavel

    #include <iostream>
    #include <sstream>
    using namespace std;

    struct S { char a, b, c; int d; };
    static S s_;

    string formatChar(void *p) {
    char a[] = "c";
    *a = *(char *)p;
    return a;
    }
    string formatInt(void *p) {
    ostringstream os;
    os << *(int*)p;
    return os.str();
    }

    typedef string (*Formatter)(void *);
    struct FormatMeta {
    int fieldOff;
    Formatter formatter;
    };

    static FormatMeta SFM[] = {
    { (char*)(&s_.a) - (char*)(&s_), formatChar },
    { (char*)(&s_.b) - (char*)(&s_), formatChar },
    { (char*)(&s_.c) - (char*)(&s_), formatChar },
    { (char*)(&s_.d) - (char*)(&s_), formatInt }
    };

    #define ARR_SIZE(a) (sizeof(a)/sizeof((a)[0]))

    int main(int argc, char *argv[]) {
    cout << "Enter an integer: =>" << flush;
    int i;
    if(!(cin >> i).good() || i < 0 || i >= ARR_SIZE(SFM)) {
    cout << "Sorry\n";
    return 1;
    }
    S sa[] = { {'a', 'b', 'c', 1}, {'d', 'e', 'f', 2} };
    for(int ii=0; ii < ARR_SIZE(sa); ++ii) {
    cout << (SFM.formatter)((char*)(&sa[ii]) + SFM.fieldOff)
    << endl;
    }
    return 0;
    }
     
    Pavel, Apr 22, 2006
    #2
    1. Advertisements

  3. sherifffruitfly

    Pavel Guest

    Sorry, I meant to answer cdj, of course..

    Pavel wrote:

     
    Pavel, Apr 22, 2006
    #3
  4. sherifffruitfly

    Phlip Guest

    Those offsets are either undefined behavior or darn close. If you use the
    offsetof() macro, then their behavior is defined.
     
    Phlip, Apr 22, 2006
    #4
  5. sherifffruitfly

    Phlip Guest

    static FormatMeta SFM[] = {
    Let me rephrase.

    Those offsets are defined behavior because they rely on a static instance of
    S. If we want to save one S instance, we would need to use a pointer to S to
    find those offsets. Such a pointer, pointing to no S instance, would cause
    undefined behavior, even when the expression apparently resolves at compile
    time. The offsetof() macro is defined to hide these issues, relying on
    secretly implementation-defined behavior.
     
    Phlip, Apr 22, 2006
    #5
  6. sherifffruitfly

    Pavel Guest

    I am not sure I understand the "saving structure" problem you referred to.

    The behavior of my code is well defined. As for the offsetof, I could
    (and, probably, should) have used it -- but my way is not worse for the
    particular case, just less universal (which was not a requirement). BTW,
    offsetof must be implemented somehow. gcc defines it like

    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

    This "dereferencing" of NULL (when -> operator possibly "evaluates")
    seems much less "defined" to me than my code -- but apparently the HOLY
    STANDARD believes it is possible to somehow define it, so that the
    behavior is at most "unspecified" but not "undefined" in its own terms
    (I am not sure gcc stays on 'unspecified' ground here). So, yes, I
    wasted a static structure to make the behavior "better defined". I do
    not know how to *efficiently* define offsetof with the well-defined
    behavior, but for any specific struct it is easy to do it as I did.
     
    Pavel, Apr 23, 2006
    #6
  7. Thanks for the replies everyone!

    Um, did I mention that *I'm just learning* cpp? lolol

    I don't know if Pavel's code is the sort of thing I'm looking for,
    since I don't understand it - lol. I'll see if I can adapt it to my
    exercise, and let ya know!

    Here's the 3rd grade level solution I came up with (from code file -
    not main() - i think it has all the essentials of my question in it
    tho.... Is it clear what I originally said about not liking all of the
    for loops that do basically the same thing, just with a different piece
    of the struct?

    thanks again!

    cdj
    ==========
    #include <iostream>

    using namespace std;

    int displayByName();
    int displayByTitle();
    int displayByBOPName();
    int displayByPref();

    const int STRSIZE=40;
    const int NUM_BOPS=3;

    struct bop {
    char fullname[STRSIZE];
    char title[STRSIZE];
    char bopname[STRSIZE];
    int preference; //0=fullname, 1=title, 2=bopname
    };

    bop myBops[NUM_BOPS] = {
    {"chris", "master", "bopmaster", 0},
    {"josh", "dork", "bopdork", -1},
    {"paul", "friend", "bopfriend", 2},
    };

    void displayBy(int int_X)
    {
    //bop struct looks like: bopName(char, char, char, int)
    //preference member map: 0=fullname, 1=title, 2=bopname
    cout << endl;

    switch (int_X) {
    case 1: displayByName(); break;
    case 2: displayByTitle(); break;
    case 3: displayByBOPName(); break;
    case 4: displayByPref(); break;
    default: cout << "Invalid selection\n"; break;
    }
    }

    int displayByName() {
    for (int i=0; i<3; i++) {
    cout << "user #" << i << ": " << myBops.fullname << endl;
    }
    return 0;
    }

    int displayByTitle() {
    for (int i=0; i<3; i++) {
    cout << "user #" << i << ": " << myBops.title << endl;
    }

    return 0;
    }

    int displayByBOPName() {
    for (int i=0; i<3; i++) {
    cout << "user #" << i << ": " << myBops.bopname << endl;
    }

    return 0;
    }

    int displayByPref() {
    for (int i=0; i<3; i++) {
    switch (myBops.preference) {
    case 0: cout << "user #" << i << ": " << " Name: " <<
    myBops.fullname << endl; break;
    case 1: cout << "user #" << i << ": " << " Title: " <<
    myBops.title << endl; break;
    case 2: cout << "user #" << i << ": " << " BOP Name: " <<
    myBops.bopname << endl; break;

    default: cout << "user #" << i << ": " << " No valid preference
    available" << endl; break;
    }
    }

    return 0;
    }
     
    sherifffruitfly, Apr 23, 2006
    #7
  8. * Pavel:
    Please don't top-post in this group (corrected) -- FAQ.


    * Pavel:
    [snip]
    However an implementation defines offsetof, it's a way that works with
    that implementation -- but not necessarily with others.

    A standard library implemementation isn't, in general, portable.

    It's the standard library specification that's portable, so that code
    that relies on that specification can be portable.
     
    Alf P. Steinbach, Apr 23, 2006
    #8
  9. sherifffruitfly

    Pavel Guest

    wrote:
    Shame on me. I was always sure my code is the most readable in the world.

    If you are interested in working further with your code, think of using
    a pointer to the structure as an argument to your display.. functions.
    This could save you one switch (because then you could implement display
    by pref in terms of displayBy()).

    Good luck.
    Pavel
     
    Pavel, Apr 23, 2006
    #9
  10. The clearest Chinese in the world is gobbledygook to someone who only
    speaks English... :)

    It's my lack of language understanding - not your lack of writing
    skills.

    Thanks to everyone - you've given me plenty to gnaw on!

    cdj
     
    sherifffruitfly, Apr 23, 2006
    #10
  11. sherifffruitfly

    Phlip Guest

    Please don't top-quote. Mixing your comments with the replied-to text makes
    each post legible.

    Pavel wrote in message
    Sorry - I said "save" when I meant "reduce the program's footprint by the
    size of one structure." So it's the same argument as yours - how to get rid
    of the unused static object. No biggie. If this were my program and I needed
    one more unused static object, I would just leave it there and I would not
    prematurely optimize.
    Because they are allowed to write it inside that macro, and you are not
    allowed to write it outside.

    The distinction is simple. GNU will test gcc and support that macro,
    regardless where they port gcc. However, if you rely on unspecified
    behavior, then it may switch to undefined behavior when GNU changes its
    compiler and changes the contents of that macro.
    I suspect your version is perfectly efficient - a hard constant. You can
    size an array with it and see.
     
    Phlip, Apr 23, 2006
    #11
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.