placement new doesn't work without nullifying a few things. Automatic cleanup on scope exit doesn't work for locks in the kernel. See below ... Much more ...
placement new/delete are noexcept functions. But, they call std::terminate--not acceptable. The only thing that works is an alloc function that returns NULL (or (void *) -errno). Returning null is not fatal in the kernel. The caller must be able to deal with it (usually returning -ENOMEM). So, the [global] new/delete must be changed. Also, placement delete has problems [I've left off the backslashes for clarity]:
#define GETPTR(_ptr,_typ,_siz)
switch (_typ) {
case 0:
_ptr = alloca(_siz);
break;
case 1:
_ptr = kmalloc(GFP_KERNEL,_siz);
break;
case 2:
_ptr = kmalloc(GFP_ATOMIC,_siz);
break;
case 3:
_ptr = slab_one(_siz);
break;
case 4:
_ptr = slab_two(_siz);
break; // ...
}
void
myfnc(int typ)
{
void *ptr;
GETPTR(ptr,typ,23);
class abc *x = new(ptr) abc(19,37);
}
At this point, a delete operator [even a placement version] has no idea which pool to release to because there's no way to pass typ to it. You might be able to create a contructor abc(typ,19,37) but that adds an extra member element to hold typ so the delete operator can get at it, but that's additional overhead/complexity that C doesn't have. It might be possible to make it work by casting typ to void* and using that as the pointer:
class abc *x = new((void *) typ) abc(19,37);
and have the class specific new operator use GETPTR internally. I tested this and it works. However, I haven't yet been able to get the corresponding placement delete to work as a class specific overload [yet]. In trying to find the way, I came upon:
http://www.scs.stanford.edu/~d...
It's fairly detailed and lays out a [pretty strong] case against using the new operator [more eloquently than I could do here].
A lot of kernel code puts definitions in the usual place [top of function body] for C. In C++, this invokes the constructor, which is not what you want. The reason is that [say] 10 vars are defined. The function does a quick check on args and does a non-standard return -EINVAL. All that wasted create/destroy. This may be harmful if the constructors have side effects such as lock acquisition. Note that doing a [wasteful] lock followed by an immediate unlock [to satisfy having a destructor do lock cleanup] is a non-starter in the kernel [you'll never get such code checked in/signed off on]
So, you'd have to go through every kernel function by hand [there are 16.9 million lines of source code] and move the definitions down:
{
struct foo x;
if (bad_news)
return -EINVAL; ...
}
{
if (bad_news)
return -EINVAL;
struct foo x; ...
}
You can't put a lock release in a destructor because you'd need an extra member var that would have to be set/cleared when you acquire/release a lock. That's because the destructor has to have some way of knowing whether to suppress the lock release. So, you're adding an extra variable [that isn't needed in C] just to prevent an attempt to release a lock that was never acquired in the first place. More overhead and slower [and more complex] than its C counterpart.
In kernel functions, multiple different types of locks have to be acquired. Sometimes, it's:
get_lock_a();
x = find_object_in_a();
if (! x)
goto release_a;
get_lock_b();
y = find_object_in_b();
if (! y)
goto release_b; // do stuff
release_lock_b(); // do more stuff
release_lock_a(); // do even more stuff
return 0;
release_b:
release_lock_b();
release_a:
release_lock_a();
return -EINVAL;
Although you can create a goto-less version, sometimes the goto's are done deliberately for speed.
Another common snippet:
get_lock_a();
x = find_object_in_a();
if (! x)
release_lock_a();
return x; // return with object list locked if we found one
Here's another one:
myfnc()
{
if (in_interrupt()) {
if (! trylock()) {
schedule_work(myfnc);
return;
}
}
else {
getlock();
} // ...
release_lock();
}
I've been writing linux device drivers for a living for the last 20 years. For 12 before that Unix. For 10 before that other OSes. So, I've had to read an awful lot of kernel code.
These are just the smallest of examples [junior grade--I was in a hurry] of what would be required. There are many more. Try a different approach. Download the kernel source code and start reading it. You'll find out a few things:
(1) C isn't nearly as messy or anemic as most C++ programmers think it is.
(2) See what expert level C programmers can actually do. The kernel is far cleaner that you probably suspect.
(3) Linus [and crew] don't want to use C++ merely because "they don't understand it". If it were truly beneficial in a kernel environment, they'd have switched long ago.
(4) Contrary to belief [on slashdot] Linus is a very reasonable guy. I've met him personally a number of years back. Ignore the bombast in postings. He only does it to counter some strong egos. But, it's completely done for shock effect to get stubborn [and wrong] programmers to do their jobs. Linus has had many discussions/battles where the others were saying "you just don't understand". This has usally been the gcc developers. In the end, he ends up being right [e.g. it really was a bug in the compiler and not a bug in Linus' understanding].
(5) The kernel is overhead to getting work done [an application]. Thus, it's designed to be fast--very fast. Other OSes have died because they forgot this. Mach, for example. [Clean] message passing microkernel architecture. Unfortunately, it [even after tweaks] was too slow for a production system and the project was sidelined.
(6) Linux is the basis for Android. Linux powers Google servers. Linux powers Facebook servers. Linux powers most zillion-core supercomputers. Considering all the diversity in arches, devices, etc. if Linux weren't already cleanly designed, it would have collapsed under the weight of maintaining all of the above.
(7) The kernel is "bare metal" programming. C is better suited to that than C++.
If you truly think the kernel will benefit from C++, read [a lot] of the code first [Repeating: 16.9 million lines of code]. Then join the project that started the discussion.