C++ Handling Objects System Wide
C++ Handling Objects System Wide
Recently I switched to C++. Whenever I want to call a function from a different file I need to create a new object. How can I get around this? Imagine having 340 files and you need to use a specific function from foo.h that means 340 different foo objects. Is there a way to create a global system wide place that would hold all those objects (single instances of different objects)? In C to call a function you just needed to include a header. In C++ you need to create an object for that class in order to call that specific function. Any solutions?
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
Re: C++ Handling Objects System Wide
You can declare global variables of any type, including objects, in C++ just as you can in C. Whether this is good design is another matter.
http://stackoverflow.com/questions/1882 ... bally-in-c
http://stackoverflow.com/questions/1882 ... bally-in-c
Re: C++ Handling Objects System Wide
But how would that work? I want to declare all the objects inside my Kernel.cpp file. But that file itself does not have any classes. Also I would have to access it from other files.
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: C++ Handling Objects System Wide
I gather part of the issue is that you are expecting that you will need to have all the functions operating on the objects as method of the class; this isn't necessarily the case.
The purpose of OOP is to treat objects as atomic structures, the internals of which are (as far as reasonably possible) only operated on by their instance methods. The fundamental metaphor is of separate active parties ('actors', if you will, though the actual Actor Model takes the idea a lot further) communicating back and forth with each other. The fundamental difference between classes and Abstract Data Types - which most people tend to ignore - is that classes are meant to define something that behaves like real-world objects do (even when the 'object' is a purely abstract one), rather than just hold pieces of data.
As a matter of general design principles, for both ADTs and classes, you want to design a minimal interface with the world outside the type/class encapsulation, which performs all the actual manipulation of the fields/instance variables. Anything that isn't reasonably part of the 'outside' of the object - what it can be told to do, and what you need it to do to itself - should not be part of the class definition itself. The idea is that the class defines things that are properties and capabilities of the objects, not things that can be done to the objects.
Mind you, the metaphor is a bit shaky in places: in the real world, a doctor doesn't get a person's chest sounds (to try and detect a murmur or listen for wheezing in the lungs, for example) by asking them to tell them what it sounds like, they use a stethoscope to listen to it directly. The sounds their chest makes, while a result of internal state and 'technically' something that would be private in a programmed object, is itself something of an 'interface' for a properly-equipped doctor, even though it is purely incidental to the patient's physiology (i.e., the sounds are part of the processes, just a result of them).
The point is that is it very hard to get the right balance between not enough formal interfaces, meaning that the class needs to expose internals unnecessarily, and too many, meaning that things which shouldn't really part of the class are wrapped into, or even having both cases in different aspects a of a class. Good class design is hard. The main reason is is still useful (in the specific set of places where it actually is - OOP is only really well-suited for certain types of problems, with device drivers and process management being two of them as it happens) is because it let's you wrap a lot of complexity up into relatively neat and tidy black boxes when done well. All abstractions leak, but a good class design will leak a lot less than a shoddy one.
The purpose of OOP is to treat objects as atomic structures, the internals of which are (as far as reasonably possible) only operated on by their instance methods. The fundamental metaphor is of separate active parties ('actors', if you will, though the actual Actor Model takes the idea a lot further) communicating back and forth with each other. The fundamental difference between classes and Abstract Data Types - which most people tend to ignore - is that classes are meant to define something that behaves like real-world objects do (even when the 'object' is a purely abstract one), rather than just hold pieces of data.
As a matter of general design principles, for both ADTs and classes, you want to design a minimal interface with the world outside the type/class encapsulation, which performs all the actual manipulation of the fields/instance variables. Anything that isn't reasonably part of the 'outside' of the object - what it can be told to do, and what you need it to do to itself - should not be part of the class definition itself. The idea is that the class defines things that are properties and capabilities of the objects, not things that can be done to the objects.
Mind you, the metaphor is a bit shaky in places: in the real world, a doctor doesn't get a person's chest sounds (to try and detect a murmur or listen for wheezing in the lungs, for example) by asking them to tell them what it sounds like, they use a stethoscope to listen to it directly. The sounds their chest makes, while a result of internal state and 'technically' something that would be private in a programmed object, is itself something of an 'interface' for a properly-equipped doctor, even though it is purely incidental to the patient's physiology (i.e., the sounds are part of the processes, just a result of them).
The point is that is it very hard to get the right balance between not enough formal interfaces, meaning that the class needs to expose internals unnecessarily, and too many, meaning that things which shouldn't really part of the class are wrapped into, or even having both cases in different aspects a of a class. Good class design is hard. The main reason is is still useful (in the specific set of places where it actually is - OOP is only really well-suited for certain types of problems, with device drivers and process management being two of them as it happens) is because it let's you wrap a lot of complexity up into relatively neat and tidy black boxes when done well. All abstractions leak, but a good class design will leak a lot less than a shoddy one.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: C++ Handling Objects System Wide
Uhm... I think you've got a few different questions you need to resolve here.octacone wrote:But how would that work? I want to declare all the objects inside my Kernel.cpp file. But that file itself does not have any classes. Also I would have to access it from other files.
I am assuming that the class declarations are all in headers, and the source files only contain the (non-inline parts of the) implementations, correct? That part works just like declaring typedefs in C, for the most part (The article Why function implementations shouldn't be put In header files touches this topic, but from the opposite direction, sort of, while the specific point is made in this Daniweb post of mine). As I said, this is something I expect you know, I just wanted to make certain.
As for the actual global objects, you can use an extern declaration in a header file for objects just the same way you can with POD variables. You might not want to expose all or even most of the objects that way - again, the name of the game is getting all the interfaces you need to communicate with the kernel, but no more than that - and anything else you need to pass back and forth should probably be done by something like a callback or a Factory instead.
Wait, something just occurred to me: this isn't meant to expose the kernel operations to the userland applications, is it? That's a separate issue entirely - any place where non-privileged code has to interface with privileged code should be through system calls. The OS should never expose any of its internals to non-privileged applications. I assume that this isn't what you mean, but I wanted to make sure we were in agreement on that.
Last edited by Schol-R-LEA on Sun Apr 02, 2017 8:52 am, edited 1 time in total.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Re: C++ Handling Objects System Wide
Oh! Totally forgot about extern declaring. Yes, class declarations (+variable declarations) are stored inside the headers, and the source files only contain the relevant code. Actually this has nothing to do with the functions themselves, in order to access them I need to access their instantiated objects.Schol-R-LEA wrote:Uhm... I think you've got a few different questions you need to resolve here.octacone wrote:But how would that work? I want to declare all the objects inside my Kernel.cpp file. But that file itself does not have any classes. Also I would have to access it from other files.
I am assuming that the class declarations are all in headers, and the source files only contain the (non-inline parts of the) implementations, correct? That part works just like declaring typedefs in C, for the most part.
As for the actual global objects, you can use an extern declaration in a header file for objects just the same way you can with POD variables. You might not want to expose all or even most of the objects that way - again, the name of the game is getting all the interfaces you need to communicate with the kernel, but no more than that - and anything else you need to pass back and forth should probably be done by something like a callback or a Factory instead.
Wait, something just occurred to me: this isn't meant to expose the kernel operations to the userland applications, is it? That's a separate issue entirely - any place where non-privileged code has to interface with privileged code should be through system calls. The OS should never expose any of its internals to non-privileged applications. I assume that this isn't what you mean, but I wanted to make sure we were in agreement on that.
For example:
Code: Select all
//Test.cpp
...
void Test_Class::Test_Function()
{
TTY.Put_String("Hello Earth!");
}
void Another_Class::Another_Function_2()
{
TTY.Put_String("Hello Mars!");
}
...
//TestFile.h
...
Text_Type_Interface TTY;
...
//AnotherFile.h
...
Text_Type_Interface TTY;
...
Edit: where did that second reply appear from?
I understand what you are saying, but isn't instantiating one object for n times wasteful, when you can instantiate it only once and use it for n times.
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: C++ Handling Objects System Wide
Oops, my bad habit of editing and re-editing posts did that, I think.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Re: C++ Handling Objects System Wide
Just declare a single instance of class TTY outside any other class or function, if that's the way you want to do it. Just like declaring a global variable in C.
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: C++ Handling Objects System Wide
What I mean is, the user applications shouldn't be able to call anything in the kernel space directly, which is true regardless of the language you are working in.
OK, more stuff of the 'you should already know this' variety, just to make sure we agree on things. Don't take this the wrong way, I just want to make things as clear as possible.
Protected mode operating systems have different levels of privilege, which control the ability to use certain instructions, access certain memory areas, etc. Applications usually have very limited privileges, while the kernel itself has to have more or less unlimited privileges.
In x86, there are four privilege levels (CPL0 through CPL3, often called 'ring 0' through 'ring 3'), but most OSes don't use CPL1 and CPL2 at all, simply using the same absolute 'root/user' division as Unix.
In order for an application in CPL3 to do anything that requires CPL0 (supervisor, or root) privilege, it has to make one or more System Calls - specific actions that change privilege level in a controlled way, and prevents the code outside of the OS proper from running at all while the process is in the privileged state.
(Depending on the design, the 'OS proper' may or may not include loadable modules such as drivers. Classic Monolithic kernels have no loadable parts, but require everything to be linked into a single executable image at compile time. Classic Microkernels move things like drivers out of the kernel entirely and define them as separate user processes which the applications communicate with via IPC - in some cases, the message-passing primitives are the only system calls the OS provides, and the OS itself is also a separate process, unlike most Higher Half Kernel designs. Most OSes since the early 1990s have been Hybrid Kernel designs instead, meaning that the drivers and other modules are privileged, but the OS is able to load modules into supervisor space dynamically.)
There are a number of ways that an OS can provide system calls; older x86 systems usually used soft interrupts similar to those in MS-DOS (such as the Linux INT 0x80 interrupt), and some use 'call gates', but newer x86 OSes almost always implement a handler for the SYSENTER instruction (or the SYSCALL instruction in Long mode) instead.
In most cases, the application programmers do not use the system calls directly, but use libraries which wrap them up and provide a cleaner API - indeed, a lot of the stdio.h library functions were originally just light wrappers around Unix system calls. I think part of the confusion you are having is that you are thinking in terms of the API rather than the actual system interface.
This has absolutely nothing to do with how the OS itself is written; it is just the basic design used by most operating systems running on CPUs with separate supervisor modes since that sort of thing was introduced back in the 1960s.
As for using one object in several places, well, that's what pointers and reference variables are for - you would do the same thing in C, anyway, right? However, as I said, the object in question wouldn't be part of the OS, but would be part of the API that is running in userland instead, so each process would still have its own separate one regardless. There may be a corresponding abstraction in the OS, but the applications would not be accessing those directly, ever, at least not with most OS designs (something like Synthesis is somewhat of an exception, though even there the access is mediated through system calls).
OK, more stuff of the 'you should already know this' variety, just to make sure we agree on things. Don't take this the wrong way, I just want to make things as clear as possible.
Protected mode operating systems have different levels of privilege, which control the ability to use certain instructions, access certain memory areas, etc. Applications usually have very limited privileges, while the kernel itself has to have more or less unlimited privileges.
In x86, there are four privilege levels (CPL0 through CPL3, often called 'ring 0' through 'ring 3'), but most OSes don't use CPL1 and CPL2 at all, simply using the same absolute 'root/user' division as Unix.
In order for an application in CPL3 to do anything that requires CPL0 (supervisor, or root) privilege, it has to make one or more System Calls - specific actions that change privilege level in a controlled way, and prevents the code outside of the OS proper from running at all while the process is in the privileged state.
(Depending on the design, the 'OS proper' may or may not include loadable modules such as drivers. Classic Monolithic kernels have no loadable parts, but require everything to be linked into a single executable image at compile time. Classic Microkernels move things like drivers out of the kernel entirely and define them as separate user processes which the applications communicate with via IPC - in some cases, the message-passing primitives are the only system calls the OS provides, and the OS itself is also a separate process, unlike most Higher Half Kernel designs. Most OSes since the early 1990s have been Hybrid Kernel designs instead, meaning that the drivers and other modules are privileged, but the OS is able to load modules into supervisor space dynamically.)
There are a number of ways that an OS can provide system calls; older x86 systems usually used soft interrupts similar to those in MS-DOS (such as the Linux INT 0x80 interrupt), and some use 'call gates', but newer x86 OSes almost always implement a handler for the SYSENTER instruction (or the SYSCALL instruction in Long mode) instead.
In most cases, the application programmers do not use the system calls directly, but use libraries which wrap them up and provide a cleaner API - indeed, a lot of the stdio.h library functions were originally just light wrappers around Unix system calls. I think part of the confusion you are having is that you are thinking in terms of the API rather than the actual system interface.
This has absolutely nothing to do with how the OS itself is written; it is just the basic design used by most operating systems running on CPUs with separate supervisor modes since that sort of thing was introduced back in the 1960s.
As for using one object in several places, well, that's what pointers and reference variables are for - you would do the same thing in C, anyway, right? However, as I said, the object in question wouldn't be part of the OS, but would be part of the API that is running in userland instead, so each process would still have its own separate one regardless. There may be a corresponding abstraction in the OS, but the applications would not be accessing those directly, ever, at least not with most OS designs (something like Synthesis is somewhat of an exception, though even there the access is mediated through system calls).
Last edited by Schol-R-LEA on Sun Apr 02, 2017 9:46 am, edited 1 time in total.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Re: C++ Handling Objects System Wide
Yeah, I know how system calls work, privileged instructions, that is quite clear to me. I wasn't planning on doing anything with them at all. I am fully aware that the userspace applications shouldn't be able to use those privileged instructions. That is why CPL3 exists. Protection is always questionable, since kernel has all the power to do anything he wants. Then why not just allow it to do anything. No userspace, no CP3, at the moment, I am talking about the pure kernel ring usage. Maybe my question was a bit funky. My mind is all over the place. I've seen other people (other C++ based kernels) calling system wide functions. I don't understand their concept. Let me do a diagram:
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: C++ Handling Objects System Wide
It sounds like what you want is either a Singleton class, where there is only a single instance of the class is created and every subsequent instantiation via the c'tor is just a pointer or reference to that one instance, or Object Pool class, which creates a pool of existing objects and returns a reference to one of those pooled objects wherever a new one is needed, then returns the object to the pool when it is finished.
Three other patterns to look at are Flyweight, where some or most of the state is shared between all the objects of the class (as class/static variables rather than instance variables) and only the part that is specific to an instance is actually part of the object; Observer, where an object which several other parts of the program need to keep tabs on (such as keyboard input) can be passed callbacks which it can use to call the objects which need to watch it, rather than vice versa; and Factory, which can pick an appropriate subclass for a given type (which could help when subclassing, say, drivers).
Three other patterns to look at are Flyweight, where some or most of the state is shared between all the objects of the class (as class/static variables rather than instance variables) and only the part that is specific to an instance is actually part of the object; Observer, where an object which several other parts of the program need to keep tabs on (such as keyboard input) can be passed callbacks which it can use to call the objects which need to watch it, rather than vice versa; and Factory, which can pick an appropriate subclass for a given type (which could help when subclassing, say, drivers).
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: C++ Handling Objects System Wide
As a disclaimer to the last post, you should see these patterns as guidelines or inspirations, not as straitjackets, and definitely not as libraries of existing code.
Patterns primarily serve as a common language for things programmers were already doing but didn't have names for, many of them things which show up spontaneously in very different kinds of programs just out of the nature of what the program is doing.
The original purpose of design patterns was not to codify new development techniques (though they are certainly useful for new designs), but to give a language for describing common idioms in existing code so the places they are being kinda-sorta informally used can be identified, allowing the pattern to used as a guide for how best to make changes to the existing code (whether for fixing bugs, or adding new features, or cleaning the code up to make it easier to maintain later).
For new designs, they should be seen mainly as a way to crystallize ideas that haven't quite gelled yet, and as a way of giving meaningful and familiar names to different possible design options which anyone working on a project can recognize immediately while brainstorming together. They let you talk about problems in an abstract way when going over possible ways to approach it.
Trying to reuse the specific code isn't necessarily part of this; trying to reuse the ideas is what patterns are all about.
Patterns primarily serve as a common language for things programmers were already doing but didn't have names for, many of them things which show up spontaneously in very different kinds of programs just out of the nature of what the program is doing.
The original purpose of design patterns was not to codify new development techniques (though they are certainly useful for new designs), but to give a language for describing common idioms in existing code so the places they are being kinda-sorta informally used can be identified, allowing the pattern to used as a guide for how best to make changes to the existing code (whether for fixing bugs, or adding new features, or cleaning the code up to make it easier to maintain later).
For new designs, they should be seen mainly as a way to crystallize ideas that haven't quite gelled yet, and as a way of giving meaningful and familiar names to different possible design options which anyone working on a project can recognize immediately while brainstorming together. They let you talk about problems in an abstract way when going over possible ways to approach it.
Trying to reuse the specific code isn't necessarily part of this; trying to reuse the ideas is what patterns are all about.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Re: C++ Handling Objects System Wide
With respect to your diagram, you wouldn't declare a variable in a C header file. If you did you are liable to have "multiple declaration" problems. So you wouldn't do the same in C++ either. Declare the variable in one of your code files, say kernel.cpp and then make extern declarations to it in othe files.
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: C++ Handling Objects System Wide
@iansjack Trust me to overthink things. Thank you for giving the right answer where I went overboard with a wrong (or at least excessive) one.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Re: C++ Handling Objects System Wide
Just use static functions, not TTY.write() but TTY::write()
This won't work if you need to carry some state around, but TTY state is usually global enough to make it static too.
This won't work if you need to carry some state around, but TTY state is usually global enough to make it static too.
Learn to read.