[comp.lang.c] questionnaire

phil@ux1.cso.uiuc.edu (Phil Howard KA9WGN) (01/12/90)

I would like to find out from some different people what kinds of decisions
you typically make, and how you might make them regarding:
 
1.  Whether or not to use "typedef" for a "struct"
    a.  how do you deal with the case of two different struct types
        having pointers to the other type?
 
2.  When you decide to separate structures into header files
    a.  how big must the program be to warrant this?
    b.  do you always do so?
    c.  never?
 
3.  Making functions used (in many places) only by your program extern
    a.  what if only one program calls this function?
 
4.  Do you consider passing a "pointer to a function" to another function
    which will (blindly?) use it to call that function, a "kosher"
    programming practice?
-- 

--Phil Howard, KA9WGN--
<phil@ux1.cso.uiuc.edu>

mlandau@bbn.com (Matt Landau) (01/13/90)

[I originally sent a private email reply, but later decided this
 topic was interesting enough to warrant some exchange of ideas 
 on programming styles.  I know, we have this conversation once
 a year and it often degenerates into a flame war, but this time
 the questions Phile asked were specific enough that maybe there's 
 hope...]

>1.  Whether or not to use "typedef" for a "struct"
>    a.  how do you deal with the case of two different struct types
>        having pointers to the other type?

Depends on the semantics of what the structure is for.  In general,
if a structure is going to be used by other parts of a program, or
if it's part of a library interface, I use typedefs.  

Whether the typedef corresponds to the structure or to a pointer to 
the structure depends on how I expect the user of the extrinsic 
interface to use it -- if it's intended that you can poke around 
and access fields of the structure, then I use something along the
lines of "typedef struct foo foo_t".  

If, on the other hand, I want the implementation details to be hidden, 
then I use something like "typedef struct foo *foo_t".  I almost
never provide both forms for a single structure, because if you're 
supposed to be able to poke around inside it, then I want you to 
use "foo_t *fooptr" for a pointer to an object of this type, to make 
manifest the fact that it's a pointer to something you can get 
inside of.

In both cases, I tend to provide access functions (or macros) to 
set and get the parts of the data structure that are supposed to
be public.  That is, I'd either do this:

	struct _complex_struct {
	    /* Public */
		float	real_part;
		float	unreal_part;
	};

	typedef struct _complex_struct complex_t;

	#define	COMPLEX_REAL(c)		((c).real_part)
	#define	COMPLEX_IMAGINARY(c)	((c).unreal_part)

or else I'd do this:

	struct _bag_struct {
	    /* Public */
		int	obj_count;
		int	bag_size;
		thing_t	*obj_list;
	    /* Private */
		bool	initialized;
		cache_t	cache_elems;
		type_t  *base_types;
	};

	typedef struct _bag_struct bag_t;

	#define BAG_SIZE(bag)		((bag)->bag_size)
	#define	BAG_COUNT(bag)		((bag)->obj_count)
	#define BAG_CONTENTS(bag)	((bag)->obj_list)
		
>2.  When you decide to separate structures into header files
>    a.  how big must the program be to warrant this?
>    b.  do you always do so?
>    c.  never?

Any structure used by more than 1 source module gets put in a header
file, along with the typedefs and access macros (or function externs)
that apply to it.  I often group the structure/macro/extern info for
multiple source modules into a single header, if those sources make up
a single logical library package.  I also often wish C had real 
support for multi-file modules...

>3.  Making functions used (in many places) only by your program extern
>    a.  what if only one program calls this function?

If a function is generically useful, it's an extern, and often goes
into a libraries.  (Even my small programs often consist of a library
and a program-specific part.)  If it's not generically useful and it's
only used in one module, then it's static.  If it's not generically
useful and it's used in more than one module, then I ususally use 
either a naming convention, or a piece of syntactic sugar.

In a header someplace, I'd have this:

    /* Some people will find this gross -- I like it :-) */
    #define PUBLIC	/* nothing */
    #define PRIVATE	static 	    	 /* static functions */
    #define PACKAGE	/* nothing */    /* internal to a module */


The source code would look like this:

    PUBLIC bag_t CreateBag(size)
    int size;
    {
	bag_t self;

	if ((self = bag_alloc(size)) == NULL)
	    ex_throw(EX_NO_MEMORY, "Not enough memory to make a bag.");
	...
	return (self);
    }

    PUBLIC void DestroyBag(bag)
    bag_t bag;
    {
	...
    }

    PRIVATE bag_t bag_alloc(size)
    int size;
    {
	...
    }

    PACKAGE bag_t bag_check_size(size, nitems)
    int size;
    int nitems;
    {
	...
    }

>4.  Do you consider passing a "pointer to a function" to another function
>    which will (blindly?) use it to call that function, a "kosher"
>    programming practice?

Not only kosher, but sometimes necessary.  

For instance, all of my libraries and toolkits have hooks that allow 
the caller to specify a routine that is to be used to display internal 
error messages.  The default routine either ignores them or prints
them on stderr.  

A program with a graphical user interface might want to override the 
default error output routine to display the message in a popup dialog 
box.  This is very easy to do if you have the library source code
dereference a function pointer to get the function used for message
display.

Since the output routines are expected to to use a well defined 
calling syntax based on varargs (much like that of vprintf), all of
my libraries and my application error handlers are interchangeable,
and we all know that Code Reusability is Good :-)

tps@chem.ucsd.edu (Tom Stockfisch) (01/13/90)

In article <1990Jan12.022039.15799@ux1.cso.uiuc.edu> phil@ux1.cso.uiuc.edu (Phil Howard KA9WGN) writes:
>I would like to find out from some different people what kinds of decisions
>you typically make, and how you might make them regarding:
>1.  Whether or not to use "typedef" for a "struct"

I define *all* structs as

	typedef struct FOO {...} FOO;

Typedefs and struct tags belong to different identifier overload classes,
so the same name may be used for both.  Now you can say either
	
	struct FOO	bar;

or just

	FOO	bar;

>    a.  how do you deal with the case of two different struct types
>        having pointers to the other type?

This method automatically takes care of this:

	struct FOO2;

	typedef struct FOO {
		struct FOO	*next, *previous;
		...
		struct FOO2	*buddy;
	}	FOO;

	typedef struct FOO2 {
		struct FOO2	*next, *previous;
		...
		struct FOO	*buddy;
	}	FOO2;

>2.  When you decide to separate structures into header files

If you have two different source files which use the same struct, the struct
definition better be in a header file which is included by both.  Otherwise,
I probably would keep it in the same file.
-- 

|| Tom Stockfisch, UCSD Chemistry	tps@chem.ucsd.edu

phil@ux1.cso.uiuc.edu (01/17/90)

> This method automatically takes care of this:
> 
> 	struct FOO2;
> 
> 	typedef struct FOO {
> 		struct FOO	*next, *previous;
> 		...
> 		struct FOO2	*buddy;
> 	}	FOO;
> 
> 	typedef struct FOO2 {
> 		struct FOO2	*next, *previous;
> 		...
> 		struct FOO	*buddy;
> 	}	FOO2;

Is the first "struct FOO2" really necessary?  What is the scope of the
definition of struct FOO2 inside the first typdef?  If the scope of the
definition is limited as such, and the second typedef again defines FOO2
(for the first time in the wider scope) do these two distinct definitions
actuall rendevous in some way as to have them deal with each other as
being the same thing?

--Phil Howard, KA9WGN--
<phil@ux1.cso.uiuc.edu>

rick_jones@f616.n713.fido.oz (Rick Jones) (01/18/90)

Original to: phil@ux1.cso.uiuc.edu
In a message of <12 Jan 90 02:20:39>, phil@ux1.cso.uiuc.edu (3:713/603) writes:

 pA>1.  Whether or not to use "typedef" for a "struct"
 pA>    a.  how do you deal with the case of two different struct types
 pA>        having pointers to the other type?

Depends on the program.  I quite often use typedef struct to define structures
so that I may then reference them by their new type.

 pA>2.  When you decide to separate structures into header files
 pA>    a.  how big must the program be to warrant this?
 pA>    b.  do you always do so?
 pA>    c.  never?

d.  Whenever there is a reasonable chance that the same structures will be used
in two or more programs/modules.

 pA>3.  Making functions used (in many places) only by your program extern
 pA>    a.  what if only one program calls this function?

Same as above.  if the function is useful enough to be <maybe> used in more
than one program/module, it finds its way into the library.

 pA>4.  Do you consider passing a "pointer to a function" to another 
 pA>function
 pA>    which will (blindly?) use it to call that function, a "kosher"
 pA>    programming practice?

I don't know about blindly, but yes I have quite a few programs that do that.
particularly my format() module that does all the work for printf, fprintf,
lprintf, etc.  is called with the pointer to an output function, allowing one
function to perform multiple roles.

Rick.


--- msged 1.99S ZTC
 * Origin: /\/\onitor \/\/orld, Sydney 'Where you can C forever...' (3:713/616)

keithd@anvil.oz (Keith Duddy) (01/23/90)

rick_jones@f616.n713.fido.oz (Rick Jones) writes:

> pA>4.  Do you consider passing a "pointer to a function" to another 
> pA>function
> pA>    which will (blindly?) use it to call that function, a "kosher"
> pA>    programming practice?

>I don't know about blindly, but yes I have quite a few programs that do that.
>particularly my format() module that does all the work for printf, fprintf,
>lprintf, etc.  is called with the pointer to an output function, allowing one
>function to perform multiple roles.

In fact this is the way in which one implements generics in C. If you are
storing some generic data type in a module then you must pass in a function
sometimes to do comparisons/retrievals etc. As long as the interface and
intention of the function is clearly defined then there is no problem. (look
at the qsort/bsearch standard libraries).
_______________________________________________________________________
                        __                     |    keithd@anvil.oz.au
 /_/  __  o _/ /_      /  )     __/ __/        |       (07)870 4999
/ \  (-' (  ( / /  o  /_ ' (_( (_/ (_/ (__/ o  |  Anvil Designs Pty Ltd   
    Don't be stupid Niel, everybody      /     |   PO Box 954, Toowong,
      knows potatoes can't fart.      (_/      |     4066, Australia.

[Disclaimer: Anvil Designs Pty Ltd actually encourage creative thought -
      but they don't want to be involved with creative litigation.]