Disable global constructors
Disable global constructors
Is there a way to disable global (and global static) constructors?
I want my C++ kernel to call some functions during initialization, that effectively construct some of the most vital objects in the system. I have noticed that leaving a single declaration of a class instance in the global (or file-local) namespace would implicitly call the empty constructor for that instance (that was obviously long ago). So I got a kernel coding rule: always declare an empty constructor (does nothing...) when declaring a class. That constructor would be empty in the defining file. As my kernel doesn't call global constructors (and won't ), all those stuff remains in the file, but's never called. I want to remove that effect, before its too late . How can I force GCC to stop emitting that stuff?
I want my C++ kernel to call some functions during initialization, that effectively construct some of the most vital objects in the system. I have noticed that leaving a single declaration of a class instance in the global (or file-local) namespace would implicitly call the empty constructor for that instance (that was obviously long ago). So I got a kernel coding rule: always declare an empty constructor (does nothing...) when declaring a class. That constructor would be empty in the defining file. As my kernel doesn't call global constructors (and won't ), all those stuff remains in the file, but's never called. I want to remove that effect, before its too late . How can I force GCC to stop emitting that stuff?
Happy New Code!
Hello World in Brainfuck :[/size]
Hello World in Brainfuck :
Code: Select all
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
- xenos
- Member
- Posts: 1121
- Joined: Thu Aug 11, 2005 11:00 pm
- Libera.chat IRC: xenos1984
- Location: Tartu, Estonia
- Contact:
Re: Disable global constructors
My "solution" for this problem is to use placement new. Schematically, it works like this:
GlobalClass.h:
GlobalClass.cpp:
Main.cpp:
This way the constructor gets called explicitly when the object is constructed using placement new syntax. Instead of the default constructor without arguments also a more general constructor can be used. The main drawback I see for this method is that GCC issues a warning since it violates the "strict aliasing" rules, which forbid objects of different type to reside at the same memory location (in this case an object of type GlobalClass and a char array). As far as I know, this causes undefined behavior when one is accessing both of them. I'm still looking for a cleaner solution which resolves this warning.
GlobalClass.h:
Code: Select all
class GlobalClass
{
public:
// Constructor
GlobalClass(void);
void SomeFunction(void);
};
extern char gc_space[];
inline GlobalClass& gc_object(void) { return(reinterpret_cast<GlobalClass&>(gc_space)); }
Code: Select all
#include <GlobalClass.h>
GlobalClass::GlobalClass(void)
{
// Initialize object.
}
void GlobalClass::SomeFunction(void)
{
// ...
}
char gc_space[sizeof(GlobalClass)];
Code: Select all
#include <GlobalClass.h>
void main(void)
{
new (gc_space) GlobalClass; // Explicitly construct object (calls constructor).
gc_object().SomeFunction(); // Use object.
}
Re: Disable global constructors
Write in C.
- max
- Member
- Posts: 616
- Joined: Mon Mar 05, 2012 11:23 am
- Libera.chat IRC: maxdev
- Location: Germany
- Contact:
Re: Disable global constructors
Whats the problem with calling global constructors? It's easy, and you can do it just when your kernel is ready, like after setting up your mm. Relying on yourself to do it right every time is a bad idea, because you *will* forget it somewhere and get undebuggable errors. What are your worries?KemyLand wrote:As my kernel doesn't call global constructors (and won't )
- xenos
- Member
- Posts: 1121
- Joined: Thu Aug 11, 2005 11:00 pm
- Libera.chat IRC: xenos1984
- Location: Tartu, Estonia
- Contact:
Re: Disable global constructors
Certain objects may depend on others, so it's important in which order their constructors are called. It's also possible that things need to be done inbetween constructor calls. This cannot be done easily if one has just an auto-generated list of constructors, as it would be the case in the "standard approach". See my kernel's startup function for an example.
Re: Disable global constructors
The obvious, simple solution to this problem is to call the global constructors and only initialize global data in them. It is a design mistake to have even remotely interesting constructors. Placement new is just a poor attempt at fixing this. Don't have global singleton classes, just havd regular functions.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: Disable global constructors
Replace "class" with "namespace". Goodbye constructor.sortie wrote:Don't have global singleton classes, just havd regular functions.
Re: Disable global constructors
Yes, I agree with just empty constructor plus an init function.
If you care about the constructor sequence & state, you may also worry with the exceptions, and init() provide much flexibility.
Code: Select all
class GlobalClass {
public:
// Default Constructor
int init();
void SomeFunction(void);
};
GlobalClass gc;
void main(void){
int rez = gc.init(); // Explicitly init object
gc.SomeFunction(); // Use object.
}
- xenos
- Member
- Posts: 1121
- Joined: Thu Aug 11, 2005 11:00 pm
- Libera.chat IRC: xenos1984
- Location: Tartu, Estonia
- Contact:
Re: Disable global constructors
This doesn't work if your classes have virtual functions and derived classes and you wish to decide at runtime which of them to use. Just to give you an example, I have a Clock class, from which different classes (PITClock, ACPIClock, HPETClock) are derived. The kernel measures time using a global clock object which has base class Clock, but it is decided upon at runtime of which derived class the actual clock is instantiated. This does not work with namespaces (otherwise I would indeed use namespaces instead). (The clock is just an example - there are plenty other such cases, such as the physical memory manager etc.)Combuster wrote:Replace "class" with "namespace". Goodbye constructor.sortie wrote:Don't have global singleton classes, just havd regular functions.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: Disable global constructors
But in that example we're not talking about singletons either.
Re: Disable global constructors
Namespaces + function pointers ~= virtual classes.
The obvious solution to placement new is to get dynamic allocation online very early.
The obvious solution to placement new is to get dynamic allocation online very early.
Re: Disable global constructors
As mentioned *long* above, my problem is call-order related. I can't leave GCC do what it wants in the order it pleases. This is what would happen:
I have a KInit macro. It wraps a extern "C" function (using __VA_ARGS__ tricks ) named as __init ## subsys ## __ (i.e. subsystem LIB's KInit is named __initLIB__ internally). As a subsystem is able to hold another subsystem, its easy to make a call stack from this macro, along with KInitCall(). KInit() also provides the subsystem with a single-initialization mechanism: if a KInitCall() occurs twice to a single subsystem, Panic Mode is set, and you know how this ends...
My extern "C" KernelInit() function is responsible for KInitCall()ing all the main subsystems, which can then call their own (sub)subsystems.
I don't want this "Global Constructor Table" to even exist. I've implemented placement new, but I don't like that approach too much .
Although I can implement a KInit() version for the init() approach, the global constructor table is still there. I will try with this approach, but I still want to remove that extra table...
I have a KInit macro. It wraps a extern "C" function (using __VA_ARGS__ tricks ) named as __init ## subsys ## __ (i.e. subsystem LIB's KInit is named __initLIB__ internally). As a subsystem is able to hold another subsystem, its easy to make a call stack from this macro, along with KInitCall(). KInit() also provides the subsystem with a single-initialization mechanism: if a KInitCall() occurs twice to a single subsystem, Panic Mode is set, and you know how this ends...
My extern "C" KernelInit() function is responsible for KInitCall()ing all the main subsystems, which can then call their own (sub)subsystems.
I don't want this "Global Constructor Table" to even exist. I've implemented placement new, but I don't like that approach too much .
Although I can implement a KInit() version for the init() approach, the global constructor table is still there. I will try with this approach, but I still want to remove that extra table...
Happy New Code!
Hello World in Brainfuck :[/size]
Hello World in Brainfuck :
Code: Select all
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
Re: Disable global constructors
Then don't have any global constructors and the table is empty. I recommend you still have it, even if potentially empty, and call it, as the compiler might use it various language features. If it is empty, no harm done, if the compiler can't do something at compile time, it might emit a global constructor and do it at start up. Fighting the table is fruitless. If you really don't want it, ignore it, don't call _init, don't link in the sections in your linker script, and it all goes away.
Either way, there are no dependency issues here if you design your code sanely: The global constructors must be trivial and just initialize global memory. That way, there are no dependencies.
As for dependencies between subsystems, I recommend a no-initialization approach. If you can initialize it at compile/link/load time rather than run-time, then great, do that. That removes these initialization dependencies. For instance, my system call table is done at compile time, and so can the GDT, IDT and such be. If everything can be done at that time, there is very, very little initialization code remaining and it can just immediately execute the real code. (Of course, this isn't fully possible in a kernel, and might not in your design, but it's a simplicity goal I aim towards).
Either way, there are no dependency issues here if you design your code sanely: The global constructors must be trivial and just initialize global memory. That way, there are no dependencies.
As for dependencies between subsystems, I recommend a no-initialization approach. If you can initialize it at compile/link/load time rather than run-time, then great, do that. That removes these initialization dependencies. For instance, my system call table is done at compile time, and so can the GDT, IDT and such be. If everything can be done at that time, there is very, very little initialization code remaining and it can just immediately execute the real code. (Of course, this isn't fully possible in a kernel, and might not in your design, but it's a simplicity goal I aim towards).
Re: Disable global constructors
I think I've finally found the solution
In <def.h> (Main type definitions):
Then all classes will declare
Then I can call global constructors, if any put in there by GCC, call KernelInit(), and problem solved
Thanks to all of you!
By the way...
Sortie, you just gave me an excellent idea:sortie wrote:Then don't have any global constructors and the table is empty.
In <def.h> (Main type definitions):
Code: Select all
//...
#define Attribute(attr) __attribute__((__attr__))
//...
#define Inline Attribute(always_inline)
//...
Code: Select all
Inline FooClass() {}
Thanks to all of you!
By the way...
Yes, in my design it isn't any practical . I follow that rule (initialize as little as you can at run-time), but there are still some run-time dependencies (i.e. unpredictable dynamic memory allocation) that I can't remove (by now...)sortie wrote:As for dependencies between subsystems, I recommend a no-initialization approach. If you can initialize it at compile/link/load time rather than run-time, then great, do that. That removes these initialization dependencies. For instance, my system call table is done at compile time, and so can the GDT, IDT and such be. If everything can be done at that time, there is very, very little initialization code remaining and it can just immediately execute the real code. (Of course, this isn't fully possible in a kernel, and might not in your design, but it's a simplicity goal I aim towards).
Happy New Code!
Hello World in Brainfuck :[/size]
Hello World in Brainfuck :
Code: Select all
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
- xenos
- Member
- Posts: 1121
- Joined: Thu Aug 11, 2005 11:00 pm
- Libera.chat IRC: xenos1984
- Location: Tartu, Estonia
- Contact:
Re: Disable global constructors
If I wanted to use function pointers and thus throw away a lot of the nice type safety of C++, I wouldn't use C++ at all. Using virtual classes instead of function pointers is an intentional design decision.sortie wrote:Namespaces + function pointers ~= virtual classes.
...unless the dynamic allocator itself depends on global objects, in which case this "solution" does not work for these objects.The obvious solution to placement new is to get dynamic allocation online very early.