Hi David!
On Apr 10, 11:54 am, mathog <
dmat...@gmail.com> wrote:
> Mostly when I program it is maintenance work - making little tweaks and
> corrections to other people's code. The C++ books and tutorials are all
> clear and wonderful about objects and methods but they only ever give
> toy examples. In real world programs it seems that the code is always a
> morass of object types and methods, often with similar names.
> (Resulting in: "yes, 'print', but which 'print'?"). I find it extremely
> difficult to find in such code where actions actually occur, or in many
> cases what earlier events led to an issue later in the program.
First off, "We Share Your Pain!"
By the way, you should be aware of the Microsoft WSYP program
for improving user experience:
http://www.youtube.com/watch?v=3dF-POFE30E
> Presumably there are tools to help with this that I should be using but
> am not. What are these tools? If somebody knows of a tutorial or
> reference on how to deal with complex code like this, please share it.
A couple of comments:
Much real-world code isn't that good. It may be that the
programmers who wrote it weren't that good. It may be that
the programmers were great, but the code grew and evolved
over time, obscuring what was originally a good design, and
that the (possibly rational) decision was made not to refactor
the code base and clean up the current design.
Also, good real-world code is often (very) complicated. Many
real-world problems are inherently complicated. So the art
of good programming is not to eliminate complexity (you can't),
but to master that complexity in as organized a way as you can.
Further, as you point out, many programming books aren't realistic.
It's legitimate to use toy examples -- otherwise the books would
become unreadably long -- but authors often leave out (purposely?)
real-world complexity that their favorite design methodology
doesn't handle well.
So ... Welcome to the real world.
How to deal with this? I don't have a good or simple answer.
What I do is try to get an overview of the code -- or, better,
the part of the code that is relevant to what I am doing. I
try to get a feel for its "shape," for lack of a better word.
Then I rely on intuition to zero in on where the action (relevant
to my problem) is.
(I know, I know ... This is hardly useful advice.)
Only at this point is it practical to look at the details.
(One can "look at details" by reading the code, running a
debugger, putting in print statements, or otherwise adding
instrumentation, according to one's taste. My preference
is to use a combination of reading the code and adding print
statements, but the exact technique doesn't really matter.)
It's hard, it's challenging, and I can't give you a detailed
recipe for how to do it, because for me, there's a lot of
intuition involved.
In my experience it's a rare talent for a developer to be able
to work effectively with a large, unfamiliar code base.
I liken it to the Radar O'Reilly character in the M.A.S.H.
story (movie, TV, etc.). The joke with him is that he'd
look up and say "Choppers." and then only after a few minutes
had passed would the other characters hear the choppers
flying in to deliver the wounded.
In my experience there is a minority of developers who, when
looking for a bug in "Other People's Code," have -- like
Radar O'Reilly -- some sort of sixth sense for where the
bodies are buried. And they are worth their weight in gold.
The only concrete advice I can give you is to gain (a lot
of) experience in working with large, unfamiliar code bases.
The more you see various chunks of code written by programmers
with differing styles and levels of talent, the more easily
you'll be able to recognize at a higher level what they're
trying to do, before drilling down into the details.
To give an overly simplistic "toy" example, when I look at
code I can say "Oh, this guy's using a bunch of nested if
statements." or "This guy's using a switch statement." or
"This guy's using virtual functions in a bunch of derived
classes." or "This guy's setting up a look-up table." all
to accomplish the same programming task. In this context
it doesn't matter whether a specific technique is "right"
or "wrong" or 'better" or 'worse," so it's not worth
arguing about. In practice you will see all manner of
code, and you need to be able to recognize what the guy
is trying to do whether or not he is doing it "right."
> For instance, lately I have had to make some changes to Inkscape...
> ...
> Figuring out the logic in real C++ programs is also challenging. In
> Inkscape, for instance, much of the program is event driven, so a
> backtrace will often not tell you what happened before a given execution
> point. Example:
>
> A ->
> B ->
> C (configure: event X will cause E) ->
> D (configure: something else relevant when E runs) ->
> C ->
> B (event X) ->
> E (stops at preset breakpoint)
>
> So the back trace shows
>
> A->B->E
>
> and there is no clue that C and D are important, or even that they ever
> ran.
Yes, you're absolutely right about this. Working with
event-driven (sometimes called "reactive") programming
is especially hard. Trying to understand "Other People's
Code" becomes even more difficult because, as you point
out, traditional procedural techniques such as reading
one line of code after another or stepping through
execution with a debugger don't map well to the actual
event-driven logic. At some point you have to hope for
the good fortune that the original programmer approached
his event-driven design in a thoughtful and well-organized
manner.
When I _write_ event-driven code, I tend to instrument it
with print statements that include a tag that indicates
which _logical_ process a particular step belongs to. So
in your example, although the backtrace would show:
A->B->E
my print-statement log file would show:
Process-Q: A
Process-Q: B
Process-Q: C: handle event X; schedule E
Process-Q: D: configure some property of E
Process-Q: E: (stops at some breakpoint)
Sometimes you can retroactively instrument existing code along
these lines, but often it's not practical.
> This A->E example is grossly simplified, since in the real program
> there were thousands of function calls before event X. Short of tracing
> every function call how would one ever know that C,D need to be looked at?
Very hard. You need to develop that Radar O'Reilly sixth sense,
or you need to (partially) instrument the code along the lines
described above. And if the code isn't naturally organized
into logical sequences of event processing, it can get pretty
nasty.
> Thank you,
I apologize that I haven't offered any particularly good
recipe for tackling these issues. I would love to hear
what other folks think, and what procedures and tools they
use for these kinds of challenges.
The problem is that when dealing with "Other People's Code,"
you have to work with the code as it is, rather than as you
would wish it to be.
> David Mathog
Good Luck ... And Happy (OPC) Hacking!
K. Frank