All right, wise guy. I’ll take that challenge. I consider “operator
new”, in C++, to be syntactic sugar because you can achieve the exact
same thing, down to the assembly level, using equivalent C.
Yes, there are many language constructs that can be thought of as
“macros”, in the sense that you can always achieve the same thing by
doing itself, albeit in a more “low level” fashion. C is a nice
front-end to assembly. C++ is a nice front-end to C. And so on and so
on.
Did I say these things are BAD? No, of course not. I’m not one of
these programmers that harkens back to some mythological golden age,
where hairy-chested Real Programmers banged on assembly all day. I
would MUCH rather use C++ than C (or assembly) – for most tasks, and
given that I’m using C++ “correctly”, and not just slathering classes
and overloaded operators all over everything in sight.
Again, I don’t think these things are bad at ALL. They make life
reasonably livable. However, C is beyond 30 years old, and it is simply
inadequate for many real-world apps today. C++ is just make-up on a
horse – it’s a slightly prettier horse, but it’s still a horse.
How is what I’m “claiming” obfuscation? I’m pointing out that there are
real weaknesses in C/C++ – and anyone who has ever worked on a project
beyond a few dozen KLOC has hit these same problems. Mismatched
headers. Heap corruption nightmares. Mismatched method/function
signatures. Totally broken encapsulation (unless you bend over backward
with use of public interfaces). The whole confusion between #define and
const. The confusion between “const means compile-time constant” and
“const means read-only”. The evil pass-by-reference & syntax.
Bolted-on post-facto templates.
C++ is a bastard’s mix of OOP and C. Most of us use it because we have
to. Some of us have a, well, provincial affection for C++, and mistake
its design flaws for features. I still work in C++ because I have to –
but most of my work, when possible, has transitioned to managed
languages (CLR). The productivity gains are real. C/C++ will
eventually fade into a niche (performance, machine-specific, etc.),
similar to what has happened with assembly. And me? I won’t shed a
tear when I don’t have to chase down another leaked pointer or spammed
heap block again.
– arlie
-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of George Blat
Sent: Saturday, December 06, 2003 7:48 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] RE: how to ensure atomic code in a kernel mode driver
In many areas simple programs are called macros. If you don’t define the
context within you are talking you can say anything that comes to your
mind. In the context of C++ (and C) macros are well “defined” and what
you’re claiming doesn’t do anything but obfuscation. You can define
anything that you don’t like as syntactic sugar, or saccharine or what
have you. I here define your use of English as syntactic sugar over
using your hands and grunts to try to communicate.
George Blat
At 10:33 PM 12/5/2003, you wrote:
The “new” keyword is a macro. Not in the sense of a #define. It’s a
macro in the sense that it is just syntactic sugar for a call to the
global “void * operator new (size_t len)” function, and then to a call
to a constructor of the class. This is a macro in the conceptual
sense, not a #define. Duh.
Nobody can access amunge? Bullshit. All I have to do is copy your
header, change “private:” to “public:”, and I’m done. That, or I can
generate a method signature that matches it and calls it.
You still don’t get it. I’m talking about REAL type-safety, as in CLR.
– arlie
-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Moreira, Alberto
Sent: Friday, December 05, 2003 5:38 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] RE: how to ensure atomic code in a kernel mode driver
I’ll only reply to the technical points. The sequence
CFoo * instance = (CFoo *) 0x12345678;
instance->method(); // boom
is C disguised as C++, and you get what you shop for. If you’re doing
it in
C++, you can use references instead of pointers: no cast, and no boom.
Indeed, no language can prevent you from acting like a jackass, but
some languages provide you with alternatives that make it less likely
you will.
The “this” pointer is, indeed, a hidden pointer, but obviously, you’re
passing the worry of maintaining it to the compiler where it belongs.
Heck, you are willing to delegate to the API at runtime, which is
inherently unsafe (even the runtime stack size cannot be guaranteed to
accomodate you
!) and yet you grumble when you’re allowed to delegate to the compiler,
which is eons safer ? One ounce of compile-time checking is worth a ton
of runtime shit. And that’s additional protection too, because if I
issue “datafield” without a pointer or a class:: in front of it, what
do you know, it’s an implied “this”: again, one more chore delegated to
the compiler. And that helps preventing us from acting like jackasses.
The “new” keyword is not a macro - it’s an OPERATOR. It can be
overridden through the standard C++ mechanisms.
As for “same level of protection”, try this. Assume I have the
following
program:
class b
{
private:
class a
{
public: int amunge(…);
};
a aobj;
public:
NTSTATUS bmunge(…)
{
aobj.amunge(…);
}
};
Nobody outside class b can access amunge(). Now, write me a C program
that doesn’t let anyone but objects of class b to invoke function
amunge. Once you manage it, try this one:
class a
{
protected:
int amunge() { … }
};
class b: private a
{
public:
int bmunge() { return amunge(); }
};
and again, write me a C program that doesn’t let anyone but objects of
class b, or derived from class a, to invoke function amunge. In this
last case, even the sequence
main()
{
a aobj;
aobj.amunge();
…
}
will be frigged by the compiler as a violation; if I want that to work,
I must open my protection and declare amunge as public instead of
protected.
Bottomline: I have a three-level C++ function call hierarchy here, duly
scoped and duly encapsulated, while C only gives me two. I could have
multiple levels if I wanted too; not in C.
Alberto.
-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com]On Behalf Of Arlie Davis
Sent: Friday, December 05, 2003 4:48 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] RE: how to ensure atomic code in a kernel mode driver
Bullshit. C++ provides the exact same level of protection as C –
virtually none. I’m not throwing a tantrum, I’m insulted that you
think I don’t understand C or C++, when I’ve been earning my living
with them (and assembly, and years of k-mode development, blah blah
blah) for many, many years.
I never said that C++ doesn’t provide static type checking. It does –
but it does NOT prevent you from violating its own type safety. It
still provides ABSOLUTELY NO constraints to what developers can do.
And most large-scale applications REQUIRE you to violate type safety to
get any real work done. C++ is just syntactic sugar. In case you
didn’t know, the first C++ compilers were just front-ends – they just
transformed the C++ code into equivalent C code.
“this” management is hardly revoluationary, Alberto. It’s JUST A
POINTER. Just because you don’t see it in the argument list, doesn’t
mean it isn’t there. It isn’t any more protected than any other
pointer. It’s just the “favorite” pointer of the compiler, within a
given class.
The “new” keyword in C++ is just a macro over “malloc” (or whatever
malloc-equivalent you want to use). There is nothing sacred about it.
Again, STOP LECTURING – I’ve been doing this for DECADES and yes, I
understand ALL OF THIS. You aren’t contributing anything new. And I
can still construct an “object” by doing this:
CFoo * instance = (CFoo *) 0x12345678;
instance->method(); // boom
I’m not going to continue this bullshit argument any more. C/C++ are
fundamentally flawed. Don’t bother telling me all about the joys of
OOP
– I know all about it, I’ve been doing all of this for YEARS, and not
just as some leaf-node. I’ve managed teams of developers, on projects
that took years to develop and release. Apparently you don’t get it
that *I* get it, so this argument is just totally pointless.
– arlie
-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Moreira, Alberto
Sent: Friday, December 05, 2003 4:22 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] RE: how to ensure atomic code in a kernel mode driver
Arlie, you stated that C++ doesn’t protect. I just gave you a fair
amount of examples of how it does protect. But if you’re as senior as
you claim, you will know well enough the difference in level between C
and C++. If you don’t, so be it, but at least give me a few valid
technical points as to why, tantrums won’t do - remember, I have
pointed hair.
C++ gives you additional type checking and safety because member
C++ functions
are now part of the class. For example, if you have a function
NTSTATUS blahblah (packet *p, int packetLength, …) { . //
references to
p->thisandthat; … }
your have near zero protection for both the pointer and the length. If
now you say
NTSTATUS packet::blahblah(…references to thisandthat, no p-> needed
in this context ! …) { }
You’re more protected, because (1) no more direct references to pointer
p in your calls, actually, no need for that pointer at all, (2) no more
need for the programmer to handle packet lengths, (3) you can replace
p->thisandthat by a simple thisandthat, and (4) you can make those
fields private so that they’ll be opaque to the caller no matter what,
the compiler won’t even allow the caller to reference that field. You
also have two less parameters in your call, you no longer have to use
things such as p->thisandthat inside your code, because you KNOW that
“thisandthat” are fields inside the object: a shitload of pointer
dereferencing has been avoided and that’s protection; and if you use
references instead of pointers, you don’t even run into casting issues.
As for memory abuse, try
packet *p = new packet(…);
and your memory allocation is automatic and needs no size management;
delete your memory when you delete your object, presto, gone. Deleting
a deleted object can only happen if you’re using pointers, but then,
good
luck: you’re on your own either way, but in C++ at least you can use
references instead of pointers. Reference counts are managed inside the
object: it’s easy enough to extend the class with an interface that
manages the counter automatically for you, so that the caller doesn’t
know about it; and circularity is guaranteed not to happen because you
KNOW that the counters are incremented every way in and decremented
every way out, because that’s the way your object works ! In garbage
collected languages such as C# it’s even better: you don’t need to
worry
about those counters, the compiler handles them for you. And indeed,
C++
can’t stop you from casting pointers, but you don’t need to use
pointers: you can use references instead, and goodbye casting. Also,
precisely because there’s nothing in the DDK that prevents you from
doing harm, it’s a good idea to encapsulate those things with classes
that guarantee some sanity - provided of course you use them. Believe
me, the highest level language won’t prevent you from acting like a
jackass, but at least they’ll give you plenty of ways to choose that
are
as far from stupidity as it can gets.
Until we have a kernel side CLR - and I’m all for it, let’s hide all
that shit inside a C# managed code model and get the JIT compiler to
generate compiled code at DriverEntry time - we have to live with the
nonsense we have, and I find C++ well superior to C in that respect.
And hey, wouldn’t it be great ? The JIT Compiler could know whether
you’re running on a single processor or on a multiprocessor, or on a
Xeon, or on an Opteron, and compile your code differently according to
what it finds out about the runtime. Anything that needs SMP
synchronization could go through an IL monitor construct, which gets
compiled into spinlocks, barriers or what else at runtime ! No more
synchronization or memory management of any kind it’s all automatic or
buried inside the OO model. You could write a driver in C#, debug it
inside MSVC.NET in the user side, then just move the IL to kernel side,
done: all low level stuff buried inside the IL, all you have to do is
to write the high level logic, and any junior programmer could write a
driver !
Alberto.
Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256
You are currently subscribed to ntdev as: xxxxx@compuware.com
To unsubscribe send a blank email to xxxxx@lists.osr.com
The contents of this e-mail are intended for the named addressee only.
It contains information that may be confidential. Unless you are the
named addressee or an authorized designee, you may not copy or use it,
or disclose
it to anyone else. If you received it in error please notify us
immediately
and then destroy it.
Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256
You are currently subscribed to ntdev as: xxxxx@sublinear.org To
unsubscribe send a blank email to xxxxx@lists.osr.com
Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256
You are currently subscribed to ntdev as: xxxxx@ntrealtime.com To
unsubscribe send a blank email to xxxxx@lists.osr.com
Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256
You are currently subscribed to ntdev as: xxxxx@sublinear.org To
unsubscribe send a blank email to xxxxx@lists.osr.com