2

extern "C" {
    typedef struct Pair_s {
        char *first;
        char *second;
    } Pair;

    typedef struct PairOfPairs_s {
        Pair *first;
        Pair *second;
    } PairOfPairs;
}

Pair pairs[] = {
    {"foo", "bar"}, //this is fine
    {"bar", "baz"}
}; 

PairOfPairs pops[] = {
    {{"foo", "bar"}, {"bar", "baz"}}, //How can i create an equivalent of this NEATLY
    {&pairs[0], &pairs[1]} //this is not considered neat (imagine trying to read a list of 30 of these)
}; 

How can I achieve the above style declaration semantics?

chacham15
  • 13,719
  • 26
  • 104
  • 207
  • 2
    In C++, you would use `std::string` and not `char*`. However, what is the relationship between these "pairs"? – Jesse Good Jun 04 '12 at 22:45
  • @JesseGood its c++ code that interfaces with c code, so it uses `char*`'s. The pair is a key/value and pair of pairs is a small set. Therefore, no relationship exists between them other than what is declared which is why the `&pairs[0], &pairs[1]` notation would be impossible to read. – chacham15 Jun 04 '12 at 22:49
  • 2
    You can use `std::string` and still interface with C code. Just call `c_str()` on the string to get a char pointer you can pass to the C code. – jalf Jun 04 '12 at 22:52
  • @jalf except that these structs were declared in c not c++. Im just declaring the data in c++ code. How is that relevant to the question anyways? – chacham15 Jun 04 '12 at 23:01
  • This may be a dumb question, but which isn't PairOfPairs just two Pairs instead of two Pair*s? Unless you really need to share the same actual Pair objects across multiple PairOfPairs objects (so if you mutate one, all the others also change), this is just extra code complexity, memory use, and CPU work for no benefit. – abarnert Jun 05 '12 at 00:42
  • @abarnert you assume that the pairs arent going to be changed at all – chacham15 Jun 05 '12 at 01:08
  • @chacham15: No I don't. Immutability does seem the most likely possibility, given that you're trying to, in effect, create Pair literals here. But there's nothing stopping you from changing the Pair members of a non-pointer-based PairOfPairs. And if you need to have multiple instances of a Pair all sharing identity so they mutate together, I specifically referred to that in my comment. – abarnert Jun 05 '12 at 01:33
  • @abarnert you also assume that a `NULL` pair wont exist. Its also important to note that a `NULL` pair is distinctly different than a pair with two `NULL` values. – chacham15 Jun 05 '12 at 18:57
  • @chacham15: What's the "also" for? I didn't assume they were immutable, so "also" in addition to what? And I was not assuming anything. The Pairs could be pointers because they need to share identity, or because they need to be nullable, or because you copied and pasted code without thinking, or for some other reason I haven't thought of. Since I didn't know, and didn't want to assume, I asked a question—which I specifically said might be a dumb question. There's nothing for you to get defensive about here. – abarnert Jun 05 '12 at 19:37
  • @abarnert Please dont get defensive yourself as I was merely listing reasons why the two scenario's arent equal. I wrote "also" because there are two differences: 1. if I change the values, all locations where that value is referenced also change and 2. a pair can be `NULL`. Therefore, it isnt "extra code complexity, memory use, and CPU work for no benefit." – chacham15 Jun 06 '12 at 06:25

2 Answers2

5

In C++11 you could write:

PairOfPairs pops[] = {
    { new Pair{"a", "A"}, new Pair{"b", "B"} },
    { new Pair{"c", "C"}, new Pair{"d", "D"} },
    // the grouping braces are optional
};

Do note the implications of using the free store: objects allocated there are not destructed at the end of the execution of your program (like static objects are) or anytime else (without a corresponding delete). Probably not a concern in hosted implementations if Pair is a C struct and does not manage resources (and you always expected your program to use that memory until it exits).

edit: If you can't use C++11 features, you can always create a helper function. Example:

static Pair* new_pair(const char* first, const char* second)
{
    Pair* pair = new Pair;
    pair->first = first;
    pair->second = second;
    return pair;
}
eq-
  • 9,986
  • 36
  • 38
  • whats a few kb of wasted memory (which actually, will be in use until exit anyways)? the only concern is C++11, is there a way for earlier versions of g++? – chacham15 Jun 04 '12 at 23:55
  • @chacham15 You can always make a free helper function to construct POD types in pre-C++11. See edit. – eq- Jun 05 '12 at 00:00
  • +1, if dynamic allocation is not a concern, this is the way to go. – jxh Jun 05 '12 at 00:05
2

This is a case where I think macros would increase the readability of your code. You could create an unguarded include file:

/* file: pairs.def */
PAIR_DEF(foo, bar)
PAIR_DEF(bar, baz)
//...

When you initialize your pairs array, you would use this file:

Pair pairs[] = {
#define PAIR_DEF(x, y) { #x, #y },
#include "pairs.def"
#undef PAIR_DEF
};

And you can create corresponding enumerated constants:

enum PairEnum {
#define PAIR_DEF(x, y) PAIR_ ## x ## _ ## y,
#include "pairs.def"
#undef PAIR_DEF
};

Then create a helper macro to help you initialize your pops array.

#define PAIR(x, y) &pairs[PAIR_ ## x ## _ ## y]
PairOfPairs pops[] = {
    { PAIR(foo, bar), PAIR(bar, baz) },
    //...
};

This is similar to the technique I use to associate enum values to strings, described in my answer to this question.

Community
  • 1
  • 1
jxh
  • 69,070
  • 8
  • 110
  • 193
  • clever, but a little complicated. eq's solution seems a little cleaner – chacham15 Jun 04 '12 at 23:52
  • @chacham15, the only advantage is that it doesn't rely on C++11 and there is no dynamic memory allocation. I use this technique whenever I have to match strings with enums. – jxh Jun 04 '12 at 23:55
  • yea, true enough. hence the +1 :) – chacham15 Jun 04 '12 at 23:57
  • @chacham15: another advantage is if you refer to the same pair multiple times, this solution reuses the same pointer representing that pair, while eq's would allocate it again. But you mentioned pair of pairs is a small set, so there again you might not care. – jxh Jun 05 '12 at 09:44