OSDev.org
https://forum.osdev.org/

C to C++
https://forum.osdev.org/viewtopic.php?f=1&t=15832
Page 1 of 2

Author:  djnorthyy [ Thu Jan 03, 2008 3:00 pm ]
Post subject:  C to C++

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.

Author:  Silver Blade [ Thu Jan 03, 2008 4:22 pm ]
Post subject: 

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.

Author:  djnorthyy [ Thu Jan 03, 2008 4:36 pm ]
Post subject: 

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.

Author:  Silver Blade [ Thu Jan 03, 2008 7:02 pm ]
Post subject: 

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?

Author:  Solar [ Fri Jan 04, 2008 2:35 am ]
Post subject: 

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).

Author:  JamesM [ Fri Jan 04, 2008 3:03 am ]
Post subject: 

@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

Author:  AJ [ Fri Jan 04, 2008 3:05 am ]
Post subject: 

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

Author:  Solar [ Fri Jan 04, 2008 4:25 am ]
Post subject: 

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.

Author:  GuiltySpark [ Fri Jan 04, 2008 5:29 am ]
Post subject: 

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.

Author:  JamesM [ Fri Jan 04, 2008 8:07 am ]
Post subject: 

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.

Author:  jnc100 [ Sat Jan 05, 2008 7:09 am ]
Post subject: 

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.

Author:  djnorthyy [ Sat Jan 05, 2008 10:34 am ]
Post subject: 

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.

Author:  Solar [ Mon Jan 07, 2008 12:34 am ]
Post subject: 

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)

Author:  JamesM [ Mon Jan 07, 2008 2:31 am ]
Post subject: 

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?

Author:  Solar [ Mon Jan 07, 2008 2:58 am ]
Post subject: 

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...

Page 1 of 2 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/