J
Jorgen Grahn
That's a stretch.
No; it was how I immediately interpreted it too. Who has anything
against Italy these days?
/Jorgen
That's a stretch.
Malcolm said:My pattern is to have a create and a kill. The create allocates the
structure, the kill destroys it (so C++ constructors / destructors,
in effect). Now you write the kill so that it's robust to partly created
structures. So it always does a no-op for a null. But it can't
distinguish a valid pointer from an uninitialised garbage pointer.
So you need to be careful to set all pointer members to null immediately
after allocating them.
Tim said:Geoff said:I found this code in a Quake 2 project. Would you call this a
case of premature optimization or a case of single-returnitis in
the face of a necessarily multi-return function. I am referring
to the multiple gotos in the function designed to bypass a single
line of code. [snip 74 line function definition]
I would call it atrocious code in need of re-writing. A
classic example of trying to do too much in a single function,
with hard-to-follow control flow being just one symptom.
It
seems much better to divide the original function up so there
are a couple of subfunctions, with the smaller pieces being
more easily comprehensible.
No; it was how I immediately interpreted it too. Who has anything
against Italy these days?
David said:Code that involves "goto" can almost always be better written using
multiple functions and some sort of state variables to give clearer and
more maintainable code.
You generally see such "goto" style in older code, as a way of getting
faster code. More modern compliers and more modern C standards (giving
explicit and automatic "inline") can roll structured code with multiple
functions into a single block with goto's generated in the generated code.
No; it was how I immediately interpreted it too. Who has anything
against Italy these days?
/Jorgen
for me you could be racist against italians etc
not all italians are the same, but the 90% is *not* ok for me
the way they follow is wrong for me
When you start declaring function pointer members of structuresMalcolm McLean wrote:
Just using C++ where possible would save you a lot of pain![]()
I agree.I'm starting to believe this goto phobia is a sign of incompetence,
and reflects a need to blindly and mindlessly adopt beliefs which are
believed to be held by a group in an effort to try to fit in, while showing
a complete lack of critical thinking and insight on the issue.
Malcolm said:When you start declaring function pointer members of structures
to allow for run-time resolution of bindings, it's time to consider
whether you should move to C++. But the constructor / destructor
paradigm works perfectly well in C.
That's a stretch.
The comment was clearly about italians, and how the entire nation is crassly incompetent with
regard to technical issues. It might be a joke, but it's still a racist one, and even if
someone follows the racist joke with any version of "lighten up", it's still racist.
Rui said:Tim said:Geoff said:I found this code in a Quake 2 project. Would you call this a
case of premature optimization or a case of single-returnitis in
the face of a necessarily multi-return function. I am referring
to the multiple gotos in the function designed to bypass a single
line of code. [snip 74 line function definition]
I would call it atrocious code in need of re-writing. A
classic example of trying to do too much in a single function,
with hard-to-follow control flow being just one symptom.
I looked at your alternative and saw the same thing: atrocious code in need of re-writing.
You've declared single-purpose functions which only evaluate a single logical expression,
write to a text stream and return a value, for something which only takes a couple of lines on
a function and is executed once.
what does it mean above?
Ian said:Rui said:Tim said:I found this code in a Quake 2 project. Would you call this a
case of premature optimization or a case of single-returnitis in
the face of a necessarily multi-return function. I am referring
to the multiple gotos in the function designed to bypass a single
line of code. [snip 74 line function definition]
I would call it atrocious code in need of re-writing. A
classic example of trying to do too much in a single function,
with hard-to-follow control flow being just one symptom.
I looked at your alternative and saw the same thing: atrocious code in
need of re-writing. You've declared single-purpose functions which only
evaluate a single logical expression, write to a text stream and return a
value, for something which only takes a couple of lines on a function and
is executed once.
So?
Which is easier to test, spaghetti or single-purpose functions?
Which is easier to understand and maintain, spaghetti or single-purpose
functions?
Only in a manual and therefore error prone way. The big gain from C++
is automatic object destruction which removes a whole class of
programmer errors (and the need to ever use goto!). This is also just
about the only trick in C++'s box that C can't replicate.
void TeamplaySpawnEntities(char* mapname, char* entities,
char* spawnpoint)
{FILE *f;
char szFile[MAX_QPATH];
int nEntSize, nRead;
char *pszCustomEnt;
// If teamplay is off, do the normal thing.
if(ctf->value==0)
{rexit1:
SpawnEntities (mapname, entities, spawnpoint);
rexit : return;
}
// Create the pathname to the entity file.
Com_sprintf(szFile, sizeof (szFile), "%s/ent/%s.ent",
gamedir->string, mapname);
// Try to open it.
f=fopen (szFile, "rb");
// No custom entity file, so just use the default.
if(f==0) goto rexit1;
// Get the size of the file.
if (fseek (f, 0, SEEK_END) != 0)
{gi.dprintf ("TeamplaySpawnEntities(): fseek %s\n", szFile);
errClose:
fclose (f);
goto rexit;
}
nEntSize = ftell (f);
if (nEntSize < 0)
{gi.dprintf("TeamplaySpawnEntities(): ftell %s (%d)\n",
szFile, nEntSize);
goto errClose;
}
if (fseek(f, 0, SEEK_SET) != 0)
{gi.dprintf ("TeamplaySpawnEntities(): fseek %s\n", szFile);
goto errClose;
}
// Create a buffer and read the custom entity file.
pszCustomEnt = malloc (nEntSize + 1);
if (pszCustomEnt==0)
{gi.dprintf ("TeamplaySpawnEntities(): malloc\n");
goto errClose;
}
nRead = fread(pszCustomEnt, 1, nEntSize, f);
if(nRead != nEntSize)
{gi.dprintf("TeamplaySpawnEntities(): fread %s (%d/%d)\n",
szFile, nRead, nEntSize);
errFree:
free (pszCustomEnt); goto errClose;
}
// Null-terminate the string.
pszCustomEnt[nEntSize] = '\0';
// Now spawn *these* entities!
SpawnEntities (mapname, pszCustomEnt, spawnpoint);
free (pszCustomEnt);
fclose (f);
}
but i think it would be better return one int
with something as R fclose(f);...
Geoff said:All,
In the end I took advantage of some internal knowledge of the Q2
engine and the fact it uses its own allocator and the game DLLs are
supposed to be using that allocator instead of malloc/free. Q2's
memory management posts diagnostic messages and terminates the program
in case of errors and this makes it unnecessary to actually check and
report errors when using it.
Especially helpful in putting my thoughts on the right track were
Tim's use of __func__, and Ben's note that guarding fclose(f) was a
good idea
and I quite agree with James Kuyper on the value of
single-return being desirable even without being dogmatic about it.
This is very old code and by necessity I am dealing with Microsoft's
compiler and their lack of support for newer C standards but I managed
to incorporate some of them.
To that end I attempt the following rewrite, apologizing for the line
length:
void TeamplaySpawnEntities (char *mapname, char *entities, char *spawnpoint)
{
FILE *f;
char filename[MAX_QPATH];
int nEntSize, nRead;
char *pszCustomEnt;
// Create pathname to the entity file and try to open it.
Com_sprintf (filename, MAX_QPATH, "%s/ent/%s.ent", gamedir->string, mapname);
f = fopen (filename, "rb");
// if we don't have a custom file or we're not
// configured to load one, just spawn the defaults
if (!f || (!custom_ents->value && !ctf->value))
{
SpawnEntities (mapname, entities, spawnpoint);
return;
}
// Get the size of the file.
if (fseek (f, 0, SEEK_END) != 0)
gi.dprintf ("ERROR: %s: fseek %s\n", __func__, filename);
else if ((nEntSize = ftell (f)) < 0)
gi.dprintf ("ERROR: %s: ftell %s (%d)\n", __func__, filename, nEntSize);
else if (fseek (f, 0, SEEK_SET) != 0)
gi.dprintf ("ERROR: %s: fseek %s\n", __func__, filename);
else
// allocate space for the entities we are about to read
// if gi.TagMalloc fails, it reports error and terminates the program
pszCustomEnt = gi.TagMalloc (nEntSize + 1, TAG_LEVEL);
if ((nRead = fread (pszCustomEnt, 1, nEntSize, f)) != nEntSize)
{
gi.dprintf ("ERROR: %s: fread %s (%d/%d) not all entities were loaded\n",
__func__, filename, nRead, nEntSize);
gi.TagFree (pszCustomEnt);
}
// Null-terminate the string.
pszCustomEnt[nEntSize] = '\0';
// Now spawn *these* entities!
SpawnEntities (mapname, pszCustomEnt, spawnpoint);
if (f) fclose (f);
}
I believe the guarding of fclose is unnecessary given the refactoring
and may even be a little annoying since there is only one fopen/fclose
path.
I am open to more suggestions about how to improve this code. I know
some of you will probably have something to say about bracketing the
statements for clarity or maintainability. I have tested it but have
not tested all the paths yet.
If you can throw in a few quips about my Teutonic and Saxon origins so
I can call you racist I would very much appreciate it. I know
Europeans would be the last people on Earth to cast aspersions on
someone because of their ethnicity or religion. Especially in the 21st
century.![]()
Two minor things. I suggested it as a way to permit the flow of control
to pass, always, to the lexical end of the function. You don't do that
below, which seems to have introduced a bug.
In fact I'd almost always write multiple functions to do this kind of
thing. I only didn't do so here because I wanted to make a point about
the particular linear structure you had.
There seems to be opposition to single-use functions, but I use them
quite a lot. They encapsulate the parts of a more complex task in a
particularly easy to understand way. Function that must succeed or fail
depending on various conditions can be written very clearly in terms of
function calls:
I'm starting to believe this goto phobia is a sign of incompetence, and reflects a need to
blindly and mindlessly adopt beliefs which are believed to be held by a group in an effort to
try to fit in, while showing a complete lack of critical thinking and insight on the issue.
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.