Conscious antipattern behavior

R

Ray Gardener

I've always wondered what other C++ programmers
fail to do while coding even though they know
what they're doing is... naughty (and some of the
items below occur in other languages, of course).

People make these slip-ups all the time accidentally,
but what I'm getting at is when we make them while
we consciously _know_ that we're being bad. This is
the more interesting phenomenon because I tend to
interpret consciously "fighting the language" or
consciously "not using the language as intended"
as an indication that the language or the
development environment could be improved.

(Granted, in C++ the "right way" is vague because
C/C++ has broad expressive capability, but there
are informal standards of good coding practice
that we all more or less know).


Examples:

- Duplicating code via copy/paste instead of
making subroutines.

- Failing to make methods and other things 'const'
when they should be.

- Failing to assert what should be asserted.

- Not using templates when they would be suitable.

- Trying patches instead of refactoring (only
to sooner or later refactor anyway, and then
feeling sheepish about it).

- Passing to functions references to objects
that give too much info to the function,
when it only needs a few members of the object.

- Making "incestuous" calls instead of using
signal/slots/broadcasters/listeners or similar
design patterns.

- Using your own container classes instead of STL,
when STL would have been the better choice.

- Using #define macros instead of template functions.

- Using boolean types instead of enums even though
you know that the type will soon grow beyond
two possible states.

- Letting a class grow until it has a zillion members.

- Using pointers instead of references when refs
would have been better.

- Putting "is-a" members inside a class instead of
making a new subclass, and having callers query
those members in a roll-your-own form of RTTI.

- Using error code returns or object error states
instead of exceptions.

- Letting class members be public that shouldn't be.

- Letting a function grow really long only because
technically it doesn't actually have to be broken
down into subroutines because none of them would
be called from anywhere else anyway.

- Using more and more preassigned function args
to avoid having to declare another function of
a similar name, or the reverse -- not using
preassigned args where they make sense.

- Making methods with names like "add" and "mul"
instead of using operator overloading.

- Being inconsistent in naming, spelling,
interface strategies, etc.

- Declaring more global objects than necessary.

- Placing code that makes sense in a shareable library
into application-side classes (or just letting it
sit and evolve on the app side while telling yourself
that you'll factor it out "someday").

- Using char* instead of a more Unicode-friendly string type.

- Using two member vars to track a state of only
one logical member (e.g., a bool and a pointer when
the pointer's being null will suffice to indicate
a false state).

- Specifying constants by value instead of by macro or enum.

- Using int instead of size_t when size_t is the
better type (e.g., you're declaring a zero-based counter).

- Using #define's instead of enums.

- Using external enums instead of class member enums.

- Using a struct instead of a class (even when it becomes
painfully obvious that it should be a class).

- Solving a typical problem one way, then the same
problem a different way somewhere else because
it is superior, but failing to go back and upgrade
the usages of the first implementation.

- Using malloc/free in some places and operator new/delete
in others, for no particular reason.


Obviously some of the above are rational tactical
decisions under certain situations, so I'm not
including those combinations of choice and context.

I'm interested in finding out what poor choices
C++ programmers consciously make and why. Was it
easier at the time? More expedient? Force of habit?
Does the dev environment foster those habits
(e.g., an editor with great copy/paste might
actually be bad)? Is good coding a matter of
willpower or do certain tools turn the tide?
Does extreme or paired-up programming help
combat these tendencies? Is perfect discipline
even desirable?

Ray
 
B

Bob Hairgrove

I've always wondered what other C++ programmers
fail to do while coding even though they know
what they're doing is... naughty (and some of the
items below occur in other languages, of course).

People make these slip-ups all the time accidentally,
but what I'm getting at is when we make them while
we consciously _know_ that we're being bad. This is
the more interesting phenomenon because I tend to
interpret consciously "fighting the language" or
consciously "not using the language as intended"
as an indication that the language or the
development environment could be improved.

(Granted, in C++ the "right way" is vague because
C/C++ has broad expressive capability, but there
are informal standards of good coding practice
that we all more or less know).


Examples:
[snipped]

Obviously some of the above are rational tactical
decisions under certain situations, so I'm not
including those combinations of choice and context.

I'm interested in finding out what poor choices
C++ programmers consciously make and why. Was it
easier at the time? More expedient? Force of habit?
Does the dev environment foster those habits
(e.g., an editor with great copy/paste might
actually be bad)? Is good coding a matter of
willpower or do certain tools turn the tide?
Does extreme or paired-up programming help
combat these tendencies? Is perfect discipline
even desirable?

Ray

Some programmers just plain don't know any better. Then there are
those who don't want to know any better because they have gotten away
with their stuff for years and nobody ever said anything. Then there
are those who know better, but have such ingrained bad habits that the
attempt to adhere to any kind of programming guidelines totally
paralyzes them and they can't get any work done as a result. Then
there are those who would like to change their ways, but management
won't let them for various reasons.

For some companies, in-house software development is only a necessary
evil (or at least treated as such). There is absolutely no budget for
refactoring unless the customer is at the door screaming. Nothing is
allocated money-wise that cannot be billed on a basis of man-hours to
a paying customer. If something works, or seems to work OK, it is sent
into production with a minimum of testing (usually most of the testing
is done by the developer, or there might be some integration tests
with such a limited amount of data that no bugs ever show up before
sending the thing to the customers). Deadlines are totally unrealistic
and are usually not met if the developers try to do any proper design
instead of just ripping out bits and pieces of other software, use 3rd
party components which seem to "do the job" without regard to later
portability or extensibility, etc. etc.

In such an environment, there is very often no code review. Developers
think they are the only ones who will ever have to maintain their own
code, especially if the project has a short life cycle due to other
concerns. Since nothing gets reused, no effort is made to make a good
design. Under these circumstances, copying and pasting often seems to
be the quickest way of implementing "code reuse".

Most of your examples are very good ones. Templates and other advanced
language features, however, were not supported adequately by a lot of
compilers until just recently, so lots of experienced developers avoid
them. As a result, they have no experience using templates and often
don't have the time (or aren't given time) for additional courses so
they can learn how to use them.

Just my 2 cents ...
 
C

Cy Edmunds

Ray-

Nice post. But if you're looking for someone to cast the first stone you'll
have to look elsewhere. We all have cut a few corners from time to time.
 
R

Ray Gardener

Cy Edmunds said:
Ray-

Nice post. But if you're looking for someone to cast the first stone you'll
have to look elsewhere. We all have cut a few corners from time
to time.


"We cannot fix what we do not dare speak about"

Ray
 
R

Ray Gardener

EventHelix.com said:
Your list is very good. One of the main problems is
lack of training.

You will not find many training courses on such bread-and-butter
issues. Training sessions often focus on much higher level of
design.

You might find the following articles interesting:

http://www.eventhelix.com/RealtimeMantra/Object_Oriented/object_d
esign_tips.htm
http://www.eventhelix.com/RealtimeMantra/Object_Oriented/object_d
esign_tips_2.htm

Sandeep


Thanks, those are good articles. The topic on hierarchical state
machines was particularly interesting.

I wonder if one could implement a language that does to C++ what
C++ does to C -- abstract it upwards (C++ was even first
implemented as a C code emitter that a C compiler would then
compile). In this language, templates and signals/slots would be
first-class citizens, crossgrain/systemwide features would be
straightforward to implement, some desirable practices would be
easier instead of harder to do, etc. I see some of these ideas
being tried elsewhere in fits and starts but having the language
emit C++ code would let us use the existing compilation
infrastructure.

Ray
 
G

galathaea

: I wonder if one could implement a language that does to C++ what
: C++ does to C -- abstract it upwards (C++ was even first
: implemented as a C code emitter that a C compiler would then
: compile). In this language, templates and signals/slots would be
: first-class citizens, crossgrain/systemwide features would be
: straightforward to implement, some desirable practices would be
: easier instead of harder to do, etc. I see some of these ideas
: being tried elsewhere in fits and starts but having the language
: emit C++ code would let us use the existing compilation
: infrastructure.

Generative programming has been a developing science for quite a few years
now. With template metaprogramming, much of this can be done inside the
type-checked part of translation, and the rest can usually be done in the
preprocessor. There are some good pattern libraries out, perhaps the most
famous being Loki, and it certainly seems to be a fertile field.

If you do not already have it, I would strongly suggest getting the book on
Generative Programming by Czarnecki and Eisenecker. It is the best textbook
I have found on these issues, though of course there are many pattern and
conference-proceedings-type books that cover fragments of the research.
 
G

galathaea

: I've always wondered what other C++ programmers
: fail to do while coding even though they know
: what they're doing is... naughty (and some of the
: items below occur in other languages, of course).
:
: People make these slip-ups all the time accidentally,
: but what I'm getting at is when we make them while
: we consciously _know_ that we're being bad. This is
: the more interesting phenomenon because I tend to
: interpret consciously "fighting the language" or
: consciously "not using the language as intended"
: as an indication that the language or the
: development environment could be improved.
:
: (Granted, in C++ the "right way" is vague because
: C/C++ has broad expressive capability, but there
: are informal standards of good coding practice
: that we all more or less know).

I don't think they are that vague. I think for many of the issues you
discuss, there are objective metrics which can be applied to at least
partially order the usefulness of approaches. I think for the most part,
though, it has taken years of research and experience with the c++ language
for these metrics to be developed in the community. And along the way, some
developers did not keep up. It is rarely taught that programmers should
read conference proceedings, IEEE Software, or other more technical sources
outside of the academic setting, and a lot of initial work on such metrics
originate in those types of places. So usually, these things come to the
working developer either through personal insight and experience or through
popularisations. In fact, my experience has been that many developers do
not even read about their art except when a new skill is required, and then
not often deeply. Some people are just not readers...

However, that is not the particular interest here. The conscious
antipattern behavior is, in my experience, a phase that most people go
through when trying to adjust their habits. Because really, I believe
programming is rarely a task limited only by time. I believe that much of
the effort in programming is mental, as trying to solve complicated logical
problems and keep all the relevant pieces juggled in short term memory can
be quite a draining endeavor. People build habits to make things easier.
Sometimes suboptimal habits get formed, and when this is discovered, the
habits must change. But changing habits can take more energy than forming
fresh, uninfluenced ones, so you need to give it time. Sometimes people
don't give it the time or effort, and the habits don't change.

So, even if the "more correct" way to do something requires less typing /
clicking / manual work overall (including future refactoring considerations
and reuse), the habituated suboptimal behavior may manifest because it
requires less mental energy. And when you are being hounded by management
and deadlines for large subsystem work, mental energy can be a rare
commodity. Only when breathing time is found, can the work on changing a
habit occur.

[...]
: I'm interested in finding out what poor choices
: C++ programmers consciously make and why. Was it
: easier at the time? More expedient? Force of habit?
: Does the dev environment foster those habits
: (e.g., an editor with great copy/paste might
: actually be bad)? Is good coding a matter of
: willpower or do certain tools turn the tide?
: Does extreme or paired-up programming help
: combat these tendencies? Is perfect discipline
: even desirable?

I do believe the development environment is an important factor. I think
this is one of the driving forces for the intensional programming movement.
 
E

E. Robert Tisdale

Ray said:
I've always wondered what other C++ programmers
fail to do while coding even though they know
what they're doing is... naughty (and some of the
items below occur in other languages, of course).

People make these slip-ups all the time accidentally,
but what I'm getting at is when we make them while
we consciously _know_ that we're being bad. This is
the more interesting phenomenon because I tend to
interpret consciously "fighting the language" or
consciously "not using the language as intended"
as an indication that the language or the
development environment could be improved.

(Granted, in C++ the "right way" is vague because
C/C++ has broad expressive capability, but there
are informal standards of good coding practice
that we all more or less know).

Examples:

- Duplicating code via copy/paste
instead of [defining and calling functions].

You mean "manual inlining" to save the cost of a function call
and permit the C++ compiler to optimize the code.
The alternative is to declare functions *inline*
so that the compiler will automatically inline them.
- Failing to make methods and other things 'const'
when they should be.

When should they be const?
- Failing to assert what should be asserted.

An example would help.
The C preprocessor assert macro is only useful
for detecting and debugging programming errors.
- Not using templates when they would be suitable.

When are templates suitable?
- Trying patches instead of refactoring
(only to sooner or later refactor anyway
and then feeling sheepish about it).

Refactoring takes time and costs money.
Feeling sheepish is free.
- Passing references to objects to functions
that give too much info to the function
when it only needs a few members of the object.

- Making "incestuous" calls instead of using
signal/slots/broadcasters/listeners or similar
design patterns.

- Using your own container classes instead of STL,
when STL would have been the better choice.

Never reference STL container classes
(or any built-in type) directly in your application source code.
Wrap the STL container class is your own class definition
so that you can substitute something else later if necessary.
- Using #define macros instead of template functions.

- Using boolean types instead of enums even though
you know that the type will soon grow beyond
two possible states.

- Letting a class grow until it has a zillion members.

Group the members into classes as appropriate.
- Using pointers instead of references
when references would have been better.

When are references better?
- Putting "is-a" members inside a class instead of
making a new subclass, and having callers query
those members in a roll-your-own form of RTTI.

- Using error code returns or object error states
instead of exceptions.

You can return an exception object instead of an error code
but you can use the function in an expression.
- Letting class members be public that shouldn't be.

When should class members be public?
- Letting a function grow really long only because
technically it doesn't actually have to be broken
down into subroutines because none of them would
be called from anywhere else anyway.

Functional decomposition helps make your code
easier to read, understand and maintain.
- Using more and more preassigned function args
to avoid having to declare another function of
a similar name, or the reverse -- not using
preassigned args where they make sense.

Where do they make sense?
- [Implementing functions] with names like "add" and "mul"
instead of using operator overloading.

- Being inconsistent in naming, spelling,
interface strategies, etc.

- Declaring more global objects than necessary.

When is it *necessary* to declare global objects?
- Placing code that makes sense in a shareable library
into application-side classes (or just letting it
sit and evolve on the app side while telling yourself
that you'll factor it out "someday").

- Using char* instead of a more Unicode-friendly string type.

- Using two member vars to track a state of only
one logical member (e.g., a bool and a pointer when
the pointer's being null will suffice to indicate
a false state).

- Specifying constants by value instead of by macro or enum.

- Using int instead of size_t when size_t is the
better type (e.g., you're declaring a zero-based counter).

- Using #define's instead of enums.

- Using external enums instead of class member enums.

- Using a struct instead of a class (even when it becomes
painfully obvious that it should be a class).

There is *no* difference between a struct and a class in C++
except that class members are private by default and
struct members are public by default.
- Solving a typical problem one way, then the same
problem a different way somewhere else because
it is superior, but failing to go back and upgrade
the usages of the first implementation.

- Using malloc/free in some places and operator new/delete
in others, for no particular reason.

Obviously some of the above are rational tactical
decisions under certain situations, so I'm not
including those combinations of choice and context.

I'm interested in finding out what poor choices
C++ programmers consciously make and why. Was it
easier at the time? More expedient? Force of habit?
Does the dev environment foster those habits
(e.g., an editor with great copy/paste might
actually be bad)? Is good coding a matter of
willpower or do certain tools turn the tide?
Does extreme or paired-up programming help
combat these tendencies? Is perfect discipline
even desirable?

The C++ computer programming language introduced
many subtle new features to C programmers.
They are still learning to use them. It takes time
and many of the older, more experienced programmers
will never fully appreciate them and take advantage of them.
Most C++ programmers are amateur programmers.
They write C++ programs to help them do the work
that they were hired and paid to do.
They typically learn just one language
and they try to use it for everything.
They learn just enough about the language
to do what they need to do.
They avoid the more complicated and subtle features
of the language and use the subset of features
with which they have experience and that
they believe they really understand.

Computer programming is *evolutionary* -- not revolutionary.
You must wait until this older generation of programmers
retires (or passes away) before you can expect real improvement.
 
J

Jonathan Turkanis

E. Robert Tisdale said:
Ray Gardener wrote:


Never reference STL container classes
(or any built-in type) directly in your application source code.
Wrap the STL container class is your own class definition
so that you can substitute something else later if necessary.

How can you reference STL containers other than directly in your
source code? Maybe if you said 'rarely use STL containers in your
public interfaces' I could agree.

Do you really think one should never use built-in types, such as int?
There is *no* difference between a struct and a class in C++
except that class members are private by default and
struct members are public by default.

And struct inheritance is public by default.

Jonathan
 
E

E. Robert Tisdale

Jonathan said:
Do you really think one should never use built-in types, such as int?

typedef signed int Integer;
typedef unsigned int Cardinal;
typedef float Single;
typedef double Double;

You bet if you code really must be portable.
This allows you to substitute a more appropriate type
(or even a class definition) if the target platform
doesn't support the precision that you need for these objects.
 
R

Ray Gardener

Generative programming has been a developing science for quite
a few years
now. With template metaprogramming, much of this can be done inside the
type-checked part of translation, and the rest can usually be done in the
preprocessor. There are some good pattern libraries out, perhaps the most
famous being Loki, and it certainly seems to be a fertile field.

If you do not already have it, I would strongly suggest getting the book on
Generative Programming by Czarnecki and Eisenecker. It is the best textbook
I have found on these issues, though of course there are many pattern and
conference-proceedings-type books that cover fragments of the
research.

Thanks, those look very interesting.

I would agree for the most part that environment plays a big
factor. I have seen and experienced the effects upon developers.

I guess my situation is more unique in that I work alone, so my
coding style is more a matter of discipline (i.e., I have less
external factors I can blame :)
But this is also an opportunity to achieve some personal growth
that would otherwise be unavailable. As long as I don't have
managers laying constraints on my engineering, I may as well see
what is possible. Not that I'm unhappy with the way I do things
today, but it would be nice to maybe move up a step or two if
possible.

I find that it gets harder to think in the abstract -- I once got
halfway through writing a Wizard that would emit C++ code for
plug-ins given their specifications, and the going got weird.
The generator source looks template-like and working with it
feels akin to sloughing through an alien swamp. Maybe "harder" is
the wrong word -- it's just different enough that one cannot go
racing through it at one's usual speed. People like what is
familiar.

I fear this may be a fundamental barrier to programmer growth.
It's not that we don't have good tools or know what they consist
of, but using them is simply beyond an increasing percentage of
developers. Evolution has designed us to deal mostly with
everyday reality and to think abstractly only a small fraction of
the time.

Ray
 
G

galathaea

: "E. Robert Tisdale" wrote
: > > - Using your own container classes instead of STL,
: > > when STL would have been the better choice.
: >
: > Never reference STL container classes
: > (or any built-in type) directly in your application source code.
: > Wrap the STL container class is your own class definition
: > so that you can substitute something else later if necessary.
: >
:
: How can you reference STL containers other than directly in your
: source code? Maybe if you said 'rarely use STL containers in your
: public interfaces' I could agree.

I wouldn't say never about this, either. Using a typedef alias as a
variation point can be very useful, but that type of duty is better done
through a simple refactoring and shouldn't be mandatory the first time
through a piece of code. Then, maybe wrap in a class later if you are
looking to integrate functionality with a collection of other objects in the
system with container like properties, but that would likely involve
inheriting an interface base and offloading a trait or two for runtime and
translationtime abstraction.
 
G

galathaea

: Thanks, those look very interesting.
:
: I would agree for the most part that environment plays a big
: factor. I have seen and experienced the effects upon developers.
:
: I guess my situation is more unique in that I work alone, so my
: coding style is more a matter of discipline (i.e., I have less
: external factors I can blame :)
: But this is also an opportunity to achieve some personal growth
: that would otherwise be unavailable. As long as I don't have
: managers laying constraints on my engineering, I may as well see
: what is possible. Not that I'm unhappy with the way I do things
: today, but it would be nice to maybe move up a step or two if
: possible.

I too work mostly alone, though I wish I had more time to pair. It can be
difficult without some kind of one on one mentoring regularly help us move
forward. Thats actually why I like newsgroups, message boards, and related
technology so much!

: I find that it gets harder to think in the abstract -- I once got
: halfway through writing a Wizard that would emit C++ code for
: plug-ins given their specifications, and the going got weird.
: The generator source looks template-like and working with it
: feels akin to sloughing through an alien swamp. Maybe "harder" is
: the wrong word -- it's just different enough that one cannot go
: racing through it at one's usual speed. People like what is
: familiar.

Exactly! The more you advance in any field, the more likely it is that your
gonna encounter some mind-bending logical problem that you cannot figure
out. Those are exactly the type of situations where having someone just sit
you down and teach you what to do will bring the most benefit.

: I fear this may be a fundamental barrier to programmer growth.
: It's not that we don't have good tools or know what they consist
: of, but using them is simply beyond an increasing percentage of
: developers. Evolution has designed us to deal mostly with
: everyday reality and to think abstractly only a small fraction of
: the time.

I think its a serious failing of education. We're already obsessive
creatures. Its our evolutionary advantage. Our babies will stare at an
object for longer than nearly any other creature. Language is an obsession
with associating concepts with sounds. I think that most developers can
probably learn how to efficiently do their jobs with the effort that they
put into their career today (in other words, I don't think learning better
requires more effort by the learner). They just need more direction and
perhaps more interactive education such as found in computer-based tools.
The developers I have seen that have "given up" learning been tired out of
the learning process most of the time because there was a problem that
stalled their progress.
 
E

EventHelix.com

I wonder if one could implement a language that does to C++ what
C++ does to C -- abstract it upwards (C++ was even first
implemented as a C code emitter that a C compiler would then
compile). In this language, templates and signals/slots would be
first-class citizens, crossgrain/systemwide features would be
straightforward to implement, some desirable practices would be
easier instead of harder to do, etc. I see some of these ideas
being tried elsewhere in fits and starts but having the language
emit C++ code would let us use the existing compilation
infrastructure.

Thats an interesting idea. Actually this could be achieved within
the C++ by implementing a higher abstraction framework with
clever use of templates and operator overloading.

Some of the other consistancy checks could be implemented
by a version of lint for C++.

Sandeep
 
R

Ray Gardener

EventHelix.com said:
Thats an interesting idea. Actually this could be achieved within
the C++ by implementing a higher abstraction framework with
clever use of templates and operator overloading.

I'd use templates more if there were some way to debug
them as comprehensively as normal C++ code. It's the
only thing holding back more deployment. Or even just
to be able to see the code the templates emit.

Ray
 
J

Jonathan Turkanis

E. Robert Tisdale said:
int?

typedef signed int Integer;
typedef unsigned int Cardinal;
typedef float Single;
typedef double Double;

You bet if you code really must be portable.
This allows you to substitute a more appropriate type
(or even a class definition) if the target platform
doesn't support the precision that you need for these objects.

This is appropriate sometimes, to be sure. But to say you should never
use int is a gross exaggeration.

Jonathan
 
L

lilburne

E. Robert Tisdale said:
typedef signed int Integer;
typedef unsigned int Cardinal;
typedef float Single;
typedef double Double;

You bet if you code really must be portable.
This allows you to substitute a more appropriate type
(or even a class definition) if the target platform
doesn't support the precision that you need for these objects.

Except it doesn't work. I well remember back in the 80's when the vogue
was for WORD, LONG, etc, and an implementation from one compiler system
that included the following.

/* WARNING USE OF THIS FILE WILL MAKE YOUR PROGRAM INCOMPATIBLE WITH
THE REST OF THE UNIVERSE. */

and you know what they were right!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,007
Latest member
obedient dusk

Latest Threads

Top