tags:

views:

1163

answers:

6

I'm writing a code generator, well, actually a data generator that will produce data structures of this form (obviously the actual data structures are much more elaborate):

typedef struct Foo {
  int a;
  struct Foo* foo;
} Foo;

extern Foo f1;
extern Foo f2;

Foo f1 = {1, &f2};
Foo f2 = {2, &f1};

This is portable for all C and C++ compilers I have tried.

I would like to forward declare these struct instances as static so as not to pollute the global variable space, as in:

typedef struct Foo {
  int a;
  struct Foo* foo;
} Foo;

static Foo f1;
static Foo f2;

static Foo f1 = {1, &f2};
static Foo f2 = {2, &f1};

Although this works with gcc and probably all C compilers, the above code does not work with C++ compilers and results in a compile error:

error: redefinition of ‘Foo f1’
error: ‘Foo f1’ previously declared

I understand why this is happening in C++. Is there a simple workaround that does not involve using code at runtime to achieve the same effect that is portable to all C++ compilers without resorting to using a C compiler to compile certain files?

A: 

#ifdef __cplusplus

Pete Kirkham
+4  A: 

What you are trying to avoid is termed as Static Initialization Order Fiasco. You will be well served to use functions instead and also to initialize the individual objects with default values and then reseat the member pointers.

Your code samples mean radically different things. You will have to relook. The first one succeeds because you have a definition of one object and a declaration of another. This will work with both C and C++.

extern Foo f1;

This is a declaration and a tentative definition.

static Foo f1;

This is the declaration and definition of an object f1 of type struct Foo.

static Foo f2;

Ditto.

static Foo f1 = {1, &f2};

This is a redefinition. You have violated the One Definition Rule which says, there must be one and exactly one definition of a symbol in a translation unit. Outside of that you are allowed to have multiple definitions but of course each such occurrence has to have the same syntax and semantics.

static Foo f2 = {2, &f1};

Ditto.

extern Foo fn;
/* some code */
extern Foo fn;
/* some more ... */
Foo fn; /* finally a definition */

This is fine since it's ok to have multiple tentative declarations.

dirkgently
Yes, that was stated that in the original post. The question is how to use static struct instances in C++ to achieve the same effect as C. It does not seem to not be possible in C++. It appears to be a language oversight.
@dirkgently: it is valid C because the C standard says: "Within one translation unit, each declaration of an identifier with internallinkage denotes the same object or function".
Michael Burr
It's surprising that this C functionality cannot be achieved in C++. Wasn't C++ designed to be a superset of C?
@Michael Burr:That is for external linkage, I believe.
dirkgently
@Michael Burr: Can't find the appropriate sections, so I'll go with you. My mistake. Thanks again.
dirkgently
That's a copy-n-paste from the C99 standard PDF (6.2.2). The same wording is in the C90 standard in 6.1.2.2.
Michael Burr
Annex C, 3.1/3 of c++98 explains what makes this not work in C++ (where tentative definitions are not allowed). have fun :)
Johannes Schaub - litb
+2  A: 

You can't forward declare objects, only types. The extern solution is the correct solution. Or if you really need to avoid global namespace pollution, make them static and initialize them using a function which you call before all others.

EDIT: Michael Burr mentioned the reason in a comment, I figured I'd add it to a post:

@dirkgently: it is valid C because the C standard says: "Within one translation unit, each declaration of an identifier with internal linkage denotes the same object or function".

C++ has no such rule.

EDIT:

As noted in one other post. You can use an anonymous namespace as well to limit the scope of the variable. Just wrap the namespace stuff in an #ifdef __cplusplus and you should be good to go.

Evan Teran
It's a shame C++ lacks the ability to forward declare a struct instance as you can do in C. extern pollutes the global variable space, which is what I am trying to avoid.
+5  A: 

This should compile with either C or C++ and give you the same name to access the same thing in both compilers.

#ifdef __cplusplus
 namespace // use anonymous namespace to avoid poluting namespace.
 {
    struct StaticFoos
    {
       static Foo f1;
       static Foo f2;
    };

    Foo StaticFoos::f1 = {1, &StaticFoos::f2};
    Foo StaticFoos::f2 = {2, &StaticFoos::f1};
 }

 static const &Foo f1 = StaticFoos::f1;
 static const &Foo f2 = StaticFoos::f2;
#else
 static Foo f1 = {1, &f2_};
 static Foo f2 = {1, &f1_};
#endif

Now in C and C++, you can access f1 and f2.

Eclipse
We have a winner! Brilliant - that's exactly what I was looking for. Thanks.
My faith in C++ has been restored.
This also works: static Foo static Foo and achieves the same effect without pointers.
or: static const Foo static const Foo
Updated to reflect your suggestions.
Eclipse
A: 

I would create two files (.cpp and .h):

code.h:

typedef struct Foo {
  Foo() {}
  Foo(int aa, struct Foo* ff) : a(aa), foo(ff) {}
  int a;
  struct Foo* foo;
} Foo;

static Foo f1;
static Foo f2;

code.cpp:

void myfun()
    {
    f1 = Foo(1, &f2);
    f2 = Foo(2, &f1);
    }

I would also prefer to put all variables like f1, f2 ... into some kind of "storage" object (of my own class or e.g. some STL container). Then, I would define this object as a static one.

Wacek
+5  A: 

This seems to have a similar effect to Josh's answer, but with less complexity:

#ifdef __cplusplus
namespace {
    extern Foo f1;
    extern Foo f2;

    Foo f1 = {1, &f2};
    Foo f2 = {2, &f1};
}
#else
    static Foo f1;
    static Foo f2;

    Foo f1 = {1, &f2};
    Foo f2 = {2, &f1};
#endif

When compiled for C++, the extern definitions for f1 and f2 are exposed in the object file with an externally linkable symbol; however, because they are inside an anonymous namespace the symbols are mangled in such a way that they will not conflict with symbols from another translation unit.

With macro magic you could set things up so there's only one place where f1 and f2 are declared and defined, but if this is being generated mechanically there's probably not much reason to do that.

Something like:

#ifdef __cplusplus
#define START_PRIVATES namespace {
#define END_PRIVATES   }
#define PRIVATE extern
#else
#define START_PRIVATES 
#define END_PRIVATES   
#define PRIVATE static
#endif

START_PRIVATES
    PRIVATE Foo f1;
    PRIVATE Foo f2;

    Foo f1 = {1, &f2};
    Foo f2 = {2, &f1};
END_PRIVATES
Michael Burr
yeah haha same thing i had in mind. you got my vote, thumbs up :)
Johannes Schaub - litb
additional, the extern stuff avoids using deprecated stuff (static being deprecated in C++) and allows them to be passed as template arguments (can't pass static variables to templates as parameters. only those having extern linkage). but they're still mangled effectively TU local. neat! :)
Johannes Schaub - litb