Sign in to follow this  

Dangerous situation after using extern instead of #define

This topic is 3570 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I had following defines in a header:
#define Vector3_0 Vector3(0.0,0.0,0.0)
#define Vector3_x Vector3(1.0,0.0,0.0)
#define Vector3_y Vector3(0.0,1.0,0.0)
#define Vector3_z Vector3(0.0,0.0,1.0)
Avoiding macro's is a good thing (tm) plus it's always calling the constructor if you use it, so I changed it into:
In the header:

extern const Vector3 Vector3_0;
extern const Vector3 Vector3_x;
extern const Vector3 Vector3_y;
extern const Vector3 Vector3_z;

In a .cpp file:

const Vector3 Vector3_0 = Vector3(0.0,0.0,0.0);
const Vector3 Vector3_x = Vector3(1.0,0.0,0.0);
const Vector3 Vector3_y = Vector3(0.0,1.0,0.0);
const Vector3 Vector3_z = Vector3(0.0,0.0,1.0);
I had done that together with multiple changes so it wasn't immediatly clear that this could be the cause of crashes. Anyway after that my game suddenly had a crash, and a velocity that became "NaN" instead of a velocity. It took a long time to track down, but eventually I found out that some other objects were also being created on the heap before main() was called. And the order of that isn't defined, and something that was using Vector3_x & co was already being called before Vector3_x & co themselves had been created. This unlike the #defines! What should I do in this case? Go back to the #defines? Or avoid creating objects on the heap before main() is called? Does there exist another alternative to the #defines that is less dangerous? Maybe make them static? [Edited by - Lode on March 6, 2008 7:17:42 AM]

Share this post


Link to post
Share on other sites
I prefer

class Vector3
{
public:
static Vector3 Zero()
{
// Alternatively return an on-demand cached static member
// if you care about "construction cost."
return Vector3(0,0,0);
}
};

Share this post


Link to post
Share on other sites
Objects with constructors are subject to initialization order problems. You can reduce those issues by using globals with aggregate initializers. Ex:

struct Vector3Helper {
float x;
float y;
float z;
operator Vector3() { return Vector3(x, y, z); }
};

extern const Vector3Helper Vector3_0;
extern const Vector3Helper Vector3_x;
extern const Vector3Helper Vector3_y;
extern const Vector3Helper Vector3_z;

const Vector3Helper Vector3_0 = { 0, 0, 0 };
const Vector3Helper Vector3_x = { 1, 0, 0 };
const Vector3Helper Vector3_y = { 0, 1, 0 };
const Vector3Helper Vector3_z = { 0, 0, 1 };

Share this post


Link to post
Share on other sites
SiCrane, that's very interesting because I was also dealing with problems such as "should I make the Vector3 POD or give it a convenient extra constructor?". Sadly the one excludes the other.

Is it guaranteed that objects without constructor will all be initialized first (so the Vector3Helpers), and only later the other objects?

Share this post


Link to post
Share on other sites
From 3.6.2 paragraph 1 of the C++ Standard:
Quote:

Objects of POD types ... with static storage duration initialized with constant expressions ... shall be initialized before any dynamic initialization takes place.

Share this post


Link to post
Share on other sites
Quote:
Original post by Lode
I had following defines in a header:

#define Vector3_0 Vector3(0.0,0.0,0.0)
#define Vector3_x Vector3(1.0,0.0,0.0)
#define Vector3_y Vector3(0.0,1.0,0.0)
#define Vector3_z Vector3(0.0,0.0,1.0)


Avoiding macro's is a good thing (tm) plus it's always calling the constructor if you use it, so I changed it into:

In the header:

extern const Vector3 Vector3_0;
extern const Vector3 Vector3_x;
extern const Vector3 Vector3_y;
extern const Vector3 Vector3_z;

In a .cpp file:

const Vector3 Vector3_0 = Vector3(0.0,0.0,0.0);
const Vector3 Vector3_x = Vector3(1.0,0.0,0.0);
const Vector3 Vector3_y = Vector3(0.0,1.0,0.0);
const Vector3 Vector3_z = Vector3(0.0,0.0,1.0);


I had done that together with multiple changes so it wasn't immediatly clear that this could be the cause of crashes.

Anyway after that my game suddenly had a crash, and a velocity that became "NaN" instead of a velocity. It took a long time to track down, but eventually I found out that some other objects were also being created on the heap before main() was called.

And the order of that isn't defined, and something that was using Vector3_x & co was already being called before Vector3_x & co themselves had been created.

This unlike the #defines!

What should I do in this case? Go back to the #defines? Or avoid creating objects on the heap before main() is called? Does there exist another alternative to the #defines that is less dangerous? Maybe make them static?


I think the real solution is ultimately to try to avoid, as much as possible, having code that executes before main(), except for objects that are meant to be constant. That's the real source of your issue, but if it's rampant through your code, you might want to just consider gradually working towards this goal.

Share this post


Link to post
Share on other sites
Here's a possible way to avoid SOI problems, but it's obviously a hack around existing bad design (and kinda smells of singletonitis, too):


// before

Foo x;
Bar y;
Baz z;

int main() {
doStuffWith(x);
};

// after

struct Global {
Foo x;
Bar y;
Baz z;

// use constructor/destructor logic to ensure everything is done in the right order.
};

Global* global; // a weak pointer; don't delete!

int main() {
// Only ever instantiate it here:
std::auto_ptr<Global> global = new Global();
::global = global; // allow other functions access.

// everything else
doStuffWith(global->x);

// RAII takes care of the rest.
}


I haven't actually tested this approach, but I've had the idea for a while.

Share this post


Link to post
Share on other sites
Now you've coupled every global to every other global. So if the type definition of Foo changes, then every file that uses y and z will need to be recompiled even if they previously had no dependency on x.

Share this post


Link to post
Share on other sites

This topic is 3570 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this