C++ initialization mess
-
- Member
- Posts: 595
- Joined: Mon Jul 05, 2010 4:15 pm
C++ initialization mess
During startup the kernel must run through a series of initialization methods of several classes. Each class has an init method often with a return value so that init process can be halted if anything went wrong. Exceptions are disabled so that way isn't possible. Init methods aren't really that nice but necessary as order of the startup is important. This also leads to that subclasses must have init methods as well which don't like since I want them to be more generic.
After startup you can use C++ more like you're used. However, many classes I use are more generic, even third party, have constructors and those classes are starting to find their way into the classes that are run during early startup. I have a few global classes which means that the constructors are run when there is nothing available which causes troubles.
The question is if it is possible to disregard the constructors that are defined in ctors (or init array) and run them when they are needed. Basically you use the placement new operator. Now the constructors are run when you expect them.
MyClass A;
void boot()
{
new((void*)&A) MyClass;
A.Init();
}
Basically the initialization become a two step process first the constructor is run and then the init method. Since I've failed already I wonder if you have any better way of dealing with this problem.
After startup you can use C++ more like you're used. However, many classes I use are more generic, even third party, have constructors and those classes are starting to find their way into the classes that are run during early startup. I have a few global classes which means that the constructors are run when there is nothing available which causes troubles.
The question is if it is possible to disregard the constructors that are defined in ctors (or init array) and run them when they are needed. Basically you use the placement new operator. Now the constructors are run when you expect them.
MyClass A;
void boot()
{
new((void*)&A) MyClass;
A.Init();
}
Basically the initialization become a two step process first the constructor is run and then the init method. Since I've failed already I wonder if you have any better way of dealing with this problem.
Re: C++ initialization mess
OSwhatever; - I feel your pain.
The search for an object-oriented way to write an operating system is a real hurdle, and i dont think anyone has found the right way just yet.
The problem is simple. Its the language. C/C++ just were not designed for information organisation.
I, personally, have come to believe our solution is MVC (Model-View-Controller) and DCI (Data-Context-Interaction), They are both ways of thinking to organise information.
From your post, I think your already heading in that direction. Your model has all the domain data objects (MyClass), and your controller inits everything (void boot).
DCI represents the Model in MVC, So DCI is how you organise "MyClass". Data objects are the lowest understanding at your current scope, They are very simple objects, and the only functions in them are ones to interact with them (most likely read/write), no logic at all. Contexts+Interactions are how you make data objects more complex. They are called Context's though because you want to be describing use cases, and not blobs of data.
e.g.
data RAM {
function read(); function write();
}
context buddyAllocateMemory {
// This context adds the buddy allocator algorithm.
function ... ... // <-- Interactions
}
This is a model. A view is how you represent this model to its users. In a kernels case, a user is an application. So your API, IPC etc goes into the view.
The controller is the links and connections from the view and the model. A view fires or emits events, then takes that event to create the business that needs to happen to the model
View.onMyForkSyscall = function( ) {
// Tell the model to copy my stack blah blah..
// ...
}
And data changes from the model to the view...
watch(Model.RAM.size) = function(oldvalue, newvalue) {
// Tell the view ram size has changed..
// ...
}
The controller has the main(), which sets up the model and view, and any init stuff that needs to be done.
Their are many other paradigms, which all try to solve this problem. So please read about and find the right one for you. I hope this helps.
Mike Brown
The search for an object-oriented way to write an operating system is a real hurdle, and i dont think anyone has found the right way just yet.
The problem is simple. Its the language. C/C++ just were not designed for information organisation.
I, personally, have come to believe our solution is MVC (Model-View-Controller) and DCI (Data-Context-Interaction), They are both ways of thinking to organise information.
From your post, I think your already heading in that direction. Your model has all the domain data objects (MyClass), and your controller inits everything (void boot).
DCI represents the Model in MVC, So DCI is how you organise "MyClass". Data objects are the lowest understanding at your current scope, They are very simple objects, and the only functions in them are ones to interact with them (most likely read/write), no logic at all. Contexts+Interactions are how you make data objects more complex. They are called Context's though because you want to be describing use cases, and not blobs of data.
e.g.
data RAM {
function read(); function write();
}
context buddyAllocateMemory {
// This context adds the buddy allocator algorithm.
function ... ... // <-- Interactions
}
This is a model. A view is how you represent this model to its users. In a kernels case, a user is an application. So your API, IPC etc goes into the view.
The controller is the links and connections from the view and the model. A view fires or emits events, then takes that event to create the business that needs to happen to the model
View.onMyForkSyscall = function( ) {
// Tell the model to copy my stack blah blah..
// ...
}
And data changes from the model to the view...
watch(Model.RAM.size) = function(oldvalue, newvalue) {
// Tell the view ram size has changed..
// ...
}
The controller has the main(), which sets up the model and view, and any init stuff that needs to be done.
Their are many other paradigms, which all try to solve this problem. So please read about and find the right one for you. I hope this helps.
Mike Brown
- blobmiester
- Member
- Posts: 45
- Joined: Fri Jul 16, 2010 9:49 am
Re: C++ initialization mess
You have global objects of classes, correct? The idiomatic way in C++ is singletons. With these, the constructor is delayed until the first call to the instance() function.OSwhatever wrote:The question is if it is possible to disregard the constructors that are defined in ctors (or init array) and run them when they are needed. Basically you use the placement new operator. Now the constructors are run when you expect them.
Code: Select all
class MyClass
{
public:
static MyClass& instance()
{
static MyClass s_instance;
return s_instance;
}
void foo();
private:
MyClass();
};
Code: Select all
MyClass::instance(); // Call constructor
MyClass::instance().foo(); // Call foo
Now, is this initialization process your kernel initialization? Or is it a collection of user-defined initializers (as in Linux) that are run during startup? If the former, then the above should suffice; the latter, well, there is a thread around here (or two) about that.
-
- Member
- Posts: 595
- Joined: Mon Jul 05, 2010 4:15 pm
Re: C++ initialization mess [Solved]
This is for the kernel initialization. I use singletons, but not in that way. I have the static instance member as a member in private data of the class, not in the instance method. Having "static MyClass s_instance" in the instance() method, where will the class be allocated? When I try this I get loads of constructor calls, as the singleton is local in some way. Also now the linker is asking me for __cxa_guard_acquire and__cxa_guard_release. Stubbing these solves it however.blobmiester wrote:You have global objects of classes, correct? The idiomatic way in C++ is singletons. With these, the constructor is delayed until the first call to the instance() function.OSwhatever wrote:The question is if it is possible to disregard the constructors that are defined in ctors (or init array) and run them when they are needed. Basically you use the placement new operator. Now the constructors are run when you expect them.
So to initialize or call something:Code: Select all
class MyClass { public: static MyClass& instance() { static MyClass s_instance; return s_instance; } void foo(); private: MyClass(); };
That is just for that.Code: Select all
MyClass::instance(); // Call constructor MyClass::instance().foo(); // Call foo
Now, is this initialization process your kernel initialization? Or is it a collection of user-defined initializers (as in Linux) that are run during startup? If the former, then the above should suffice; the latter, well, there is a thread around here (or two) about that.
Re: C++ initialization mess
Gosh, some day I'll go on a rampage when I read how Singletons somehow give "the blessing of the church of OO" to global objects...blobmiester wrote:You have global objects of classes, correct? The idiomatic way in C++ is singletons.
...especially in C++, where the only reason to have Singletons at all is to enforce you get only one instance per class. Which is completely unnecessary if you control the code base (i.e., it's a library thing).
Did I understand OSwhatever's problems correctly: You managed to get so many interdependent global objects (by calling third party code???) that simply calling the constructors in the order the compiler places them in the .ctors section doesn't work anymore, and being somewhat confused about how it should work you now want to hack your way around it?
I'd seriously suggest considering a redesign. I smell: Problem #1 - you somewhat confused the startup initialization of basic kernel objects with the startup initialization of the computer itself. There is very little that should need initialization that early. Problem #2 - Singleton anti-pattern. 99% of all singletons ever written are either completely unnecessary (because you control the code base) or just a poor man's excuse for having a global and admitting as much. Problem #3 - Interdependency. Your objects are apparently too tightly coupled if they depend on each other so much. Why not "enabling" features as neighouring objects come up? (References instead of has-a (or worse, is-a), querying if the referenced class is "up" already, updating the reference when it is and "make do" if it isn't.)
That last one might not even make sense in your specific setup, but it's hard to tell without actually seeing code. But as a rule of thumb, if class interdependencies become a problem, you should solve the interdependencies of your design, not blame the language (or hacking your way around it).
Every good solution is obvious once you've found it.
-
- Member
- Posts: 595
- Joined: Mon Jul 05, 2010 4:15 pm
Re: C++ initialization mess
No, I hardly have anything in ctors since most classes are initialized with init methods, the constructors are therefore mostly empty. I don't like this since it is not really a good way to solve it and cause problems when you do add classes that initialized with constructors, this basically my question, how to avoid this. Third party code init the classes the normal way you'd expect them to do. I have global objects and I have no other way to store the objects.Solar wrote:blobmiester wrote:Did I understand OSwhatever's problems correctly: You managed to get so many interdependent global objects (by calling third party code???) that simply calling the constructors in the order the compiler places them in the .ctors section doesn't work anymore, and being somewhat confused about how it should work you now want to hack your way around it?
- gravaera
- Member
- Posts: 737
- Joined: Tue Jun 02, 2009 4:35 pm
- Location: Supporting the cause: Use \tabs to indent code. NOT \x20 spaces.
Re: C++ initialization mess
Hi,
My kernel is in C++, and I basically do not, for global objects rely on order of construction; Additionally, I don't use constructors for anything other than zeroing out memory in the object before it is used. Constructors for kernel objects do nothing more than initialize them to a safe state, and that safe state is usually memset(&obj, 0, sizeof(object)).
Most classes have an "initialize()" method. That's because constructors can't return values. You'll probably end up doing something similar in your own project.
--Peace out
gravaera
My kernel is in C++, and I basically do not, for global objects rely on order of construction; Additionally, I don't use constructors for anything other than zeroing out memory in the object before it is used. Constructors for kernel objects do nothing more than initialize them to a safe state, and that safe state is usually memset(&obj, 0, sizeof(object)).
Most classes have an "initialize()" method. That's because constructors can't return values. You'll probably end up doing something similar in your own project.
--Peace out
gravaera
17:56 < sortie> Paging is called paging because you need to draw it on pages in your notebook to succeed at it.
- Owen
- Member
- Posts: 1700
- Joined: Fri Jun 13, 2008 3:21 pm
- Location: Cambridge, United Kingdom
- Contact:
Re: C++ initialization mess
...because they have an implicit return value, this. I don't see why you would need another, and why, if you do, returning it by reference is an inadequate solution.gravaera wrote:Most classes have an "initialize()" method. That's because constructors can't return values. You'll probably end up doing something similar in your own project.
- gravaera
- Member
- Posts: 737
- Joined: Tue Jun 02, 2009 4:35 pm
- Location: Supporting the cause: Use \tabs to indent code. NOT \x20 spaces.
Re: C++ initialization mess
Hi:
--All the best,
gravaera
Code: Select all
class foo
{
public:
foo(void){};
};
int main(void)
{
/* Below we can see that the constructor does not return a value,
but rather the OPERATOR new returns a value. C++ constructors
do not return values. The compiler inserts calls to the constructor
where deemed fit, and the user cannot call the constructor without
placement new.
Additionally the return value is not "foo *this", but rather "void *newmem"
as returned by the new() operator, and the new() operator auto-casts
the "void *newmem" to "foo *newmem" and takes care of the type
safety.
The implementation details for how the compiler calls and handles constructor
calling are private to the compiler. All the programmer needs to care about
is that whatever memory WAS constructed is returned by "void *operator new(size_t sz)"
or else NULL is returned on failure.
**/
foo *pf = new foo;
/* In the next case we examine the non pointer situation: What would be the use
of the constructor returning "foo *this"?
**/
foo f;
}
gravaera
17:56 < sortie> Paging is called paging because you need to draw it on pages in your notebook to succeed at it.
- Owen
- Member
- Posts: 1700
- Joined: Fri Jun 13, 2008 3:21 pm
- Location: Cambridge, United Kingdom
- Contact:
Re: C++ initialization mess
Wow, useless language lawyering. And you missed the main point of my post...
- gravaera
- Member
- Posts: 737
- Joined: Tue Jun 02, 2009 4:35 pm
- Location: Supporting the cause: Use \tabs to indent code. NOT \x20 spaces.
Re: C++ initialization mess
What was your point? :O
17:56 < sortie> Paging is called paging because you need to draw it on pages in your notebook to succeed at it.
Re: C++ initialization mess
But there was no point to your post. It was simply completely wrong. A constructor has no return value, period. You're probably being confused about statements such as:Owen wrote:Wow, useless language lawyering. And you missed the main point of my post...
var = X(a,b);
with X naming a class. It looks just like a function call "returning" a value that is assigned to var, but it isn't. This is an explicit type conversion, meaning that the function that it appears in allocates a temporary X and invokes the appropriate constructor to initialize it. The value of the expression is not the return value from a function.
- Owen
- Member
- Posts: 1700
- Joined: Fri Jun 13, 2008 3:21 pm
- Location: Cambridge, United Kingdom
- Contact:
Re: C++ initialization mess
No; I perfectly understand the C++ semantics; I simply phrased my statement wrong. Rather than implicit, I should have said effective. In other words: In every context in which you construct an object, the object is the effective return value of that operation.Gigasoft wrote:But there was no point to your post. It was simply completely wrong. A constructor has no return value, period. You're probably being confused about statements such as:Owen wrote:Wow, useless language lawyering. And you missed the main point of my post...
var = X(a,b);
with X naming a class. It looks just like a function call "returning" a value that is assigned to var, but it isn't. This is an explicit type conversion, meaning that the function that it appears in allocates a temporary X and invokes the appropriate constructor to initialize it. The value of the expression is not the return value from a function.
My real question was why gravaera would need an additional return value - or - what he saw as deficient in the facilities the language provides.
- gravaera
- Member
- Posts: 737
- Joined: Tue Jun 02, 2009 4:35 pm
- Location: Supporting the cause: Use \tabs to indent code. NOT \x20 spaces.
Re: C++ initialization mess
Yo:
Kay lemme wrap this up:
I prefer the latter because in the first, if I chose to use the "is_initialized" style to check if objects were initialized properly (in other words, for classes which allocate memory on construction, the check would be for whether or not memory was available) I would have to have an extra variable in each object just to check if the object was initialized properly. Most objects are relatively small in actual size, and can be allocated from an object cache (slab/slub style). I'd rather not waste 1-4 bytes on each object. That's just me, and Owen you're free to do whatever you want in your kernel. That said, it's probably better to just reply to the OP with your suggestions when there's no need to reply to another member before giving your suggestion. You could have just proposed your method without involving me at all altogether.
Both methods are functionally suitable...one makes more sense to me.
--Peace out
gravaera
Kay lemme wrap this up:
Code: Select all
class foo
{
public:
foo(void) { do_init_stuff(); is_initialized = 1; };
int is_initialized;
};
void main(void)
{
foo f;
if (!f.is_initialized) { printf("zomg error"); };
}
Code: Select all
class foo
{
public:
foo(void) {};
int initialize(void) { do_init_stuff(); return 1; };
};
void main(void)
{
foo f;
if (!f.initialize()) { printf("zomg error"); };
}
Both methods are functionally suitable...one makes more sense to me.
--Peace out
gravaera
17:56 < sortie> Paging is called paging because you need to draw it on pages in your notebook to succeed at it.
- Owen
- Member
- Posts: 1700
- Joined: Fri Jun 13, 2008 3:21 pm
- Location: Cambridge, United Kingdom
- Contact:
Re: C++ initialization mess
The method that makes sense to me is...
i.e. using the language as intended, and letting RAII clean up afterwards
Code: Select all
Class::Class()
{
tryAndDoSomeInitializationStuff();
if(thatFailed) throw SomeException(maybeWithSomeInformation);
}