C++: Difference between revisions

Jump to navigation Jump to search
245 bytes added ,  15 years ago
no edit summary
[unchecked revision][unchecked revision]
No edit summary
Line 24:
 
The following code applies to GCC:
<source lang="cpp">
<pre>
extern "C" void __cxa_pure_virtual()
{
// print error message
}
</presource>
 
The following code applies to Visual C++:
<source lang="cpp">
<pre>
int __cdecl _purecall()
{
// print error message
}
</presource>
 
If during runtime your kernel detects that a call to a pure virtual function couldn't be made, it calls the above functions. These functions should actually never be called, because without hacks (or through undefined behaviour of your kernel) it is not possible to instantiate a class that doesn't define all pure virtual functions. But nonetheless you have to define these functions or your linker will complain about unresolved symbols.
Line 47:
====Why?====
All objects have constructor and deconstructor code. When an executable code is loaded into memory, and the program jumps straight into main, the constructor code for each global object has not be run. You could do this manually, by calling in the top of <tt>main()</tt>:
<source lang="cpp">
<pre>
object1.object1();
object2.object2();
object3.object3();
// etc
</presource>
 
====Enabling global objects====
Line 67:
The construction of global/static objects is the same as of older versions of GCC. After you have called the objects constructor GCC automatically calls the function
 
<source lang="cpp">
int __cxa_atexit(void (* f)(void *), void *p, void *d);
</source>
 
<tt>f</tt> is a function-pointer to the destructor, <tt>p</tt> is the parameter for the destructor and <tt>d</tt> is the "home DSO" (DSO = dynamic shared object). This function should save all three parameters and if successful return zero, on failure nonzero. When your kernel exits you should call
 
<source lang="cpp">
void __cxa_finalize(void *d);
</source>
 
with d = 0 in order to destroy all with <tt>__cxa_atexit</tt> registered objects. Objects, which were registered first with <tt>__cxa_atexit</tt>, must be destroyed last by <tt>__cxa_finalize</tt>. You must provide the symbol <tt>__dso_handle</tt> in your executable. Only the address of this symbol is needed, because GCC calls <tt>__cxa_atexit</tt> with <tt>&__dso_handle</tt>.
 
<source lang="cpp">
<pre>
extern "C"
{
Line 112 ⟶ 116:
}
}
</presource>
 
=====Visual C=====
Line 124 ⟶ 128:
Here is some code. Sorry for its length, but it is hard to explain any other way. Simply call <tt>runInit()</tt> when you want to initialize any static objects and then call <tt>runTerm()</tt> when static object destructors are to be run.
 
<source lang="cpp">
<pre>
typedef void (*_PVFV)(void);
typedef int (*_PIFV)(void);
Line 218 ⟶ 222:
__declspec(allocate(".CRT$XIB")) static _PIFV pinit = onexitinit;
#pragma data_seg()
</presource>
 
===Local static variables (GCC only)===
Line 230 ⟶ 234:
Note, that these are only stubs to get the code compiled, and you should implement them yourself. Simply add a mutex like guard with test and set primitive.
 
<source lang="cpp">
<pre>
namespace __cxxabiv1
{
Line 256 ⟶ 260:
}
}
</presource>
 
Actual code, emited by GCC, to call local static variable's constructor looks something like this:
 
<source lang="cpp">
<pre>
static <type> guard;
if (!guard.first_byte) {
Line 277 ⟶ 281:
}
}
</presource>
 
===new and delete===
Line 290 ⟶ 294:
Every time you call one of the operators <tt>new()</tt>, <tt>new[]()</tt>, <tt>delete()</tt>, or <tt>delete[]()</tt>, the compiler inserts a call to them. The most simple implementation would be to map them to <tt>kmalloc()</tt> / <tt>kfree()</tt>: (or malloc() and free() depending on your implementation)
 
<source lang="cpp">
<pre>
//overload the operator "new"
void * operator new (uint_t size)
Line 314 ⟶ 318:
kfree(p);
}
</presource>
 
An easy malloc implementation you can port to your OS is [[liballoc]]. It only requires basic page management (that is, store a list of used and free pages, and have a function to find the next free page) to work.
Line 324 ⟶ 328:
In C++, and especially in OS code where structures can be found at fixed addresses, it can be useful to construct an object in memory obtained elsewhere. This is accomplished through a technique known as placement new. As an example, say you wanted to create an APIC object at address <tt>0x09fff0000</tt>. This snippet of code will use placement new to do the trick:
 
<source lang="cpp">
<pre>
void* apic_address = reinterpret_cast<void*>(0x09fff0000);
APIC* apic = new (apic_address) APIC;
</presource>
 
In order to use placement new, you need special overloads of the new and delete operators defined in scope. Fortunately, the required definitions are simple and can be inlined in a header file (the C++ standard puts them in a header called <new>).
 
<source lang="cpp">
<pre>
inline void* operator new(uint_t, void* p) throw() { return p; }
inline void* operator new[](uint_t, void* p) throw() { return p; }
inline void operator delete (void*, void*) throw() { };
inline void operator delete[](void*, void*) throw() { };
</presource>
 
The above implementation can be potentially unsafe for allocating memory, since your kernel does not mark the memory that was allocated as being used. Placement new is hardly ever used, and if you wish to read an object from a specified address in memory, it is usually easier to create a pointer to that address. [[liballoc]] does not support placement new.
Line 342 ⟶ 346:
You never call placement delete explicitly (it's only required for certain implementation detail reasons). Instead, you simply invoke your object's destructor explicitly.
 
<source lang="cpp">
<pre>
apic->~APIC();
</presource>
 
===Builtins===
Anonymous user
Cookies help us deliver our services. By using our services, you agree to our use of cookies.

Navigation menu