Mixing C/C++... Design Considerations.
Mixing C/C++... Design Considerations.
I was wondering, as I am about to re-write my kernel...
If I could code out the fundementals... IO and Paging / Memory Management, in C and then introduce a CPP file into my makefile (using the proper command lines, etc etc) Know I understand the deal about the contructors and destructors, Ive read the wiki and the pdf how-to on osdevers...
I just want to know if I will have any problems introducing classes later on?
"Cliff-notes"
Vanilla C Kernel, add in a C++ file with a class... will problems arise?
Thanks,
Rich
If I could code out the fundementals... IO and Paging / Memory Management, in C and then introduce a CPP file into my makefile (using the proper command lines, etc etc) Know I understand the deal about the contructors and destructors, Ive read the wiki and the pdf how-to on osdevers...
I just want to know if I will have any problems introducing classes later on?
"Cliff-notes"
Vanilla C Kernel, add in a C++ file with a class... will problems arise?
Thanks,
Rich
Mouse Pad - Coming in the distant future...
Kernel: Indigo Kernel - v0.0.1
Thanks to JamesM and BrokenThorn for there tutorials!
Kernel: Indigo Kernel - v0.0.1
Thanks to JamesM and BrokenThorn for there tutorials!
-
- Member
- Posts: 391
- Joined: Wed Jul 25, 2007 8:45 am
- Libera.chat IRC: aejsmith
- Location: London, UK
- Contact:
You'll have issues if you want to call functions in your C code from the C++, or functions in your C++ code from the C, the biggest problem being in the latter. C++ mangles function names, for example so that you can support having functions with the same name but different parameters. You won't be able to call any C++ functions from C unless you specifically use the mangled name. Also, to call C from C++, you'll have to put
for every declaration of functions in the C code, otherwise the C++ compiler will try to use the mangled name.[/code]
Code: Select all
extern "C" void foobar(void);
Thanks, I know about the name mangling...
However, you said issues calling a c function from a C++ compiled file (linked together)
Say I have my strlen compiled in string.c and in console.cpp a class member calls strlen...
I don't see how problems could arise with that...
95% of calling will go from C++ -> C (C would have all of the lower functions, and the C++ code is all higher level stuff...)
Thanks,
Rich
However, you said issues calling a c function from a C++ compiled file (linked together)
Say I have my strlen compiled in string.c and in console.cpp a class member calls strlen...
I don't see how problems could arise with that...
95% of calling will go from C++ -> C (C would have all of the lower functions, and the C++ code is all higher level stuff...)
Thanks,
Rich
Mouse Pad - Coming in the distant future...
Kernel: Indigo Kernel - v0.0.1
Thanks to JamesM and BrokenThorn for there tutorials!
Kernel: Indigo Kernel - v0.0.1
Thanks to JamesM and BrokenThorn for there tutorials!
-
- Member
- Posts: 50
- Joined: Sun Dec 02, 2007 1:24 pm
- Libera.chat IRC: elfenix
- Location: United States
- Contact:
This is how I am implementing/have implemented my kernel.
I wrote a 'core kernel' library, and the C++ infrastructure sits on top of that. I've been pleased with the results.
The main thing to worry about is making sure your exported C functions are safe for use by C++. You'll also have to answer some tough questions later about whether or not to wrap your C API to make it more C++ friendly.
I'd recommend keeping a C++ file or 2 compiling to make sure you're headers and choices continue to match.
The other problem you'll have is the C++ class that you may want to move down to the C level.... I've had a few of those. I originally implemented things with C++ for memory management, and then moved to C. I'd suggest figuring out some clear boundaries early.
I wrote a 'core kernel' library, and the C++ infrastructure sits on top of that. I've been pleased with the results.
The main thing to worry about is making sure your exported C functions are safe for use by C++. You'll also have to answer some tough questions later about whether or not to wrap your C API to make it more C++ friendly.
I'd recommend keeping a C++ file or 2 compiling to make sure you're headers and choices continue to match.
The other problem you'll have is the C++ class that you may want to move down to the C level.... I've had a few of those. I originally implemented things with C++ for memory management, and then moved to C. I'd suggest figuring out some clear boundaries early.
- fontknocker
- Posts: 16
- Joined: Mon Dec 11, 2006 1:49 am
- Location: Canberra, Australia
Hi,
Your strlen problem works like this:
The C++ code thinks: OK, I want to call a global function. It may have multiple overloads (that I am or am not aware of) so I'd better mangle the function name just in case.
If strlen was written in C++ (i.e. compiled with g++) then it would have been mangled by default.
However, as it is compiled in C, it won't be mangled, so the c++ code will try to call something like "_Z6strlenE" but that symbol won't be defined - "strlen" will be defined instead.
Two solutions: Either put "extern "C"" in front of all your symbol declarations (You'll have to put a guard on that because C compilers won't like seeing it, such as):
__cplusplus is ALWAYS defined by any C++ compiler, but NEVER defined by a C compiler.
The second solution is to compile all your C code with a C++ compiler. C++ is (pretty much) a strict superset of C, with the exception of implicit pointer casting, so all C code should compile.
(Note that the C++ compiler will cause an error for implicit int->ptr casts as opposed to the warning the C compiler will give.)
Cheers,
James
Your strlen problem works like this:
The C++ code thinks: OK, I want to call a global function. It may have multiple overloads (that I am or am not aware of) so I'd better mangle the function name just in case.
If strlen was written in C++ (i.e. compiled with g++) then it would have been mangled by default.
However, as it is compiled in C, it won't be mangled, so the c++ code will try to call something like "_Z6strlenE" but that symbol won't be defined - "strlen" will be defined instead.
Two solutions: Either put "extern "C"" in front of all your symbol declarations (You'll have to put a guard on that because C compilers won't like seeing it, such as):
Code: Select all
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
EXTERNC int strlen();
The second solution is to compile all your C code with a C++ compiler. C++ is (pretty much) a strict superset of C, with the exception of implicit pointer casting, so all C code should compile.
(Note that the C++ compiler will cause an error for implicit int->ptr casts as opposed to the warning the C compiler will give.)
Cheers,
James
-
- Member
- Posts: 62
- Joined: Tue Feb 13, 2007 10:46 am
Should this be placed in the wiki for C and C++ languages?JamesM wrote:__cplusplus is ALWAYS defined by any C++ compiler, but NEVER defined by a C compiler.Code: Select all
#ifdef __cplusplus #define EXTERNC extern "C" #else #define EXTERNC #endif EXTERNC int strlen();
James
I have an 80386SX 20MHz 2MB RAM.
It is my testbed platform. Only has the 3.5" and 5.25" floppy drives.
It is my testbed platform. Only has the 3.5" and 5.25" floppy drives.
One thing that I learned is that you must never put anything important in the constructors, especially memory allocations. You will want to just create a member function and call it. The main reason for this is that it is possible that the constructors may be called in the wrong order. Other than that avoiding constructors allows you to ensure that everything is initialized in the right order. You can however use constructors on objects that will only be created by using the new function and a pointer, since the constructor will be called just after allocating memory.
It's part of the C++ language definition, not exactly a secret. But if you find a suitable place, feel free to add this bit.uglyoldbob wrote:
Should this be placed in the wiki for C and C++ languages?
How do you mean? The constructors of global objects? Or generally speaking? I'd like to hear about examples for the latter, because I think it's unlikely.frank wrote:The main reason for this is that it is possible that the constructors may be called in the wrong order.
The job of the constructor is to provide a functional object you can work on. Stuff like myClass.init() is a necessary evil sometimes, but e.g. if you want to initialize constant member variables, you can only do that in a constructor.
Every good solution is obvious once you've found it.
#Solar wrote:Hmmm... global interdependent objects? Oi.
It's hardly a requirement that they be interdependent, a mere call to "new" inside a constructor before the MM is initialised will cause the world to collapse.
(And if you were referring to my use of the word "concrete", I was using it to refer to a global object that is not a pointer. Possibly the wrong choice of word.)
Hi,
That was a problem I faced when I was in the "because I'm using C++, everything has to be in a class" mentality. The memory allocator was in a global class and so had to be initialised first in case anything used the new operator...Bad Idea(TM)!
I now initialise the MM manually before initialising global constructors. This way, the heap manager has an Init() function and no constructor (except for the one added to the class by the C++ language). Also, my code which does not benefit from being in a class is not class based.
Any other classes use the normal constructor mechanism and should have no interdependencies. This is all done in the kernel preamble anyway, so it doesn't matter that it results in a couple more lines of code.
The other way of doing it would be to have a non-class based MM.
Cheers,
Adam
That was a problem I faced when I was in the "because I'm using C++, everything has to be in a class" mentality. The memory allocator was in a global class and so had to be initialised first in case anything used the new operator...Bad Idea(TM)!
I now initialise the MM manually before initialising global constructors. This way, the heap manager has an Init() function and no constructor (except for the one added to the class by the C++ language). Also, my code which does not benefit from being in a class is not class based.
Any other classes use the normal constructor mechanism and should have no interdependencies. This is all done in the kernel preamble anyway, so it doesn't matter that it results in a couple more lines of code.
The other way of doing it would be to have a non-class based MM.
Cheers,
Adam
AJ has good advice there.
One thing:
By declaring a standard constructor as private but not defining it anywhere, you can avoid the generation of any constructor "by the language", and will get a compiler error message should such a compiler be called anywhere.
One thing:
It might not help with your design, but I wanted to mention a "trick" from "Effective C++" that helped me with debugging at times.AJ wrote:This way, the heap manager has an Init() function and no constructor (except for the one added to the class by the C++ language).
By declaring a standard constructor as private but not defining it anywhere, you can avoid the generation of any constructor "by the language", and will get a compiler error message should such a compiler be called anywhere.
Every good solution is obvious once you've found it.