Assorted questions from a C++ programmer

V

V.Ch.

In near future I can face a prospect of writing some stuff in C. Being a
C++ programmer, I've got practically no experience in C. I'd be obliged
if someone could answer the following questions (from specific to more
general):

1. Having looked though some C sources I was horrified by the number of
macros. E.g., what could be the purpose of using this:

#define CFG_REF(c, f) ((c) -> f)
#define SERVER_ADDRESS(c) CFG_REF (c, server_address)


From the point of view of C++ coding, this would seem quite idiotic
(why not use -> operator directly?!). But I've got reasons to believe
that the guy who wrote this is a good C coder.

2. If you were to write some program that can potentially be compiled on
a number of platforms by different compilers, how would you declare
automatic variables? Does it pay of to be extra conservative and don't
assume that every compiler now supports C99 with respect to declaring
variables wherever you want? Personally, I find it not only tiresome,
but actually very bad to declare all the variables at the beginning of a
block. But then again, I am not a C programmer. Is there any consensus
here?

3. What are the ways to structure large programs? I am falling back to
C++ again, thinking about object-oriented programming in C (something
like fopen, fread/fwrite and fclose) and emulating VTBL interfaces with
vectors of function pointers. Any other aproaches here? Any
links/tutorials would be very welcome.

4. Any other links and suggestions that would be useful for a C++
programmer that has to write in C.
 
W

Walter Roberson

I'd be obliged
if someone could answer the following questions (from specific to more
general):
1. Having looked though some C sources I was horrified by the number of
macros. E.g., what could be the purpose of using this:
#define CFG_REF(c, f) ((c) -> f)
#define SERVER_ADDRESS(c) CFG_REF (c, server_address)

Hiding of implementation. The data structure could be completely
replaced, possibly by functions, with very little change to the code.

2. If you were to write some program that can potentially be compiled on
a number of platforms by different compilers, how would you declare
automatic variables? Does it pay of to be extra conservative and don't
assume that every compiler now supports C99 with respect to declaring
variables wherever you want?

Yes, it does pay. Actual C99 implementations are rare -- at least
ones that support the full library. Without the full library, it is
in effect "C89 plus extensions and some bugs", and as soon as you
start down the "extensions" path you are sure to lose portability.

3. What are the ways to structure large programs?

Many different ways, depending on what you are trying to achieve
and what the natural problem decomposition is.

I've even heard of people who believe that every source file should
have at most one function... but that requires exposing private data
structures.
I am falling back to
C++ again, thinking about object-oriented programming in C (something
like fopen, fread/fwrite and fclose) and emulating VTBL interfaces with
vectors of function pointers. Any other aproaches here? Any
links/tutorials would be very welcome.

What domain of problem are you trying to deal with? Unless you
have the same kind of operations being performed on a number of
ideologically similar but not identicially-structured objects, the VTBL
approach is probably a waste.

Above, was the reference to fopen etc. along the lines that you were
planning to do the equivilent of smalltalk's [self print]
in which arbitrary objects might get juggled around but you need to
be able to tell them to pretty-print themselves? Or are you working
with something like a graphics package where you want to be able
manipulate a number of different kinds of objects, and you want it
to be extensible without changing the draw code, just by implementing
new objects? [If so, then one approach that can work is to
have each object type call "registration" routines, with
the function pointers kept track of (one per -type- per functionality).
The routine that needs to invoke variable behaviour then searches
its registry table by type number and calls the associated function
pointer on the object.
 
V

V.Ch.

Walter said:
Yes, it does pay. Actual C99 implementations are rare -- at least
ones that support the full library.

I am not interested much in anything else than variable declaration
part. Are you saying that I cannot rely even on that?
What domain of problem are you trying to deal with?

Well, for now I am not going to touch anything big - just an Apache
module. I don't think it will need any king of structure :). The
question about structuring a big program was less practical (at least
for now), I asked it mainly out of quriosity.
Above, was the reference to fopen etc. along the lines that you were
planning to do the equivilent of smalltalk's [self print]
in which arbitrary objects might get juggled around but you need to
be able to tell them to pretty-print themselves?

Don't know anything about smalltalk. What I meant was quite simple
actually, that instead of a C++ class I would create a C module in which I:

1. Declare a struct to hold state variables of an "object" (FILE)
2. "Constructor" - function that creates an object and returns a
"handle" (fopen).
3. "Methods" - functions that take this "handle" as a parameter
(fread/fwrite).
4. "Destructor" (fclose)
 
E

Eric Sosman

V.Ch. said:
In near future I can face a prospect of writing some stuff in C. Being a
C++ programmer, I've got practically no experience in C. I'd be obliged
if someone could answer the following questions (from specific to more
general):

First, read Walter Roberson's response; I'm just tossing
in a few minor additions.
2. If you were to write some program that can potentially be compiled on
a number of platforms by different compilers, how would you declare
automatic variables? Does it pay of to be extra conservative and don't
assume that every compiler now supports C99 with respect to declaring
variables wherever you want? Personally, I find it not only tiresome,
but actually very bad to declare all the variables at the beginning of a
block. But then again, I am not a C programmer. Is there any consensus
here?

In C90 a block's variable declarations must precede all
its executable statements. However, blocks can nest to give
you a partial simulation of what you're looking for:

int func(int arg) {
int a; /* "function global" */
...
{
double x; /* "block local" */
...
}
...
{
char x[100]; /* nothing to do with prior `x' */
...
}
...
return 42;
}

You can even recycle a "current" identifier in a nested block,
but I'd avoid it -- can you spell "maintenance nightmare?"

Whether it's burdensome to park the variable declarations
at the start of a function depends on how many variables there
are. If you've got a Whole Lot of local variables and they
don't belong to a few tightly-related groups, it may be time
to think about splitting up the function. Small functions
improve manageability, and at the same time they tend to keep
the population of local variables under control.
3. What are the ways to structure large programs? I am falling back to
C++ again, thinking about object-oriented programming in C (something
like fopen, fread/fwrite and fclose) and emulating VTBL interfaces with
vectors of function pointers. Any other aproaches here? Any
links/tutorials would be very welcome.

If you want to write C++ programs, write them in C++ and
don't try to "port your language" to C. A programmer who
insists on writing "C-ish" code for a C++ environment is
usually making a mistake; the reverse is equally mistaken.
Seek C-ish solutions; don't try to bend C into a poor man's
version of C++. You'll wind up with something that's neither
fish nor fowl, a feathered fish that neither swims nor flies.

"A Real Programmer can write FORTRAN in any language."
 
K

Keith Thompson

V.Ch. said:
I am not interested much in anything else than variable declaration
part. Are you saying that I cannot rely even on that?

Right, you can't rely on being able to mix declarations and statements
if you want your C to be completely portable. It's likely to work on
a number of systems, but you could later need to port the code to a
system that doesn't have a C compiler that supports this C99 feature.

You can, of course, reduce the scope of a variable by creating a
block:

void foo(void)
{
int x;
statement;
statement;
{
int y;
statement;
statement;
}
}

If this causes the nesting level to become uncomfortably deep, you can
either declare everything at the top of the function or consider that
your function might be too big.

On the other hand, if you're not concerned about absolute portability,
you might be able to get away with mixing declarations and statements.
For example, if you can count on every system your code might run on
to have a sufficiently recent version of gcc, you can use gcc
extensions. (Note that gcc extensions, other than the ones that are
part of C99, are off-topic here.)
 
W

Walter Roberson

I am not interested much in anything else than variable declaration
part. Are you saying that I cannot rely even on that?

For clarity: are you refering to the ability to declare variables
where convenient, or are you refering to variable-length arrays?

There are still quite a number of compilers that are C89 without
any ability to declare variables in convenient places. Variable-length
arrays are less common.

You could restrict yourself to gcc4, which I believe supports both
features. Generally speaking, you really have to be disciplined when
you program in gcc, as they tend to throw in extensions without
making it clear that they are extensions.

Historically, gcc has had issues with silently accepting non-portable
code even when warnings were turned on: their excuse has been along the
lines of, "The ANSI standards only bind compilers that claim compliance
with the standard. Since we don't claim compliance with the standard,
we are free to ignore any part of the standard we want, and free to
implement extensions without warning that they are extensions."
What I meant was quite simple
actually, that instead of a C++ class I would create a C module in which I:
1. Declare a struct to hold state variables of an "object" (FILE)
2. "Constructor" - function that creates an object and returns a
"handle" (fopen).
3. "Methods" - functions that take this "handle" as a parameter
(fread/fwrite).
4. "Destructor" (fclose)

Apache (the original) at least [don't know about Apache2] wasn't
written in that kind of style, so it might be a bit much to expect
other people to adopt that style to use your module.

IMHO, there isn't a lot of point in adopting a class-like structure
for those functions, not unless you plan to write a -number- of different
apache modules and want them all to have the same interface.
Just do what everyone else does and use routine names that are
module specific. The users will call the routine with the right name
and you won't have to keep function pointers around.

Sometimes it is useful to have a data structure with important state
information: I'm not discouraging that, I'm just indicating that
-likely- in the context you describe, there would not be much point
in storing function pointers, as they would probably always be the -same-
function pointer.
 
C

CBFalconer

V.Ch. said:
In near future I can face a prospect of writing some stuff in C.
Being a C++ programmer, I've got practically no experience in C.
I'd be obliged if someone could answer the following questions
(from specific to more general):
.... snip ...

3. What are the ways to structure large programs? I am falling
back to C++ again, thinking about object-oriented programming
in C (something like fopen, fread/fwrite and fclose) and
emulating VTBL interfaces with vectors of function pointers.
Any other aproaches here? Any links/tutorials would be very
welcome.

I suggest you take a look at the structure and interface of my
hashlib package, for what I (for some reason) consider good C
code. You probably need read no more than the .h and .c files.
They are to be found in:

<http://cbfalconer.home.att.net/download/hashlib.zip>
 
M

Malcolm

Eric Sosman said:
A programmer who
insists on writing "C-ish" code for a C++ environment is
usually making a mistake; the reverse is equally mistaken.
You certainly often see C++ programs which are horrible kludges, and would
have been much better in C.

However I would disagree on "C++ish" C.
In C it is often a good idea to have objects, with contructors and matching
destructors. So if I want a logical regression tree, I declare a structure,
then I declare a function called "logictree()" which creates one, and a
function called "killlogictree()" which destroys it when I am finished. Then
I have a function called ltree_predict() which takes a tree and some
observations as parameters, and predicts the class.

What C is not good at is allowing objects to enter into relationships with
each other. If I decide I want a general class of "logic trees", and a
"nand-only" tree as a special case and a "balanced logic tree" as another
special case, I cannot easily represent that program design using C, and I
am better off with another language.
 
E

Eric Sosman

Malcolm said:
Eric Sosman said:
A programmer who
insists on writing "C-ish" code for a C++ environment is
usually making a mistake; the reverse is equally mistaken.

You certainly often see C++ programs which are horrible kludges, and would
have been much better in C.

However I would disagree on "C++ish" C.
In C it is often a good idea to have objects, with contructors and matching
destructors. [...]

Object-oriented design is a fine thing (in some problem
areas, anyhow), and I have no quarrel with it -- use it m'self
on occasion, ayeh, that I dew.

But the O.P. was talking about "emulating VTBL interfaces
with vectors of function pointers." This is an attempt to graft
the implementation techniques of one language into the body of
another, whose immune systems are likely to kick up all kinds of
ruckus. If he wants VTBL-ish implementation artifacts, I still
maintain he's better off seeking them in C++ than trying to emulate
them in C. One might as well translate C to Lisp by writing a
defun() for malloc() ... Again I repeat the Immortal Words:

"A Real Programmer can write FORTRAN in any language."

Don't spread jam with a meat cleaver; don't quarter a
chicken with a butterknife. Both tasks are possible; neither
would be pleasant.
 

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

Members online

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top