calling from global class's constructor, is it safe?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void triggerMe(int in);
class second;
class first { public:
	int something;
	first() {
		something = rand() % 50;
		cout << "first:" << something << endl;
		triggerMe(something);
	}
} cfirst;

class second{ public:
	int something;
	second() {
		something = rand() % 50;
		cout << "second:" << something << endl;
	}
} csecond;

void triggerMe(int in) {
	cout << "csecond.something BEFORE:" << csecond.something << endl;
	csecond.something = in;
	cout << "csecond.something AFTER:" << csecond.something << endl;
}

int main(int argc, char *argv[]) {
	cout << "main" << endl;
	return 1;
}

first:41
csecond.something BEFORE:0
csecond.something AFTER:41
second:17
main


That doesn't seem safe.
If I leave rest of the code same but change
1
2
3
4
class first { public:
	int something;
	first() {
		something = 1; // something = rand() % 50; 


1
2
3
4
class second { public:
	int something;
	first() {
		something = 2; // something = rand() % 50; 

first:1
csecond.something BEFORE:2
csecond.something AFTER:1
second:2
main


But what if:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void triggerMe(int in);
class second;
class first { public:
	int something;
	first() {
		something = 1;
		cout << "first:" << something << endl;
		triggerMe(something);
	}
} cfirst, cfirst2;

class second{ public:
	int something;
	second() {
		something = 2;
		cout << "second:" << something << endl;
	}
} *csecond;

void triggerMe(int in) {
	static bool triggered = false;
	if (!triggered) {
		csecond = new second();
		triggered = true;
	}
	cout << "csecond.something BEFORE:" << csecond->something << endl;
	csecond->something = in;
	cout << "csecond.something AFTER:" << csecond->something << endl;
}

int main(int argc, char *argv[]) {
	cout << "main" << endl;
	return 1;
}
first:1
second:2
csecond.something BEFORE:2
csecond.something AFTER:1
first:1
csecond.something BEFORE:1
csecond.something AFTER:1
main


That would be safe right?
This method's Implementation would be plugins/extensions system.
External DLL or not, doesn't matter. That's the beauty.
Last edited on
Even better:
1
2
3
4
5
6
7
};

std::unique_ptr<second> csecond;

void triggerMe(int in) {
	if (!csecond)
		csecond.reset(new second());

I would advice against hiding the order of initialization like this. If you have global objects that need to be initialized in a specific order, it's best to change them to global pointers and allocate them first thing when main() starts running.
1
2
3
4
5
6
7
8
9
void initialize_globals(){
    csecond.reset(new second);
    cfirst.reset(new first);
    //etc.
}

int main(){
    initialize_globals();
}
Now the order in which objects are initialized is completely obvious, and they're initialized all in the same place, rather than in random functions all over the codebase.
How can you be sure that csecond can be guaranteed to be 0 when It don't
have chance to initialize it's value?

Isn't the value of a declared variable random until we initialize and give it a value?
Random because that memory apartment where variable is going to be living, it's value is going to be what ever the value was for variable what was previously living there.

That should be the reason why my first and second output is different.

Giving them initialization order would render my attempt of trying to eliminate need to edit core code, useless. When you finish and you edit later on, you going to have to test it again to be sure it still works. It's just gonna be less testing that way.

Forgot to mention that one code part should run first, other's order does not matter.
Imagine writing plugins/extensions system, the core first, plugins order will always be unpredictable.
Last edited on
Hmm... You're saying that the order of initialization of plugins matters? In my opinion that's a poorly designed system. Ideally plugins should be self-contained and independent.

But fine, if it's absolutely necessary that some plugins depend on other plugins then either:
1. The user should be able to specify the order of initialization. This is what's done, for example, for Skyrim mods. This is a really simple solution, but the problem is that it puts the burden of doing things correctly on the user, and the system can become frustrating to use when many plugins are being used.
2. Plugins should be able to identify themselves uniquely (e.g. using a GUID) and declare on which other plugins it depends on. For example, this could be done through a manifest (a text file):
1
2
3
4
5
6
id = ff0a03cd-236a-483d-a144-0837259e3604
dependencies = {
    a8c3e163-0730-4388-8804-b96a898e7ab9
    67d3aba5-8a08-46eb-9128-31b4d057488c
    af2fa939-ff7f-4580-96cb-d678d59aafcd
}
Then before initializing the plugins, the loader must use this information to build a directed acyclic graph (DAG) describing the dependencies of the entire system and sort it topologically (https://en.wikipedia.org/wiki/Topological_sorting ). The sorted sequence then describes the order in which the plugins should be initialized so that there are no conflicts.
Obviously this solution is more complex, but it solves the problem of order of initialization completely, dynamically, and without user intervention.
Your first example, works, but only because csecond contains purely POD (plain old data). But I wouldn't recommend it. You shouldn't access members of a class instance until the instance has been constructed.

If I recall correctly:
- global and file scope data within a compilation unit (i.e., source file) are initialized top to bottom before main() is executed.
- If there are multiple compilation units, the order in which each unit's global/file scope data is initialized is undefined by the C++ standard. Your dev environment may have a well-defined order.
- statics within functions/methods are initialized when program flow first encounters them. This means that if the code they're in doesn't execute, they never get constructed:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

class MyClass {
public:
    MyClass (const char *msg) {
	std::cout << msg << '\n';
    }
};


MyClass global("global");
static MyClass local("file scope static");

void f()
{
    static MyClass local("local static variable");
}


int
main()
{
    std::cout << "main() starting\n";
    f();
    return 0;
}


global
file scope static
main() starting
local static variable

Topic archived. No new replies allowed.