Mixing C/C++... Design Considerations.

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
astrocrep
Member
Member
Posts: 127
Joined: Sat Apr 21, 2007 7:21 pm

Mixing C/C++... Design Considerations.

Post by astrocrep »

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
Mouse Pad - Coming in the distant future...
Kernel: Indigo Kernel - v0.0.1

Thanks to JamesM and BrokenThorn for there tutorials!
xyzzy
Member
Member
Posts: 391
Joined: Wed Jul 25, 2007 8:45 am
Libera.chat IRC: aejsmith
Location: London, UK
Contact:

Post by xyzzy »

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

Code: Select all

extern "C" void foobar(void);
for every declaration of functions in the C code, otherwise the C++ compiler will try to use the mangled name.[/code]
User avatar
astrocrep
Member
Member
Posts: 127
Joined: Sat Apr 21, 2007 7:21 pm

Post by astrocrep »

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
Mouse Pad - Coming in the distant future...
Kernel: Indigo Kernel - v0.0.1

Thanks to JamesM and BrokenThorn for there tutorials!
elfenix
Member
Member
Posts: 50
Joined: Sun Dec 02, 2007 1:24 pm
Libera.chat IRC: elfenix
Location: United States
Contact:

Post by elfenix »

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.
User avatar
fontknocker
Posts: 16
Joined: Mon Dec 11, 2006 1:49 am
Location: Canberra, Australia

Post by fontknocker »

I ran across this just yesterday; perhaps it'll help you:

How to mix C and C++, C++
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

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

Code: Select all

#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif

EXTERNC int strlen();
__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
uglyoldbob
Member
Member
Posts: 62
Joined: Tue Feb 13, 2007 10:46 am

Post by uglyoldbob »

JamesM wrote:

Code: Select all

#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif

EXTERNC int strlen();
__cplusplus is ALWAYS defined by any C++ compiler, but NEVER defined by a C compiler.

James
Should this be placed in the wiki for C and C++ languages?
I have an 80386SX 20MHz 2MB RAM.
It is my testbed platform. Only has the 3.5" and 5.25" floppy drives.
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

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.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

uglyoldbob wrote:
Should this be placed in the wiki for C and C++ languages?
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.
frank wrote:The main reason for this is that it is possible that the constructors may be called in the wrong order.
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.

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.
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

How do you mean? The constructors of global objects?
Yes, I'm pretty sure he means the construction of global concrete objects on kernel initialisation - you have to go through the .init section yourself and the order of construction is in no way guaranteed.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

Hmmm... global interdependent objects? Oi. 8)
Every good solution is obvious once you've found it.
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

Solar wrote:Hmmm... global interdependent objects? Oi. 8)
#

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.)
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

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
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

AJ has good advice there.

One thing:
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).
It might not help with your design, but I wanted to mention a "trick" from "Effective C++" that helped me with debugging at times.

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