[comp.sys.amiga.programmer] Good Programming stops guru |||| and BNF of C++

a976@mindlink.UUCP (Ron Tarrant) (01/09/91)

> m0154@tnc.UUCP writes:
> 
> * Quick way to test for outstanding files or memory allocation - run
> your program several times in succession.  Use Avail and note the
> amount of free memory after each one.  The first time you run your
> program, any needed libraries will be loaded into RAM.  After that,
> you should get the same amount of free ram each time.  Beware if the
> amount goes down by the same amount each time!
> 
> Wildstar


There's another way to do this and you only have to run your program once.
When you start up WorkBench with the loadwb command, give it an argument of
"-debug". This enables an "invisible" menu to the right of all others on the WB
screen. This menu (accessed by holding down the right mouse button and moving
the pointer to the right of the last labeled menu) has two items: debug (which
sends debug info to the serial port) and flushlibs (which dumps any currently
unused libraries from the system.
        Make note of the amount of free ram listed in the menubar of the WB
screen. Then, making sure you don't resize your CLI window, run your program,
do whatever with your program, then quit. Select "flushlibs", then recheck the
amount of free ram in the menubar. It should be the same as what you noted
before running the program.
        I found this tidbit in an issue of AmigaMail, I think.
-Ron

rcs91900@zach.fit.edu ( Charles Stockman /ADVISOR-Clutterham) (01/09/91)

First of all I wanted to thank everyone who answered my question about how 
I could descrease the number of times the guru appeared.  However, I would
like to continue this discussion, so could someone tell me how to do safe
coding (coding that protects you from the guru).

ex : One technique that I know is to make sure that all files and 
     resourses that were allocated to a program are released by the
     programmer.

My next problem is that I am looking for the BNF of C++ .  Could someone please
mail it to me 

Once again --> Thanks a lot for your help

dave@cs.arizona.edu (Dave P. Schaumann) (01/09/91)

In article <1806@winnie.fit.edu> rcs91900@zach.fit.edu ( Charles Stockman /ADVISOR-Clutterham) writes:
>First of all I wanted to thank everyone who answered my question about how 
>I could descrease the number of times the guru appeared.  However, I would
>like to continue this discussion, so could someone tell me how to do safe
>coding (coding that protects you from the guru).

One thing you certainly want to watch is how you use memory you get from
malloc.  I recently had a bug where I said 'malloc(foo)', when I really
wanted 'malloc(foo * sizeof(int)'.  Naturally, in the first version, subsequent
code stomped all over the memory past the allocated block.  Unfortunately,
this contained information vital to the integrety of the free memory list.

The result of this is at some time later (usually shortly after my program
quit) my Amiga would crash and burn.  Usually with the 'memory freed twice'
guru.

Dave Schaumann		| My folks went to uunet.uu.net, but all
dave@cs.arizona.edu	| they got me was this lousy .sig...

m0154@tnc.UUCP (GUY GARNETT) (01/09/91)

In article <1806@winnie.fit.edu> rcs91900@zach.fit.edu ( Charles Stockman /ADVISOR-Clutterham) writes:
>First of all I wanted to thank everyone who answered my question about how 
>I could descrease the number of times the guru appeared.  However, I would
>like to continue this discussion, so could someone tell me how to do safe
>coding (coding that protects you from the guru).
>
>ex : One technique that I know is to make sure that all files and 
>     resourses that were allocated to a program are released by the
>     programmer.
>
>My next problem is that I am looking for the BNF of C++ .  Could someone please
>mail it to me 
>
>Once again --> Thanks a lot for your help


Sorry, I don't have the BNF for C++ ... but I do have some basic tips
for defensive programming.  I realize that most of the readers already
know most of these, but repetition never hurts.

* Check all return values - Most library functions return a special
value for error conditions.  Test for them.  I realize that this is
basic stuff, but at some point that AllocMem of 32 bytes that "can't
fail" will fail, and then its off to see the GURU.  This also includes
testing file opens & locks, and so on.  If it returns a value, check
it before you go on.

* Pay attention to pointer usage - there have been commercially
released programs which reference a null pointer, and thereby trash
AbsExecBase.  All it takes is one, buried somewhere in your code
(cleanup and terminate routines are especially prone to this).  One of
the indicators of this problem is that the machine Guru's *after* you
have exited your program (ie: when you try to do something else).

* Quick way to test for outstanding files or memory allocation - run
your program several times in succession.  Use Avail and note the
amount of free memory after each one.  The first time you run your
program, any needed libraries will be loaded into RAM.  After that,
you should get the same amount of free ram each time.  Beware if the
amount goes down by the same amount each time!

* There are several good PD system and memory monitors around.  If
they suit your needs, use them ... Almost all of them will let you
spot outstanding file allocations, and others are useful for detecting
the null pointer problem.
 
* Plan your program (even if you "don't do designs") - Planned in
advance, your program can have efficient ways of performing its
internal tests, and can recover or exit gracefully.  Some of the best
have an "exit module" which closes files, deallocates memory, and
closes libraries for the whole program on exit.  Grafting this kind of
stuff onto an existing program is harder.

Wildstar

markv@kuhub.cc.ukans.edu (01/10/91)

> * Check all return values - Most library functions return a special
> value for error conditions.

And pay attention to what the return value should be.  Sometimes a 0
is failure, sometimes -1, sometimes 0 is success, etc.  If using C lib
functions also check errors.  You can use _OSERR, _error, and IoErr()
for more detailed info.  And try to handle errors well.  Some failures
are critical, some aren't.  If you cant open a requester, then use an 
Alert so the user knows what is going on.  An alert will *almost*
always work because it only needs a few 100 bytes.

> * Pay attention to pointer usage - there have been commercially
> released programs which reference a null pointer, and thereby trash
> AbsExecBase.

Another no-no is referenceing a pointer after it has been free, or
range errors scribbling off the end of an array.  On things like an
array always include memory for an extra element to errors of 1 (ie
referecing element index 20 in a 20 element array), or hold a NULL in
a string.

AllocRemember()/FreeRemember() can help with long chains of allocations.
 
Also, when you run AVAIL, it will walk the whole system memory list,
so any errors/corruption will be found.

And when using things like realloc(), ParentDir(), etc (functions that
"swap" data structures), dont assume the new pointer is the same as
the old.

> * There are several good PD system and memory monitors around.  If
> they suit your needs, use them ... Almost all of them will let you
> spot outstanding file allocations, and others are useful for detecting
> the null pointer problem.

Enforcer is good for catching bad pointer refs, but needs a MMU.
Mungwall munges memory around your allocations and all free RAM to
increase the chance that bounds errors or RAM problems will hit
(you cant find a bug you dont have).

> internal tests, and can recover or exit gracefully.  Some of the best
> have an "exit module" which closes files, deallocates memory, and
> closes libraries for the whole program on exit.  Grafting this kind of
> stuff onto an existing program is harder.

This is the way I work.  Have your pointers to things like windows,
screens, etc be global.  Initialize them to NULL, and set them to NULL
when you free them.  Then when your program exits, you can allocate
all that you have freed.

Ie:  struct Window *OurWindow;	/* Static data is NULL by default */

int DoExit(int foo)
{ 
	if (OurWindow) {
		CloseWindow(OurWindow);
	}
	OurWindow=NULL;
}

This way the function can be called anywhere in your program and work 
pretty well.  Also if using C, use the ANSI function atexit() or
SAS/Lattice onexit() to install your function as an exit trap, so it
gets called even if your program aborts beyond your control.

> Wildstar
-- 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Mark Gooderum			Only...		\    Good Cheer !!!
Academic Computing Services	       ///	  \___________________________
University of Kansas		     ///  /|         __    _
Bix:	  markgood	      \\\  ///  /__| |\/| | | _   /_\  makes it
Bitnet:   MARKV@UKANVAX		\/\/  /    | |  | | |__| /   \ possible...
Internet: markv@kuhub.cc.ukans.edu
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

jesup@cbmvax.commodore.com (Randell Jesup) (01/10/91)

In article <629@caslon.cs.arizona.edu> dave@cs.arizona.edu (Dave P. Schaumann) writes:
>One thing you certainly want to watch is how you use memory you get from
>malloc.  I recently had a bug where I said 'malloc(foo)', when I really
>wanted 'malloc(foo * sizeof(int)'.  Naturally, in the first version, subsequent
>code stomped all over the memory past the allocated block.  Unfortunately,
>this contained information vital to the integrety of the free memory list.
>
>The result of this is at some time later (usually shortly after my program
>quit) my Amiga would crash and burn.  Usually with the 'memory freed twice'
>guru.

	Ah, you need MungWall!  Mungwall would catch that, and show you the
trashing (as well as mostly insulate the system from the error, unless you
went _way_ out of bounds).

-- 
Randell Jesup, Keeper of AmigaDos, Commodore Engineering.
{uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.commodore.com  BIX: rjesup  
The compiler runs
Like a swift-flowing river
I wait in silence.  (From "The Zen of Programming")  ;-)

dave@cs.arizona.edu (Dave P. Schaumann) (01/10/91)

In article <1806@winnie.fit.edu> rcs91900@zach.fit.edu ( Charles Stockman /ADVISOR-Clutterham) writes:

>My next problem is that I am looking for the BNF of C++ .  Could someone
>please mail it to me 
>
If you have access to ftp, check out the FAQ list in comp.compilers.  That
lists a sight that has a yacc grammar for C++ (which is not the same as BNF,
but the differences are minor).  If this is not satisfactory, you might have
better success with your request if you posted it to comp.lang.c++.

>Once again --> Thanks a lot for your help

No problem.

Dave Schaumann		| You are in a twisty maze of little
dave@cs.arizona.edu	| C statements, all different.

jap@convex.cl.msu.edu (Joe Porkka) (01/11/91)

a976@mindlink.UUCP (Ron Tarrant) writes:

>> m0154@tnc.UUCP writes:


>There's another way to do this and you only have to run your program once.
>screen. Then, making sure you don't resize your CLI window, run your program,
>do whatever with your program, then quit. Select "flushlibs", then recheck the
>amount of free ram in the menubar. It should be the same as what you noted
>before running the program.

I dunno if this takes into account libs opening libs.
I have a program that does the following
 
	y=AvailMem(0)
	do {
	     x=y;
	     t=AllocMem(1<<31); /* Should fial :-) */
	     if(t!=0) {FreeMem(t,1<<31); break;}
	     y=AvailMem(0);
	} while(x<y);
	printf("%d %d\n", x, y);

This makes as much free mem as possible, even if open lib opens other
libs. 

rg20+@andrew.cmu.edu (Rick Francis Golembiewski) (01/11/91)

A Few trick that I have found to be very helpful in dealing with
pointers, is that you should initalize all pointers to NULL, and also
EXPECT all pointers that you deal with to be NULL. (ie make loops like

while (foo != NULL) {..} (and be sure that foo will at some point be NULL)

Also if you have pointers to pointers, like:

foo->bar->var, be sure that foo and bar are NOT NULL. before doing

foo->bar->var=value;

Also watch when passing constant arguments, for instance calling:
foo(A,B,0,D)

for

int foo(A,B,C,D)
double A,B,C,D;
{
}
is wrong, it should be
foo(A,B,0.0,D)
         ^
         |
   This a a FP argument while 0 is interpreted as an integer argument,
so the values for C & D will be mangled (I've been bitten on this one
with Lattice a few times).

Another thing, if you have an array like:
char foo[MAXLEN];

When you do loops include,
i=0;
while( i<MAXLEN) {foo[i++]='\0'....}

That way you don't overrun the size of the array, this is especially
important when doing string functions, as there might be some without
proper '\0' terminations.  

In general it's a good idea to initiallize ALL of your variables, as
some compilers initalize vars to 0 while others may not.

Try not to have lots of global variables, but if a variable gets
passed to most every function it is probabily a good idea to make it
globa (things like window & screen pointers are things that come to
mind). Try to break down your code into small modules so that they are
easier to debug, (and so that the smaller lower level modules may be
used in other programs without rewriting them).  Don't make
allocations (of memory, or any ofther resource ie [ports, audio
chanels]) unless you need them, adnd then FREE THEM UP WHEN YOU ARE
DONE!  (ie fclose(file), free(memory) etc.).  There are certainly
several other good programming practices, but I think that this post
is long enough already.  I'm sure that if I've made any mistakes in
the above I'll get plenty of flames for it...

//     Rick Golembiewski  rg20+@andrew.cmu.edu  \\
\\       #include stddisclaimer.h               //
 \\  "I never respected a man who could spell" //
  \\               -M. Twain                  //

mykes@zorch.SF-Bay.ORG (Mike Schwartz) (01/11/91)

I use Manx 'C' 3.6, so my advise may be limited to this environment.

Manx supplies a routine called _abort() in the library which can be
used to make programs that EASILY clean up after themselves.  The routine
is automatically called by the Manx debuggers anytime you want to abort
the program in the middle of a debug session.  The routine is also called
when you hit ^C while the program is running.  By supplying your own
_abort() routine, you can override the one in the library and use it to
your advantage.  Consider the following code (I have something like it
in every one of my 'C' programs):


struct IntuitionBase	*IntuitionBase = 0;
struct GfxBase		*GfxBase = 0;

/* other libraries can be opened as well... */

struct Screen		*screen = 0;
struct Window		*window = 0;

int	_abort_code = 0;
_abort() {
	struct IntuiMessage	*m;

	if (window) {
		while (m = (struct IntuiMessage *)GetMsg(window->UserPort))
			ReplyMsg(m);
		CloseWindow(window);
		window = 0;
	}
	if (screen) {
		CloseScreen(screen);
		screen = 0;
	}
	if (GfxBase) {
		CloseLibrary(GfxBase);
		GfxBase = 0;
	}
	if (IntuitionBase) {
		CloseLibrary(IntuitionBase);

		IntuitionBase = 0;
	}
	exit(_abort_code);
}


panic(s, a1,a2,a3,a4,a5,a6,a7,a8)
ULONG	s,a1,a2,a3,a4,a5,a6,a7,a8;	/* who cares what type they were...  */
{
	printf(s, a1,a2,a3,a4,a5,a6,a7,a8);
	_abort_code = 999;
	_abort();
}


panic0(v, s, a1,a2,a3,a4,a5,a6,a7,a8)
ULONG	s,a1,a2,a3,a4,a5,a6,a7,a8;	/* ULONG can be int,short,byte, etc. */
{

	if (v) return;			/* only panic if v is zero */
	printf(s, a1,a2,a3,a4,a5,a6,a7,a8);
	_abort_code = 999;
	_abort();
}


main() {
	IntuitionBase = (struct IntuitionBase *)OpenLibrary(
	    "intuition.library", 0);
	panic0(IntuitionBase, "Can't open intuition\n");
	GfxBase = ...
	/* application goes here */
	_abort();		/* _abort instead of exit */
}


The code for panic shows how (using 32-bit INT model of Manx) you can
make a printf() like routine without a lot of ugly looking code.  The
drawback is that you are pushing a lot of parameters in a lot of places.

The code for panic0 shows a way to eliminate a lot of "if" statements
from your source code, making a much more brief and readable program.

The reason I set the things that _abort() cleans up back to zero is so that
if I am using a debugger, or I make a bug that calls _abort() twice somehow,
I can use the debugger to execute _abort() again without cleaning up things
twice (it may not be too clear from my explanaition, so):

You are in your debugger.  You can just say "modify register PC = " _abort()
and then GO and your program cleans up and exits.

Some more tips...

You should look at the fields in the Task structure.  There are ways to eliminate
most GURUs from happening if you install your own exception handler.  The vast
majority of GURUs are recoverable (merely an odd address or illegal instruction).  If you make an exception handler, you can force the program to use your
_abort() routine instead of the default "GURU" handlers which force you to
reboot.

I hate typing Intuition because I constantly misspell it (Intutition, etc.).
I hate typing (struct anything *) to cast.  I hate typing struct anything
period.  So what I did was to make an include file called "host.h" that
looks something like this:

typedef struct IntuitionBase	IBASE;
typedef struct GfxBase		GBASE;
typedef struct RastPort		RPORT;
typedef struct MessagePort	MPORT;
typedef struct Message		MSG;
typedef struct IntuiMessage	IMSG;

etc.

I put all kinds of things in this file that I used to retype in many many
times/programs.  Now I can do:

IBASE	*IntuitionBase;

msg = (MSG *)malloc(sizeof(MSG));

etc.  Which is much easier to read and type.

Another candidate for the host.h file is function declarations like:
UBYTE	*malloc();
and another is parameters to open() which I hate to type as well:
#define	O_READ	O_RDONLY
#define	O_WRITE	(O_WRONLY|O_CREAT|O_TRUNC)

If you don't have powerwindows, get it.  It is the fastest and most interactive
way I've found to generate all those nasty intuition structures a program needs.

I hope this is some help.

Mykes

dillon@overload.Berkeley.CA.US (Matthew Dillon) (01/14/91)

In article <1991Jan11.073543.16293@zorch.SF-Bay.ORG> mykes@zorch.SF-Bay.ORG (Mike Schwartz) writes:
>I use Manx 'C' 3.6, so my advise may be limited to this environment.
>
>Manx supplies a routine called _abort() in the library which can be

    Under ANSI C you can use atexit().  This will be MUCH more portable
    then _abort() which is MANX specific.

    main()
    {
	atexit(myexitrt);

	...
    }

    myexirt() is called when the program exits, whether it be a normal
    exit or an abnormal exit.

>The code for panic shows how (using 32-bit INT model of Manx) you can
>make a printf() like routine without a lot of ugly looking code.  The
>drawback is that you are pushing a lot of parameters in a lot of places.

    Additionally, under ANSI C, there are var-args printing calls,
    vprintf(), vsprintf(), and vfprintf().  These make it easy to
    write your own error-printing routines without having to hack
    large variable lists:

	/*
	 *  Example use var-args [v]*printf() orutines
	 *  get the math pfmt.
	 */

	#include <stdio.h>
	#include <stdarg.h>

	void gagprint(char *, ...);

	main()
	{
	    gagprint("%d %d %d\n", 1, 2, 3);
	    return(0);
	}

	void
	gagprint(ctl, ...)
	char *ctl;
	{
	    va_list va;
	    int n;

	    va_start(va, ctl);
	    n = vprintf(ctl, va);
	    printf("%d chars written\n", n);
	    va_end(va);
	}

>
>Mykes

--
				-Matt


    Matthew Dillon	    dillon@Overload.Berkeley.CA.US
    891 Regal Rd.	    uunet.uu.net!overload!dillon
    Berkeley, Ca. 94708
    USA

dillon@overload.Berkeley.CA.US (Matthew Dillon) (01/14/91)

In article <8bXHP_y00VIFALti1O@andrew.cmu.edu> rg20+@andrew.cmu.edu (Rick Francis Golembiewski) writes:
>
>Also watch when passing constant arguments, for instance calling:
>foo(A,B,0,D)
>for
>int foo(A,B,C,D)
>double A,B,C,D;
>{
>}
>is wrong, it should be
>foo(A,B,0.0,D)
>	  ^
>	  |
>   This a a FP argument while 0 is interpreted as an integer argument,

    This is one of things that prototypes will do for you... argument
    converesion.  If you had the prototype:

int foo(double, double, double, double);

    then foo(A,B,0,D); will work just fine.

>In general it's a good idea to initiallize ALL of your variables, as
>some compilers initalize vars to 0 while others may not.

    Not true, you can rely on static's and global's being initialized to 0,
    assuming you go through the normal startup obj for the compiler.

    auto's (stack variables) are NOT initialized (i.e. contain garbage until
    you initialize them)

>Try not to have lots of global variables, but if a variable gets
>passed to most every function it is probabily a good idea to make it
>globa (things like window & screen pointers are things that come to
>mind). Try to break down your code into small modules so that they are
>easier to debug, (and so that the smaller lower level modules may be
>used in other programs without rewriting them).  Don't make
>allocations (of memory, or any ofther resource ie [ports, audio
>chanels]) unless you need them, adnd then FREE THEM UP WHEN YOU ARE
>DONE!	(ie fclose(file), free(memory) etc.).  There are certainly
>several other good programming practices, but I think that this post
>is long enough already.  I'm sure that if I've made any mistakes in
>the above I'll get plenty of flames for it...

    Those are good points.  The term you are looking for 'Modularity'.
    I have found that by dividing procedures in a logical manner and
    not using globals for variables that really ought to be locals
    (like a loop counter) you generally wind up with much better code
    no matter what kind of programmer you are.

>//	Rick Golembiewski  rg20+@andrew.cmu.edu  \\
>\\	  #include stddisclaimer.h		 //
> \\  "I never respected a man who could spell" //
>  \\		    -M. Twain		       //

--
				-Matt


    Matthew Dillon	    dillon@Overload.Berkeley.CA.US
    891 Regal Rd.	    uunet.uu.net!overload!dillon
    Berkeley, Ca. 94708
    USA

andrew@teslab.lab.OZ (Andrew Phillips) (01/24/91)

In article <1991Jan11.073543.16293@zorch.SF-Bay.ORG> mykes@zorch.SF-Bay.ORG (Mike Schwartz) writes:
>Manx supplies a routine called _abort() in the library which can be
>used to make programs that EASILY clean up after themselves. ...

For portability you would be better off using the ANSI routine
onexit(), which allows you to register routines to be called when
exit() is called.  This has the advantage of allowing reusable
routines to have this facility without _abort() having to know how
they work internally.


>panic(s, a1,a2,a3,a4,a5,a6,a7,a8)
>ULONG	s,a1,a2,a3,a4,a5,a6,a7,a8;  /* who cares what type they were...  */
>{
>	printf(s, a1,a2,a3,a4,a5,a6,a7,a8);
>...

>The code for panic shows how (using 32-bit INT model of Manx) you can
>make a printf() like routine without a lot of ugly looking code.  The
>drawback is that you are pushing a lot of parameters in a lot of places.

Why not use vprintf() which is neater and is also ANSI, from memory.
The above also won't work if you have too many parameters to panic.

>panic0(v, s, a1,a2,a3,a4,a5,a6,a7,a8)
--------^-------
>ULONG	s,a1,a2,a3,a4,a5,a6,a7,a8;  /* ULONG can be int,short,byte, etc. */
>{
>...

>	panic0(IntuitionBase, "Can't open intuition\n");

This is a really good idea but, shouldn't "v" be declared to be a
"void *" rather than defaulting to int.
-- 
Andrew Phillips (andrew@teslab.lab.oz.au) Phone +61 (Aust) 2 (Sydney) 289 8712

andrew@teslab.lab.OZ (Andrew Phillips) (01/24/91)

In article <8bXHP_y00VIFALti1O@andrew.cmu.edu> rg20+@andrew.cmu.edu (Rick Francis Golembiewski) writes:
]int foo(A,B,C,D)
]double A,B,C,D;
]{
]...
]foo(A,B,0,D)
]is wrong, it should be
]foo(A,B,0.0,D)
]         ^
]         |
]   This a a FP argument while 0 is interpreted as an integer argument,...

This is why function prototypes were invented, use them!

]In general it's a good idea to initiallize ALL of your variables, as
]some compilers initalize vars to 0 while others may not.

This is not true.
Uninitialised static variables are *always* guaranteed to be zero.
Uninitialised auto variables are *never* guaranteed to be anything.

-- 
Andrew Phillips (andrew@teslab.lab.oz.au) Phone +61 (Aust) 2 (Sydney) 289 8712