how to organize my main file ?


P

pereges

By main file, I mean the one which contains the main routine. Can some
one please provide suggestions as to how I can improve the
organization of main file ? I have just though about a rough skeleton.

In my ray tracing project, I have to carry out following task (in
sequence)

1. Read the mesh from an ascii file and store it in the mesh data
structure.
2. Create a ray list and store it in a ray list.
3. Create the binary space partitioning tree for fast mesh traversal.
4. Trace all the rays and calculate the scattered and incident
electric fields.

I'm thinking of writing a function for every task.

#include "main.h"

static mesh *m; /* pointer to the mesh */
static bsptree *tree; /* pointer to the bsp tree */
static ray *raylist; /* pointer to the ray list */

/* function prototypes */

int read_mesh(char *);
int init_plane_wave(void);
int create_bsp_tree(void);
int calc_e_fields (void);


/* Provide the name of the ascii file(from which mesh is to be read)
as a command line argument eg. main sphere.dat */

int main(int argc char *argv[])
{
if(argc < 2)
{
fprintf(stderr, "Insufficient argumens\n");
return -1;
}

if(argc > 2)
{
fprintf(stderr, "Too many arguments\n");
return -1;
}

if(read_mesh(argv[1])
return -1;
if(init_plane_wave())
return -1;
if(create_bsp_tree())
return -1;
if(calc_e_fields())
return -1;

return 0;
}

/* I decided to make the above data structures as static global
because they are needed throughout the program */

int read_mesh(char *filename)
{
FILE *fp;

fp = fopen(filename, "r");
if(fp == NULL)
{
fprintf(stderr, "Error while opening the file %s\n", filename);
return -1;
}
m = malloc(sizeof *m);
if(m == NULL)
{
fprintf(stderr, "Couldn't allocate memory for the mesh\n");
return -1;
}
/* parse_dat_file returns -1 if error occured while parsing file */
if(parse_dat_file(fp, &m))
return -1;
}

/* This function will initiailize the plane as in read the
specification related to a plane wave like frequency, electric field
at reference point, direction of the plane wave etc. It will allocate
memory for the ray list. After this it will call init_rays which
initializes a set of parallel rays. A plane wave is being simulated by
a dense grid of parallel rays */

int init_plane_wave(void)
{
...
...
}

/* This function will read the maximum allowable depth for the tree ,
allocate memory for it*/

int create_bsp_tree(void)
{

}

/* This function will call the raytrace function and after that it
will perform some calculations to find out the scattered and incident
electric fields */

int calc_e_fields (void)
{

}
 
Ad

Advertisements

P

pereges

I have another question: Is it ok to write a function to destroy all
the objects(Its not just a simple 'free' call btw, there are lists
within objects which must be destroyed first)
 
W

Walter Roberson

pereges said:
I have another question: Is it ok to write a function to destroy all
the objects(Its not just a simple 'free' call btw, there are lists
within objects which must be destroyed first)

Sure, why not? As long as the objects are dynamically allocated, that is.
 
P

pereges

It seems to me that tasks 1-3 might reasonably be called initialisation.
Task 4 appears to be the bit that does the useful processing.

A function for every task is always a good idea, but why not abstract your
first three tasks into a function called initialise() or something like
that. It might look something like this:

#include "whatever.h"

int initialise(const char *infile)
{
int rc = read_mesh(infile); /* change read_mesh() to take const char *
*/
if(0 == rc)
{
rc = init_plane_wave();
}
if(0 == rc)
{
rc = create_bsp_tree();
}
return rc;

}

and then main would look something like this:

#include <stdio.h>
#include <stdlib.h>
#include "whatever.h"

int main(int argc, char **argv)
{
int result = EXIT_FAILURE;

if(argc != 2)
{
printf("%s arguments\n", argc < 2 ? "Insufficient" : "Too many");
}
else
{
if(0 == initialise(argv[1]))
{
if(0 == calc_e_fields())
{
result = EXIT_SUCCESS;
}
}
}
return result;

}

You might want to put main() and initialise() in one source file (your
"main file" as you call it), the initialisation routines (read_mesh and
the other two) in, say, initialise.c, and the calcs in calcs.c - this will
help to keep each source file down to a manageable size and make things
easier for you to find.

Thanks, I think this is a very good idea but I think the calcs.c file
will not span more than 30-40 lines. But I think it is better this way
because later on if there is a requirement to calculate some other
things(eg. surface currents, magnetic fields , whatever etc), then all
that code can go into this file.
Note that a -1 return value from main isn't guaranteed to be meaningful,
whereas EXIT_FAILURE is.

Why ?
I have seen that everytime a C program fails, the process returns some
non zero value(Pelles C compiler reports this). So what is wrong in
returning -1 ? Is this only applicable to main function or others as
well ? I usually use

#define SUCCESS 0
#define FAILURE -1

as return values for functions other than main.
 
S

santosh

pereges said:

Because the C standard says so.
I have seen that everytime a C program fails, the process returns some
non zero value(Pelles C compiler reports this).

Perhaps, but what a C program returns to the host system after it has
finished (or to be more precise, what value your system indicates that
the program has returned) is not specified by the standard, and
therefore your observations may not be valid for one or more systems.

What the standard does specify is what value you can portably use as an
argument to the exit function or as an expression with the return
keyword. These are 0 or EXIT_SUCCESS to indicate that the program has
terminated normally or EXIT_FAILURE to indicate that the program has
terminated abnormally. Note that the value that the host system may
report to you as the termination status code of your program need not
match the value that you supplied to exit or return. The C runtime code
can perform translations on this value to ensure compatibility with the
protocols of the host environment.

IOW, there could exit a system where -1 indicates successful
termination. This would render your code broken. On the other hand if
you use EXIT_FAILURE in the place of -1 your program would work
correctly on all systems which are fully conforming to the C standard.
So what is wrong in
returning -1 ? Is this only applicable to main function or others as
well ?
Yes.

I usually use

#define SUCCESS 0
#define FAILURE -1

as return values for functions other than main.

In C zero is taken as boolean false and any other value is taken as
boolean true. Your macros above conflict against this, but in this
respect, so does a lot of C code including portions of the standard
library. Also the convention for functions is that a return of zero
indicates successful completion and a non-zero return indicates
unsuccessful completion.

I would much rather use C99's bool type rather than these macros, as
they are much abused and will merely confuse anyone reading your code.
 
K

Kenny McCormack

It's not really 'wrong', it's simply not standard (thus
not portable). The only return values for main() which

In this newsgroup, "wrong" and "not standard" are synonymous.
See also "morally bankrupt".
 
Ad

Advertisements

P

pereges

So, EXIT_FAILURE and EXIT_SUCCESS can be used in any function other
than main. Mostly I have seen that they are not used outside main.
 
S

santosh

pereges said:
I have another question: Is it ok to write a function to destroy all
the objects(Its not just a simple 'free' call btw, there are lists
within objects which must be destroyed first)

I assume you will be calling such a function either just before program
termination or after a major task has been completed. As long as it
does not free memory still in use, or try to free nonexistent memory,
it should be functionally okay. Whether such a strategy will suit your
application depends on many other factors.
 
P

pereges

I assume you will be calling such a function either just before program
termination or after a major task has been completed.

just before the termination. The last function to be called.
As long as it
does not free memory still in use, or try to free nonexistent memory,
it should be functionally okay. Whether such a strategy will suit your
application depends on many other factors.

like for eg. ? mine is a pretty straight forward numerical computation/
simulation program. I was also wondering what is the point in freeing
the dynamic memory just before the program terminates because the
objects will be destroyed once the program terminates anyways.
 
S

santosh

pereges said:
So, EXIT_FAILURE and EXIT_SUCCESS can be used in any function other
than main.

You can do so, as long as you are internally self consistent, but this
is rare. It is more common to return either zero or one. Which of these
is used to signal success and which one failure really depends on local
convention. All that matters is consistency.

For example you could have a function return the int value 1 for success
and zero for failure. Then you might test a function like this:

if (!do_foo()) {
/* handle error */
}

The code for the reverse situation might be like:

if (do_foo()) {
/* handle error */
}

Note that this method allows for multiple error codes, which is usually
convenient.

It's common (and more robust) to define an enumeration for default
success and failure and typedef the enum. You can also use #define
macros as you did above.

You might also want to use C99's bool type.

For functions returning pointer values, usually a return of NULL
indicates failure.
Mostly I have seen that they are not used outside main.

No. It's customary for most non-trivial applications to have their own
error handling conventions. It's more work up-front, but pays off as
the complexity of the program grows.
 
S

santosh

pereges wrote:

[ ... ]
I was also wondering what is the point in freeing
the dynamic memory just before the program terminates because the
objects will be destroyed once the program terminates anyways.

It will reduce the number of false positives that memory checkers like
Valgrind will report. It's also better form, even though it may not be
necessary under modern operating systems.
 
Ad

Advertisements

P

pereges

I assume, you have no need for sophisticated error handling, then there
is no need to propagate error codes all over the place, just log error
and die.

I do need a good error handling mechanism. Right now, I just check for
error condition and use fprintf to report the messages to stderr. It
tends to get a little verbose sometimes.
So, this would be my initial main:

#include <stdio.h>
#include "ray_track.h"

int main(int argc, char *argv[])
{
struct ray_track rt;

ray_open( &rt, argc, argv);
ray_fload_mesh( &rt, fname_mesh);
ray_create_raylist (&rt);
ray_create_bsp_three(&rt);
ray_calc_scattering (&rt);
ray_close(&rt);

return EXIT_SUCCESS;

}

The header file would look something like:

#ifndef RAY_TRACK_H
#define RAY_TRACK_H

struct ray_track
{
const char *fname_mesh;

struct mesh *mesh;
struct ray *raylist;
struct bsp_three *bsp_three;

/*! logging & error handlers */
void (*trace) (const char *msg);
void (*err_warn ) (int err, int line, const char *msg);
void (*err_fatal) (int err, int line, const char *msg);

};

/*! Ray track function prototypes */
void ray_open (struct ray_track *, int argc, char **argv);
void ray_create_mesh (struct ray_track *, const char *fname_mesh);
void ray_create_raylist (struct ray_track *);
void ray_create_bsp_three (struct ray_track *);
void ray_calc_scattering (struct ray_track *);
void ray_close (struct ray_track *);

#endif /* !RAY_TRACK_H*/

Can you please given an example of these three functions ? what are
they used for ? :

void (*trace) (const char *msg);
void (*err_warn ) (int err, int line, const char *msg);
void (*err_fatal) (int err, int line, const char *msg);

How to differentiate between a fatal error and a warning ?
 
R

rahul

Can you please given an example of these three functions ? what are
they used for ? :

void (*trace) (const char *msg);
void (*err_warn ) (int err, int line, const char *msg);
void (*err_fatal) (int err, int line, const char *msg);

How to differentiate between a fatal error and a warning ?
Where did you get these functions? These functions are not there in
the Standard C Library.
<off-topic>
Richard Stevens have used similar functions in his Unix Network
Programming Books. May be you got these functions from there. When you
issue a warning, a diagnostic is sent to stderr or logged and the
process continues. In case of fatal errors, you display/log an error
message and the process terminates.
</off-topic>
 
S

santosh

pereges wrote:

Can you please given an example of these three functions ? what are
they used for ? :

void (*trace) (const char *msg);

I guess that this is used to print a backtrace, preceded by the
customised message.
void (*err_warn ) (int err, int line, const char *msg);

This is to print a warning - the line where it occured, and the text
pointed by 'msg', with perhaps other information. You might call it
like this:

err_warn(errno, __LINE__, "Custom message.");
void (*err_fatal) (int err, int line, const char *msg);

Same as above except that it should terminate the program (or call a
function that does so) after printing the diagnostics.
How to differentiate between a fatal error and a warning ?

That's context specific. Generally failure to obtain critical resources
like files and memory are fatal, while things like network packet loss,
or failure of a non-essential component could just be regarded as
insufficient to terminate the program. In general any exception after
which you can still proceed with all or most of the functionality of
the program intact can be just a "warning" while a fatal error is one
that forces you to stop.
 
H

Herbert Rosenau


That is because the standard requests it.
I have seen that everytime a C program fails, the process returns some
non zero value(Pelles C compiler reports this). So what is wrong in
returning -1 ? Is this only applicable to main function or others as
well ? I usually use

#define SUCCESS 0
#define FAILURE -1

as return values for functions other than main.

main() is the interface between the system that is calling your
program. So the standard tries to give you and any system that is able
to run programs written in C rules for the interface, that is
1. how to give parameters to the program at startup
2. values allowed to return to the system

There are systems on the maret who may fail miserably when main() or
exit() returns other values than 0 or EXIT_FAIlTURE.

I know of some systems that gets confused when main() or exit() tries
to return an int outside the range 0-255, even as they accept any
value inside that value, extending the C standard with system
dependant wider range. But the system will go into undefinded behavior
by receiving a value outside 0-255 in from main()/exit().

On other hand it is complete on you to define each and any value a
function you've ritten can return. So the function can return 42 for
success and 0 for failture or a pointer to a a data type its prototype
defines or something else you means it is the ideal value.

So there are only specified values for the external interface:

The name is main. It returns int with only 2 allowed values and owns 2
different groups of parameters where
group 1 defines zero parameters (VOID)
group 2 defines exactly 2 parameters in defined sequence
first parameter: int argc - the number of parameters given
second parameter: char **argv - an array of pointers to
strings
wheras argv[0] is either NULL or the name of the progrsm
and argv[argc] is always NULL
The names of the parameter are commonly used but not really
defined.
--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
S

santosh

Herbert Rosenau wrote:

main() is the interface between the system that is calling your
program. So the standard tries to give you and any system that is able
to run programs written in C rules for the interface, that is
1. how to give parameters to the program at startup
2. values allowed to return to the system [ ... ]
The name is main. It returns int with only 2 allowed values [ ... ]

Actually three viz. 0, EXIT_SUCCESS and EXIT_FAILURE.

<snip>
 
Ad

Advertisements

P

pereges

I'm thinking of declare m, tree, raylist as extern variables in
common.h. Now, I have heard a lot of arguments against extern but what
if its done *properly* ?Well these data structures are frequently
needed in many functions, and continously passing them everywhere is
extermely painful even when they are hardly used in a function. It has
made my code extermely obfuscated, difficult to read, too many
arguments in a function and probably even slower in cases where
recursive functions are used. Let's say mesh *m is passed to a
recursive function. Then in that case, for all the calls a seperate
copy of m will be maintained even if m was utilized in probably 1
statement. Apart from this during the error handling, it is quite easy
to call the killall function from any where in the program where the
error occured and free the data structures allocated upto that point.
No need to pass any parameters.

eg :

void killall()
{
if(m != NULL)
killlmesh();
if(tree != NULL)
killtree();
if(raylist != NULL)
killraylist();
}
 
Ad

Advertisements

V

vippstar

That is because the standard requests it.




main() is the interface between the system that is calling your
program. So the standard tries to give you and any system that is able
to run programs written in C rules for the interface, that is
1. how to give parameters to the program at startup
2. values allowed to return to the system

There are systems on the maret who may fail miserably when main() or
exit() returns other values than 0 or EXIT_FAIlTURE.
And EXIT_SUCCESS. Actually I don't think they will fail miserably. I
believe the standard says that simply it's not guaranteed that a
meaningful value will be returned to the caller.
 

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

Top