Re: Maintance of c++ code
- From: piotr5@xxxxxxxxxxxxxxxxx (Piotr Sawuk)
- Date: 01 Mar 2006 02:25:57 GMT
In article <postmaster-84682A.14091723022006@xxxxxxxxxxxxxxxxxxxxxxx>,
"Daniel T." <postmaster@xxxxxxxxxxxxx> writes:
In article <dt91dn$me4$1@xxxxxxxxxxxxxxxxxxxxxxxxxx>,
piotr5@xxxxxxxxxxxxxxxxx (Piotr Sawuk) wrote:
In article <postmaster-C30328.17344217022006@xxxxxxxxxxxxxxxxxxxxxxx>,
"Daniel T." <postmaster@xxxxxxxxxxxxx> writes:
In article <dt3m1t$11i$1@xxxxxxxxxxxxxxxxxxxxxxxxxx>,
piotr5@xxxxxxxxxxxxxxxxx (Piotr Sawuk) wrote:
In article <postmaster-CB1F1F.22283909022006@xxxxxxxxxxxxxxxxxxxxxxx>,
"Daniel T." <postmaster@xxxxxxxxxxxxx> writes:
'find_sawuk_number' and 'initialized',) finally notice the comments.
I too use comments to document the reasons why I made one choice
over the other, but your methodology to comment every single function
is new for me. Another thing to try out...
The comment is supposed to answer the question: What does the function
do? (The code tells us how it does it, but that isn't always the most
clear picture.)
I see, so for example:
template<class V>
class rIt : public std::list<V>::reverse_iterator
{
....
public:
//down-casting
rIt(Self_ s) : Self_(s) {}
//no comment needed.
rIt() : Self_() {}
//any type which can be statically cast to the base-class
template<class Itera>
rIt(const Itera& i) : Self_(static_cast<Self_>(i)) {}
//applies non-symetric operator& in reverse
static int op(const V& p1,const V& p2) {return p2 & p1;}
};
But this leaves the question where to put the comments for the
template-interface? For example above op() is contained in both,
rIt and It, so that using one of them as a template-parameter
would yield a special behaviour which isn't obvious from the code.
In this case the comment would be: "op(p1,p2) executes operator&
and fulfills the requirement that if p1 and p2 where returned by
an iterator-class defined in this object, and recursively incrementing
the iterator which pointed to p1 would eventually yield p2, then
the same is also true for cl::iterator and the operator&(p1,p2)
which gets executed by this function." Now, my question is: Is
this comment explaining "what", or "how"? What about the comment above?
You somehow keep forgetting that not OOP with virtual functions,
but OOP with templates is a whole new world for me...
You can't test every possible combination, except with trivial code. The
point of the tests in this case is to demonstrate proper usage of the
class and cover some boundary conditions so users will know what happens
then.
Of course, usually there are thousands of possibilities, but one
could categorize them into some, as you called it, boundary conditions.
However, then writing tests is as tiresome as writing the actual
code, where those boundary-conditions also need to get identified.
In my experience it's just more difficult to find an example which
would trigger such a boundary-condition, than to write code for
handling those highly unlikely cases. But the idea to merely display
proper usage isn't new for me, it's what I did in the examples posted
here -- just no asserts since my code is supposed to be used in
combination with other code which could get thoroughly tested.
So, where does one unit end? Is it just the class which needs to
get tested, is it a bundle of related classes, or does it merely
encompass all classes which are related by the power of templates?
What is a unit-test anyway, and how does it apply to the kind of
OOP I demonstrated with my use of templates?
class Foo {
public:
int get() { return 5; }
};
class Bar: public Foo {
public:
int get() { return 2; }
};
void fn( Foo* f ) {
assert( f->get() == 5 );
}
int main() {
Bar* b = new Bar;
fn( b );
assert( b->get() == 2 );
}
of course, but consider:
class Foo {
public:
int get() { return 5; }
};
class Bar {
public:
int get() { return 2; }
};
template<class Type_>
void fn( Type_* f ) {
assert( f->get() == 2 );
}
int main() {
Bar* b = new Bar;
fn( b );
assert( b->get() == 2 );
}
a rarity in stl -- it's a template-library afterall. However, I
thought it was common practice to derive from a class on which
one wishes to operate without even defining a single member-variable.
No, it isn't. Better would be to set up a template parameter so that
your code will work for *any* container and not just std::list.
Then those people in the Java-course where telling bull*** when they
told me about "no differences between c++ and java"... :-)
Anyway, each container-type has a different storage-policy, and
there are many more possibilities than those few defined in stl.
In my opinion it is impossible to create code which is useful
for all cases. That's just a warning. Now I'll ty to forget this
prejudice and consider your proposal.
In my code I defined an iterator which does contain its very own
begin() and end() as static functions, and which is capable of
inverting itself into an iterator of the opposite direction. For
generic code, compatible with any container, I would need, as
template-parameters, the container's type, the value-type stored
in the container (remember that for example a map-container does
store a pair of objects, of which only one is of interest for me),
a function for retrieving the object of interest from that value-type,
and of course the object-type itself. That's 4 parameters of which
2 could have a default-value. (Of course I'm assuming here that
iterator and reverse_iterator are types defined in each container.)
Further you told me to evade inheriting the original iterators, and
that means that I would need to implement all the functions which are
usually part of an iterator: ++, --, *, ->, ==,... Of course those
would merely consist of calling the apropriate iterator's functions
and returning a new object initialized with the result. Also the
iterator's typedefs need to be re-implemented if I wish to use any
of stl's generic functions. Could you explain to me what gain I
would receive from this additional work?
Suppose my code to be useful for any container, do you think it would
still be readable with all those template-parameters? I can understand
that before deciding to use one particular container-type such generic
coding-style might be useful, but a quick decision in these affairs
could speed up the code and code-creation. Is the additional administrative
work really worth the hassle? So far I haven't met any templated code
which could be used without completely re-writing it (except for stl
and similar of course), so you obviously do have more experience in
this area. Could you please tell me from your experience how useful
such templated functions really are, and what to watch out for when
creating template-parameters and the associated classes?
The main reason why I use inheritance is for code-reuse, especially
since less code inside a class-definition does also mean more
readability of the relevant changes I made to that class. Am I wrong?
As you have learned, "less code" doesn't work. :-)
No, I didn't learn such a thing. But my question was more in the
direction of maintance, and you are right that stub-functions are
more easy to maintain than static_casts.
However, what I also learned is that documenting templates with
simple comments is quite difficult. I learned a new method for
programming: first write the function the way you imagine it
would be self-explanatory, and then create the classes to support
that underlying structure your function does expect. This way I
do get small classes which contain all the differences between
possible implementations, full of static functions and members,
which are difficult to explain. Now I would like to learn how
to make such classes more readable and maintain-able.
I suggest that you start posting in comp.lang.c++ though because most of
your issues have nothing to do with OOP.
Oh, sorry if my discussion did get off-topic. I acually wanted to
learn more about OOP, and not about c++. For example above question
on the meaning of "unit-test" is more intrigue-ing for me than
any of our quarrels about the usefulness of inheritance of non-virtual
functions. I personally dislike virtual members, and prefer templates
for replacing them. However, through your help I now have a working
program full of simple static member-functions of iterators, and a
simple unit-test to check it's workings. Unlike my previous code posted
here, I now provide the template-parameters directly to the relevant
function, in order to make re-use with other container-types more easy.
I hope I can learn some more OOP-tricks in-here. OOP with templates is
much more fun than the usual inheritance-syntax...
--
Better send the eMails to netscape.net, as to
evade useless burthening of my provider's /dev/null...
P
.
- Follow-Ups:
- Re: Maintance of c++ code
- From: Daniel T.
- Re: Maintance of c++ code
- Prev by Date: Re: Time outs and state machines
- Next by Date: Re: OOP can be simply summed up as 'passing messages to objects'
- Previous by thread: Re: Time outs and state machines
- Next by thread: Re: Maintance of c++ code
- Index(es):