OSDev.org

The Place to Start for Operating System Developers
It is currently Sun May 05, 2024 7:37 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: C to C++
PostPosted: Thu Jan 03, 2008 3:00 pm 
Offline
Member
Member
User avatar

Joined: Mon Apr 09, 2007 10:50 am
Posts: 49
Location: UK, Hants
Hey,

I have started developing my os using a combination of the Kernel C++ tutorial over at Bona Fide and Brans kernel development tutorials. I have completed the first tutorial which has left me with try to convert Brans C code to C++.

I have started out with converting the basic functions he implemented early on into a basic system class. He uses some inline assemble, but i assume there is no problem with this? Is there any problem with my plan :wink: about converting these functions to classes?

Any tips, please let me know. Thank you for all your help!
Harry.

_________________
Reflect Desktop Operating System - ' You only remember the name of the OS when it crashes '


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 03, 2008 4:22 pm 
Offline

Joined: Wed Jan 02, 2008 1:19 pm
Posts: 8
Location: Oxfordshire, UK
It's pretty much what I'm doing, too. :lol: I started out on that same tutorial.

The main things to be aware of when doing a kernel in C++ can be found here:
http://www.osdev.org/wiki/C_PlusPlus

Calling object methods in C++ doesn't make the CPU do anything weirder than if you called a C function with a single parameter. (Why single parameter? Effectively it does pass a parameter to the method, the pointer to the object itself - otherwise it has no way to figure out whose member variables you're referencing! ;) )

I preferred to implement things using namespaces where a single-instance were used (GDT, IDT...) and did toy with a Cursor class, where you could just do a ++ on its object and it'd advance the cursor by one space :) This was used by a Screen class, which was just "ColourScreen" (where I intended to maybe later add MonoScreen as an alternative, though I see little point!)

Inline asm shouldn't pose any problems. As far as I can tell, anyway.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 03, 2008 4:36 pm 
Offline
Member
Member
User avatar

Joined: Mon Apr 09, 2007 10:50 am
Posts: 49
Location: UK, Hants
Thanks for that!

I am not sure about object methods, though after a bit of research i am sure to find the answer.

What did you go onto after you found Bran's Kernel Development tutorial?

Also, here is how I am implementing my system class:

System.h

Code:
#ifndef SYSTEM_H
#define SYSTEM_H // So we dont get multiple declarations

class System
{
public:
    void *memcpy(void *dest, const void *src, size_t count);
    void *memset(void *dest, char val, size_t count);
    unsigned short *memsetw(unsigned short *dest, unsigned short val, size_t count);
    size_t strlen(const char *str);
    unsigned char inportb (unsigned short _port);
    void outportb (unsigned short _port, unsigned char _data);
};

#endif





System.cpp

Code:

#include "include\System.h"
#include "include\Video.h"

Video vid;

System::System()
{
   vid.Write("System Starting up....");
}

System::~System();
{
   vid.Write("System Shutting down....");
}

// This function is the basic outline of how i would impliment the others

void *memcpy(void *dest, const void *src, size_t count)
{

}



This is the basic outline of how I would impliment the functions that were in Brans' Tutorial. Is this ok? This is a fairly major step in my C++ codeing world so please forgive me if there are some stupid mistakes!

Many Thanks,
Harry.

_________________
Reflect Desktop Operating System - ' You only remember the name of the OS when it crashes '


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 03, 2008 7:02 pm 
Offline

Joined: Wed Jan 02, 2008 1:19 pm
Posts: 8
Location: Oxfordshire, UK
OK a brief explanation of what happens when you call a C function vs. a C++ object method (to my knowledge, anyway - some inaccuracies may be present!)

Say you have a "memset" function (as you do ;) ) - in C, you'd have something like this:
Code:
void* memset(void* dest, char value, unsigned int count);


Which, if I'm not mistaken, typically becomes:
Code:
push count
push value
push dest
call memset
; your memset code here... at the end of memset "ret" is called
mov result, eax


Now, if you want to do this the way you're doing it, you'd first need to create an instance of the System class. The class acts like structural DNA - it describes an object, and you can quite happily create instance after instance of it.

In this case, you'd probably only have one single System, right? I'll pick back up on this in a moment...

When you call the memset within a class, you'd be calling it like this I guess:
Code:
System the_computer;
the_computer.memset(&foo, 0, 1234);


Consider if you had more than one System instance - there needs to be a way to work with each one individually. To save space in memory, your class's methods will usually be shared among instances, and to differentiate between each instance, a "this" pointer is passed to the methods.

So your "memset" effectively has an additional parameter, which would get pushed on the stack prior to the function call taking place. If you were doing this in C, it'd probably look like:
Code:
void* memset(System* this, void* dest, char value, unsigned int count);


This may seem redundant in this example - and it is. If you had more than one System object, however, you'd be able to tell which one the method was called on.

So IMO it's not really worth creating an object for something you're only going to have a single instance of and be using frequently.

My own approach has been to use namespaces, which basically lets you divide up functions into named groups, which seems more appropriate. So you could have System::memset and System::outportb. The beauty of this is that it allows you to have something that *looks* object-oriented, without any of the overhead associated with classes/objects.

Not saying that classes/objects are evil, of course - my own implementation has a few things like a Port class which is declared as inline and implemented in a header file.

Works like this:
Code:
Port master_pic_command(0x20);
Port master_pic_data(0x21);
master_pic_data.writeByte(0xff);

(note: the above port I/O with regard to the PIC may be incorrect as it's just a random example!)

In this case, I have some information associated with the object (a port number) which I'd have to push on the stack for the function call anyway otherwise ;) This is by no means the most optimal way to do it (most optimal way is probably not to use objects again, but since there may be multiple Ports this seemed sensible).

Sorry for rambling there...

And I just realised that you're using the System class to represent the kernel (startup/shutdown) - that'd work, but again I personally don't see the advantage.

When the kernel runs, it'll just ultimately loop repeatedly. Something else is likely to take care of shutdown/restarts. So as far as my own kernel main() goes, I just set the environment up by using the objects and namespaces I've created.

Effectively it's that tutorial but with some fluff added to it ;)

Another thing to beware of - name mangling. Make sure whatever code gets called from external asm (including startup code) is marked extern "C" otherwise the linker will probably complain.

Hope this helps?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 04, 2008 2:35 am 
Offline
Member
Member
User avatar

Joined: Thu Nov 16, 2006 12:01 pm
Posts: 7614
Location: Germany
djnorthyy wrote:
Also, here is how I am implementing my system class:

System.h

Code:
#ifndef SYSTEM_H
#define SYSTEM_H // So we dont get multiple declarations

class System
{
public:
    void *memcpy(void *dest, const void *src, size_t count);
    void *memset(void *dest, char val, size_t count);
    unsigned short *memsetw(unsigned short *dest, unsigned short val, size_t count);
    size_t strlen(const char *str);
    unsigned char inportb (unsigned short _port);
    void outportb (unsigned short _port, unsigned char _data);
};

#endif



Hmmm... why put those functions into a class at all? Do you intend to have state (data members) added to your System class (which I would advise against), or do you intend to have more than one System instance on which to apply those functions?

I smell "because I can" here. Maybe "System.memcpy()" looks "cool", but where is the added value to simply calling memcpy(), or System::memcpy() (introducing a namespace System) if you must?

Coding in C++ does, by no means, mean that everything has to be a class. You simply get more ways to express things, which you should use where required and where meaningful.

No offense intended, but could it be that you don't have much coding experience in C++? If that's the case, don't use it for your kernel (IMHO).

_________________
Every good solution is obvious once you've found it.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 04, 2008 3:03 am 
Offline
Member
Member
User avatar

Joined: Tue Jul 10, 2007 5:27 am
Posts: 2935
Location: York, United Kingdom
@Solar: I personally wrap my memset/memcpy functions in a Kernel class. Because I like it that way. It's a kernel function - I have no need for global functions, and in fact IIRC many C++ books seem to call for the hangman at the mere mention of the word 'global'.

@Others: Have you considered making those functions static members of the System class? That means that they function identically to global functions defined in a namespace but you define them nicely in your class.

Code:
class System
{
public:
  static int memcpy(u32int *dest, u32int *src);
};


Remember that you can't give a this pointer to a static member function, so you call it thus:

Code:
System::memcpy(dest, src);


James

_________________
Horizon - a framework and language for SAS-OS development
Project 'Pedigree'
Practical x86 OSDev tutorials


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 04, 2008 3:05 am 
Offline
Member
Member
User avatar

Joined: Sun Oct 22, 2006 7:01 am
Posts: 2646
Location: Devon, UK
I agree. I'm writing a kernel in C++ and to start with, everything was put inside global classes. It very quickly became apparrent that there was none of this "added value" in a lot of my classes.

If you a) don't need more than one instance of a class and b) don't have private member variables that need to be set in a certain way via functions, then chances are you are looking more for a namespace than a class.

Classes become a lot more useful a bit later on when you are writing process/thread control structures and when using linked-list / binary tree template classes.

Cheers,
Adam


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 04, 2008 4:25 am 
Offline
Member
Member
User avatar

Joined: Thu Nov 16, 2006 12:01 pm
Posts: 7614
Location: Germany
JamesM wrote:
It's a kernel function - I have no need for global functions, and in fact IIRC many C++ books seem to call for the hangman at the mere mention of the word 'global'.


Global variables, yes. But if all you want is to limit the scope of functions, namespace is the tool you're looking for. I'd suggest kernel:: instead of System::, both because lowercase is "standard" in C/C++ and because System:: smells of Java.

A class, by definition, defines a data structure and the functions that operate on it. strlen() and inportb() don't operate on common data, and really have nothing in common but a lose categorization as "system functions". More, one is a C standard function (so why not having it in <cstring> where it belongs?), and the other is a non-standard function, so why mash them into a class?

Sorry, but this is violating pretty basic C++ design concepts. It smells of Java, and points to the OP not being "at home" with the language - which, in my opinion, means he's better off not trying to write an OS kernel in it.

PS: A focus on making everything a class, as well as doing (too) many things by operator overloading, is one of the symptoms of a C++ beginner. It's the kind of "because I can"-mentality that sets in after reading a C++ book (which, unfortunately, tend to put much too much focus on classes), and is usually healed after a couple of years of "real" work. C++ offers the whole spectrum from procedural (C-style) over OO (Java-style) to generics (what I would call C++-style). Use what is appropriate, don't get caught in a paradigma or ideology.

_________________
Every good solution is obvious once you've found it.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 04, 2008 5:29 am 
Offline

Joined: Sun Dec 16, 2007 9:47 am
Posts: 7
Location: The (Other) Counterweight Continent
I suggest reading Bjarne Stroustrup's guidelines on class definition.

The basic guideline is that classes are only really needed when you have an invariance of some kind to maintain. If you only have methods, as Solar said, put them in a namespace. If you only have getters and setters (something almost unavoidable in Java) just use a struct. There's also the pImpl design pattern which is related, but not about methods of a class.

Furthermore, Bjarne Stroustrup (and from my own experience), you have to make a distinction between methods that are intrinsic to the operation of the class (ie maintains an invariant) and methods that act on classes. The example he uses is the Date class. In most cases, class methods for converting a Date class from using UK format to ISO format are superfluous to maintaining the Date's invariances (such an invariance may be ensuring no Date contain a 13th month). Such methods are much better suited for a library which act on Date objects.

The whole "System" object design pattern basically contains a whole lot of methods that do/need not maintain an invariant. All the methods suggested as part of the class are actually those methods that may act on objects rather than methods that are an intrinsic property of that class of objects.

I actually learned the hard way many things I later found out by reading, for example, Bjarne Stroustrup's C++ FAQ.

_________________
"Pissing people off since 1986."

"Edible: n, As in a worm to a toad, a toad to a snake, a snake to a pig, a pig to a man, and a man to a worm."


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 04, 2008 8:07 am 
Offline
Member
Member
User avatar

Joined: Tue Jul 10, 2007 5:27 am
Posts: 2935
Location: York, United Kingdom
Solar wrote:
JamesM wrote:
It's a kernel function - I have no need for global functions, and in fact IIRC many C++ books seem to call for the hangman at the mere mention of the word 'global'.


Global variables, yes. But if all you want is to limit the scope of functions, namespace is the tool you're looking for. I'd suggest kernel:: instead of System::, both because lowercase is "standard" in C/C++ and because System:: smells of Java.


The naming convention (using camel-case etc) is completely dependent on programmer taste. My company uses camel-case for all classes (with capitalised first letter) and camel-case for all functions/vars (with lowercase first letter), so that is what I use in my own code.

Code:
A class, by definition, defines a data structure and the functions that operate on it. strlen() and inportb() don't operate on common data, and really have nothing in common but a lose categorization as "system functions". More, one is a C standard function (so why not having it in <cstring> where it belongs?), and the other is a non-standard function, so why mash them into a class?


In my personal code there *are* functions and internal variables in the Kernel class. The extras (inb,outb,strlen,etc) are put in there because that is the place I wanted to put them. They are static and so behave in *exactly* the same way to a namespace definition. And I don't have a <cstring> header file in my kernel. My kernel uses my own classes / ADTs, end of. I have a <cstring> in the userspace libraries, but that is a whole different beastie.

Quote:
Sorry, but this is violating pretty basic C++ design concepts. It smells of Java, and points to the OP not being "at home" with the language - which, in my opinion, means he's better off not trying to write an OS kernel in it.


What your complaining about here, imho, has nothing to do with C++, per se. It seems more to do with OO design principles, which can be flouted in any language. The concrete implementation of static class member functions and global functions defined in a namespace are, iirc, completely identical.
Quote:
PS: A focus on making everything a class, as well as doing (too) many things by operator overloading, is one of the symptoms of a C++ beginner. It's the kind of "because I can"-mentality that sets in after reading a C++ book (which, unfortunately, tend to put much too much focus on classes), and is usually healed after a couple of years of "real" work. C++ offers the whole spectrum from procedural (C-style) over OO (Java-style) to generics (what I would call C++-style). Use what is appropriate, don't get caught in a paradigma or ideology.


"Appropriate" is subjective. Given that multiple methodilogies result in an identical implementation, it seems hard to define what is an "appropriate" method for a given task.

And yes, throughout all the code that I'm working on here at work (which is quite substantial, 300000+ lines) there is not once single namespace definition. Everything is done as const static members. It is in our coding standard, and thus suggests to me, again, that the 'principle' being infringed here is entirely subjective.

_________________
Horizon - a framework and language for SAS-OS development
Project 'Pedigree'
Practical x86 OSDev tutorials


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jan 05, 2008 7:09 am 
Offline
Member
Member

Joined: Mon Apr 09, 2007 12:10 pm
Posts: 775
Location: London, UK
I agree its not sensible to keep things like C library functions in a class. A separate namespace is probably more useful (e.g. namespace kernel) as that way when you call the function it is more clear in the code that the function you are calling is not a part of the CRT or some gcc builtin but rather your own function. If you're using C++ and want to write in a more OO way you should probably consider looking at the problem a totally different way, for example make string and stream classes and use operator overloading on them.

An additional advantage of classes is that you can use them to make interfaces, so that you can easily port your os to different architectures. For example, the final kernel image of my os is to link the core.a library with an architecture-specific one. All hardware-dependent calls in core.a call through a generic interface. You could then, for example, use a port interface, where the in and out functions (which you can overload for different data sizes) are specific to the particular architecture you are using. I don't actually use it for something as fundamental as a port because the functions are called too often and calling via virtual functions is slow, however you can use it for many higher level functions (e.g. I have a vga class which implements both the console interface and the ostream interface. By simple switching to have the global 'ostream kout' object be a serial class instead, I can then dump startup information to a serial port).

Regards,
John.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jan 05, 2008 10:34 am 
Offline
Member
Member
User avatar

Joined: Mon Apr 09, 2007 10:50 am
Posts: 49
Location: UK, Hants
Thank you all very much for your help. I will try and take this all on board. I will also study OO much more throughly before I attempt to develop an os.

BTW:

Quote:
he's better off not trying to write an OS kernel in it.


I do have a name, you know!

Many thanks,
Harry.

_________________
Reflect Desktop Operating System - ' You only remember the name of the OS when it crashes '


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 07, 2008 12:34 am 
Offline
Member
Member
User avatar

Joined: Thu Nov 16, 2006 12:01 pm
Posts: 7614
Location: Germany
Apologies. I got into the habit of consciously not looking at who's actually asking the question, so I judge a question by it's face value, not by who asked it.

Regarding JamesM's namepace-less work project... *shudder* 8)

_________________
Every good solution is obvious once you've found it.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 07, 2008 2:31 am 
Offline
Member
Member
User avatar

Joined: Tue Jul 10, 2007 5:27 am
Posts: 2935
Location: York, United Kingdom
Solar:

Ooh, tell a lie, I did another grep and found 2 instances of namespace. It seems to be to be completely superflous, but perhaps you can tell me why they are used?

Code:
namespace A
{
   enum Type
  {
    ...
  };
}
typedef A::Type AType;


Now I think it would be better written as:
Code:
typedef enum
{
  ...
} AType;


Is there any reason it isn't?

_________________
Horizon - a framework and language for SAS-OS development
Project 'Pedigree'
Practical x86 OSDev tutorials


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 07, 2008 2:58 am 
Offline
Member
Member
User avatar

Joined: Thu Nov 16, 2006 12:01 pm
Posts: 7614
Location: Germany
A typedef to get rid of a namespace you defined yourself?

Ouch.

Looks like the whole thing was "designed" by someone not really "at home" either...

_________________
Every good solution is obvious once you've found it.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: Amazonbot [bot], Google [Bot] and 13 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group