Kyle Moffett wrote:
On Apr 26, 2006, at 22:05:31, Roman Kononov wrote:
Kyle Moffett wrote:
On Apr 26, 2006, at 19:00:52, Roman Kononov wrote:
Linus Torvalds wrote:
- some of the C features we use may or may not be usable from
C++ (statement expressions?)
Statement expressions are working fine in g++. The main difficulties
are:
- GCC's structure member initialization extensions are syntax
errors in G++: struct foo_t foo={.member=0};
And that breaks a _massive_ amount of kernel code, including such
core functionality like SPIN_LOCK_UNLOCKED and a host of others.
There are all sorts of macros that use member initialization of that
form.
This does not break the code at run time, this breaks the code at
compile time, and should be less painful.
So breaking 90% of the source code at compile time is ok? I think not.
The kernel relies really _really_ heavily on such structure
initializers, and breaking them would effectively break the world as far
as the kernel is concerned.
I agree: do not break code, fix it. Make it more robust language-wise.
G++ compiling heavy C++ is a bit slower than gcc. The g++ front end
is reliable enough. Do you have a particular bug in mind?
A lot of people would consider the "significantly slower" to be a
major bug. Many people moaned when the kernel stopped supporting GCC
2.x because that compiler was much faster than modern C compilers.
I've seen up to a 3x slowdown when compiling the same files with g++
instead of gcc, and such would be unacceptable to a _lot_ of people
on this list.
I agree, it would be a bad idea to compile the existing C code by
g++. The good idea is to be able to produce new C++ modules etc.
No, this is a reason why C++ modules are _not_ a good idea. If you
could write the module in C or C++, but in C++ it compiled 100-200%
slower, then you would write it in C. Why? A simple matter of numbers:
Say it takes you 100 hours to write and debug the module in C++, and 140
to write and debug it in C. I estimate that at least 200,000 people
would download and compile a single version of the kernel with your
module (not an unreasonable estimate). Note that I'm not even including
the people who do repeated regression testing of versions, or people who
download and compile multiple versions of the kernel. If the source
file takes an average of 1.0 seconds to compile in C and 2.0 seconds to
compile in C++, then:
(2.0 sec - 1.0 sec) * 200,000 = 200,000 seconds = 55.6 hours
140 hours - 100 hours = 40 hours
40 hours < 55.6 hours
So for a single version of the kernel your module, you've already wasted
15.6 hours of time across people using it. Over time that number is
just going to grow, _especially_ if people start writing more and more
modules in C++ because they can. If you want to build C++ in the
kernel, write a compiler that does not include all the problematic C++
features that add so much parsing time (overloaded operators, etc).
It is hard take this seriously. For people like me, it is 5 times faster
to type and debug C++ code. And debug time is 50 times more expensive
then compile time.
A lot of C++ features are already supported sanely. You simply need
to understand them. Especially templates and type checking.
First of all, the only way to sanely use templated classes is to
write them completely inline, which causes massive bloat. Look at
the kernel "struct list_head" and show me the "type-safe C++" way to
do that. It uses a templated inline class, right? That templated
inline class gets duplicated for each different type of object put in
a linked list, no? Think about how many linked lists we have in the
kernel and tell me why that would be a good thing.
You mentioned a bad example. The struct list_head has [almost?] all
"members" inlined. If they were not, one could simply make a base
class having [some] members outlined, and which class does not enforce
type safety and is for inheritance only. The template class would
then inherit the base one enforcing type safety by having inline
members. This technique is well known, trust me. If you need real life
examples, tell me.
Ok, help me understand here: Instead of helping using one sensible data
structure and generating optimized code for that, the language actively
_encourages_ you to duplicate classes and interfaces, providing even
_more_ work for the compiler, making the code harder to debug, and
probably introducing inefficiencies as well.
The C++ language does not encourage anything like this. Instead it
actively debugs my code. And it does not produce inefficiencies at run
time unless I do something stupid.
If C++ doesn't work
properly for a simple and clean example like struct list_head, why
should we assume that it's going to work any better for more complicated
examples in the rest of the kernel? Whether or not some arbitrary
function is inlined should be totally orthogonal to adding type-checking.
You misunderstood something. The struct list_head is indeed a perfect
type to be templatized with all members inlined. C++ works properly in
this case.
Static constructor issue is trivial.
How so? When do you want the static constructors to be run? There
are many different major stages of kernel-level initialization;
picking one is likely to make them useless for other code.
For #defines core_initcall() ... late_initcall() I would type
something like this:
class foo_t { foo_t(); ~foo_t(); }
static char foo_storage[sizeof(foo_t)];
static foo_t& foo=*reinterpret_cast<foo_t*>(foo_storage);
static void __init foo_init() { new(foo_storage) foo_t; }
core_initcall(foo_init);
This ugly-looking code can be nicely wrapped into a template, which,
depending on the type (foo_t in this case), at compile time, picks the
proper stage for initialization.
You proved my point. Static constructors can't work. You can add silly
wrapper initcall functions which create objects in static memory at
various times, but the language-defined static constructors are yet
another C++ feature that doesn't work by default and has to be hacked
around. C++ gives us no advantage over C here either.
Nothing works by default. I did not say that static constructors are
advantageous. I said that it is easy for the kernel to make static
constructors working. Global variables should be deprecated anyway.
Plus this would
break things like static spinlock initialization. How would you make
this work sanely for this static declaration:
spinlock_t foo_lock = SPIN_LOCK_UNLOCKED;
Under C that turns into (depending on config options):
spinlock_t foo_lock = { .value = 0, .owner = NULL, (...) };
I would make it exactly like this:
#define SPIN_LOCK_UNLOCKED (spinlock_t){0,-1,whatever}
spinlock_t foo_lock=SPIN_LOCK_UNLOCKED;
This is easy to change. The empty structures look far more painful.
How could that possibly work in C++ given what you've said? Anything
that breaks code that simple is an automatic nonstarter for the kernel.
Also remember that spinlocks are defined preinitialized at the very
earliest stages of init. Of course I probably don't have to say that
anything that tries to run a function to iterate over all
statically-allocated spinlocks during init would be rejected out of hand.
Apparently this would be rejected. Why would it?
Regards
Roman Kononov
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Stuff]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
[Linux Resources]