questionnaire

Matt Landau mlandau at bbn.com
Sat Jan 13 06:56:40 AEST 1990


[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 :-)



More information about the Comp.lang.c mailing list