Not to get involved in another c/c++ debate, but this is the kicker for
me
the benefits clearly outweigh the occasional head scratching.
It is the head scratching that I worry about, esp for the poor soul who
has to inherit the code that was written after you are done with it. Or
even, worse, the 3rd guy who has no connection to me. Maintenance of
the code I generate is very important to me b/c it is going to live for
a very very long time (as I would assume it is for lots of folks).
I am very big on anyone being able to read the code and see what is
going on in an obvious way. Things like smart pointer classes which
auto release handles scare the crap out of me with respect to this goal.
Yes, the pointer is freed automatically if the ref count goes to zero,
but who can figure that out w/out first having to delve into a bunch of
mix in classes to truly figure it out? Sometimes non automagic code is
better, sometimes not.
Smart pointers/RAII classes do have their place, I am not arguing that.
I just think that in the code I write, they should be limited in scope
to very specific cases and that their role and actions are obvious upon
first glance by the reader.
d
– I can spell, I just can’t type.
-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Jan Bottorff
Sent: Saturday, April 22, 2006 8:19 PM
To: Windows System Software Devs Interest List
Subject: RE: [ntdev] Placement new operator in a driver
Curious, but what is the purpose of C++ in a driver? Other than added
problems such as this, why?
There is no requirement to use placement new, so this isn’t really a
problem, it’s more how do I get a better way to work.
Sorry, couldn’t resist sparking this one again… But really, all of
this can be done in C, so what’s the
point in using C++ where there will be added problems/hassles?
Everything also can be done in assembler, so why do you deal with all
the
hassles of a C compiler?
I personally use C++ in drivers because my experience is the benefits
are
much greater than the problems. A quick example, I have a little class
called ReferenceCounted, which adds an integer counter and an AddRef and
UnRef method to every class I add it to. I also have a template class
ReferenceCountedPtr which makes a smart pointer to any class that
inherits from ReferenceCounted. What this does for me is it moves all
reference counting code into a very localized bit of code, instead of
spread
all over my driver. I use my ReferenceCountedPtr pretty much like a
normal
pointer, except I don’t have to write any explicit code to manage the
reference count. I also have little classes called Linked (for objects
that
will be put in lists), SpinLocked (for objects that need to be
spinlocked),
and ObjectMetaData (which supplies a bunch of methods mostly for
debugging,
like ToString). The automatic reference counting looks like this:
// 14 lines
Void myfunc(void)
{
ReferenceCountedPtr ptr;
ptr = new AClass(1,2,3);
if (ptr) {
passObjectToSomething(ptr);
}
ptr = new AClass(4,5,6);
if (ptr) {
passObjectToSomething(ptr)
}
}
The above code does NOT leak memory, and the objects are deleted when
they
are no longer used. The first AClass allocated may or may not get freed
when
the assignment of the second AClass happens. The second AClass may or
may
not get freed when the function returns (ptr goes of scope). The
equivalent
C code would be:
// 28 lines
Void myfunc(void)
{
AClass * ptr;
ptr = ExAllocatePoolWithTag(NonPagedPool, sizeof(AClass), TAG_ACLASS);
if (ptr) {
constructAClass(ptr);
InterlockedIncrement(&(ptr->refCount));
ptr->virtualFunctions->create(ptr, 1,2,3);
passObjectToSomething(ptr);
if (InterlockedDecrement(&(ptr->refCount)) == 0) {
ptr->virtualFunctions->destructor(ptr);
ExFreePoolWithTag(ptr, TAG_ACLASS);
}
}
ptr = ExAllocatePoolWithTag(NonPagedPool, sizeof(AClass), TAG_ACLASS);
if (ptr) {
constructAClass(ptr);
InterlockedIncrement(&(ptr->refCount));
ptr->virtualFunctions->create(ptr, 4,5,6);
passObjectToSomething(ptr);
if (InterlockedDecrement(&(ptr->refCount)) == 0) {
ptr->virtualFunctions->destructor(ptr);
ExFreePoolWithTag(ptr, TAG_ACLASS);
}
}
}
This is 28 lines of code I would have to write in C vs 14 in C++. In
general, I find I get something like a 2x savings in source lines.
Unless
you believe I can write/debug C twice as fast as I can write/debug C++,
then
this seems like a real gain in developer productivity.
Encapsulation also seems much more flexible in C++. I can define a class
member function, and easily switch it between virtual, non-virtual, and
__forcedinline. If I write C code, I can’t offhand think of any syntax
that
lets me adjust the implementation of a called function so easily. Like
this:
aLocal = anObject->GetValue(); // this is the call to GetValue
I could then define:
virtual int GetValue(void) {return property1;};
or
int GetValue(void) {return property1;};
or
__forcedinline int GetValue(void) {return property1;};
The ease of encapsulation is interrelated to ease of polymorphism. In C,
I
could write a function AClass_GetValue(Class * obj) and switch it
between
inline or not, but I can’t switch it from say the inline access of a
member
variable to a polymorphic function call, without changing the source
code of
every call. I suppose I could change ACLASS_GetValue to the right stuff
for
a polymorphic call, but then my source code basically says:
AClass_GetValue(obj); // this actually may be calling BClass_GetValue
This causes namespace expansion, because as a programmer I now have to
know
there is AClass_GetValue and BClass_GetValue, instead of just GetValue.
As
things get more complex it also becomes AClass_GetValue,
BClass_GetValue,
CClass_GetValue, DClass_GetValue, DClass_GetValue instead of just a
single
GetValue. From a programmer point of view, C keeps my THINKING in terms
of
concrete implementation selection instead of abstract operation
selection.
In C++, I can just change the definition, and my source code keeps the
nouns
(the objects) and the verbs (the function selector) distinct. For
example,
every one of my classes has a member function ToString() which I know
will
give something appropriate for a trace message. The default
implementation
in the lowest base class just returns a sting with the class name and
address. Some classes return strings with more complex information. The
simple case programming cost for me to do this is in every class I
define:
Class myClass {
OBJECT_META_DATA(“myClass”);
}
I’m not saying you can’t do much of this in C, my point is its less work
to
let the C++ compiler do the work. The binary has pretty much equivalent
functionality, but the C++ source code is simpler and smaller, just like
the
C source is smaller and simpler than assembler source would be.
> Driver Works is proof drivers shouldn’t be written in C++ (of all
their
> developers and dollars, they never got it right).
I’ve never used DriverWorks. I can’t say I’ve tried to write a driver
framework that does everything a driver developer might want for every
kind
of driver.
I’ve been writing drivers in C++ for a number of years now, and still
find
every C++ based driver I write is better structured than the last one,
so I
must still be learning. There have been MANY common elements, and it
would
have been nice if those came predefined, in source form. Using C++ tends
to
add a bit of overhead at the beginning of the project, but then as the
project moves forward a LOT of things are just much easier.
For intellectual property ownership reasons, I can’t just take previous
class I’ve written for a different company and use them. I can just
write
the classes again, which goes super fast since some of them I’ve written
over and over. A minus is, I have to write essentially the same class
over
and over, a plus is I get to write it better each time. The last couple
of
projects I’ve heavily used multiple inheritance (after being inspired by
one
of the DDK multimedia samples), and the benefits clearly outweigh the
occasional head scratching.
I probably need to learn about using C++ combined with KMDF and UMDF.
- Jan
—
Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256
To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer