problem with macros

K

Keith Thompson

Malcolm McLean said:
Richard Heathfield said:
Your question has already been answered, but I have a suggestion for you:

#define X 0
#define Y 1
#define Z 2

If your physicist can't understand coord[X] to mean the X coordinate, then
he's probably not much of a physicist.
It's not a bad idea. The problem is that a less clever person, lets
say a C programmer, might look at that and say "Hummm, X is some sort
of variable that iterates over a coordinate set. But I don't see it in
scope. What on Earth is going on here?"

A C programmer is more likely to assume, correctly, that X is likely
to be a macro because its name is in all-caps.
 
A

Antoninus Twink

Antoninus Twink said:
typedef struct
{
double *coord[3];
double x;
double y;
double z;
} vector;

void vector_initialise(vector *p)
{
p->coord[0] = &p->x;
p->coord[1] = &p->y;
p->coord[2] = &p->z;
}

The OP explicitly says his main aim is to avoid triplication, so
Heathfield proposes a maintenance nightmare that involves keeping
triplicate copies of data in sync at all times. Unbelievable.

Let's assume this is so (I'm not saying it is, just to assume so)...

Oops, yes, good spot - I'd scanned Heathfield's code too quickly. So in
fact you only need to set it up once (which would be fine in a language
with ctors like C++ but is annoying in C), but then you need an ugly
extra level of indirection every time you want to access it as an array.
Still yuck.
 
A

Antoninus Twink

A C programmer is more likely to assume, correctly, that X is likely
to be a macro because its name is in all-caps.

That would be true if the name was more than one character long.
 
B

Barry Schwarz

Antoninus Twink said:
typedef struct
{
double *coord[3];
double x;
double y;
double z;
} vector;

although this does involve some setup for every vector used:

void vector_initialise(vector *p)
{
p->coord[0] = &p->x;
p->coord[1] = &p->y;
p->coord[2] = &p->z;
}

Wonderful!

Time and again Heathfield has shown us that he has a casual disregard
for gross inefficiencies in his programming, but this takes the biscuit.

The OP explicitly says his main aim is to avoid triplication, so
Heathfield proposes a maintenance nightmare that involves keeping
triplicate copies of data in sync at all times. Unbelievable.

Once again, the solution is very simple: just cast vector* to double*.

For this to work the fields x,y,z need to be stored in memory in the same
way as three consecutive array elements.

Could you please expand. As near as I can see, the approach in
question does not care even if there were unrelated members
interspersed between the doubles or the doubles were defined in a
different order.
 
B

Barry Schwarz

pereges said:
Hello, I have the following structure -

typedef struct
{
double x, y, z;

}vector;

In certain places, I could avoid triplification of code by using an
array instead of x, y, z. For eg:

typedef struct
{
double coord[3];

} vector;

I tried this:

typedef struct
{
union {
struct {double x, y, z;};
double coord[3];
};

}vector;

This allows you to access the vector as .x .y .z or as .coord[0] .coord[1]
.coord[2]. No macros needed.

However you need a compiler that allows anonymous unions and structs.

And one that promises not to insert padding. coord[0] is guaranteed
to overlay x but the same is not true for the remaining members.
 
P

pereges

Agree with you about the switch case. I also think it will be better
to return the address of the coordinate rather than its value. Here's
a small program that I tried with this approach:

#include <stdio.h>

typedef struct
{
double x, y, z;

}vector;

double *vector_iterator(vector *v, int axis)
{
switch (axis)
{
case 0: return (&v->x);
case 1: return (&v->y);
case 2: return (&v->z);
}

}

int main(void)
{
vector v;

v.x = 5;
v.y = 3;
v.z = 2;

*(vector_iterator(&v, 0)) = 3; /* Note this */
printf("%f", v.x);
return 0;

}

This is fine with my compiler (Output is 3.000000), but I'm unsure
about function call on the left side of = operator (lvalue ?)

By the way, with this code I get a warning :

$ gcc -W -Wall -ansi -pedantic -O2 foo.c
foo.c: In function `vector_iterator':
foo.c:18: warning: control reaches end of non-void function

I actually got this warning in many other programs when there was no
return value specified at the end of the function. I could return NULL
at the end of course(and then use assert before you store value at the
address), but the control will never reach that stage unless the
precondition within the function are not satisfied. I don't know if I
should absolutely get rid of this warning.
 
S

santosh

pereges said:
By the way, with this code I get a warning :

$ gcc -W -Wall -ansi -pedantic -O2 foo.c
foo.c: In function `vector_iterator':
foo.c:18: warning: control reaches end of non-void function

I actually got this warning in many other programs when there was no
return value specified at the end of the function.

When control reaches the terminating brace of a non-void function then
an indeterminate value (i.e., garbage) is returned to the caller. You
should always return a sensible value for all non-void functions. If
you do not want to return any value from a function just declare that
function as returning void.

<snip>

For the specific function you might want to include a default case for
invalid parameter values. This could return NULL.
 
B

Bartc

Richard Heathfield said:
Greg Comeau said:
On 1 Aug 2008 at 22:15, Richard Heathfield wrote:
typedef struct
{
double *coord[3];
double x;
double y;
double z;
} vector;

although this does involve some setup for every vector used:

void vector_initialise(vector *p)
{
p->coord[0] = &p->x;
p->coord[1] = &p->y;
p->coord[2] = &p->z;
}

Wonderful!

Time and again Heathfield has shown us that he has a casual disregard
for gross inefficiencies in his programming, but this takes the biscuit.

The OP explicitly says his main aim is to avoid triplication, so
Heathfield proposes a maintenance nightmare that involves keeping
triplicate copies of data in sync at all times. Unbelievable.

Let's assume this is so

No, let's not, because he's wrong.
(I'm not saying it is, just to assume so)...

I understand why it is sometimes useful to make questionable assumptions
(for example, it's a sine qua non in an r.a.a. proof), but I see no value
in it here. He's simply wrong. There is no maintenance nightmare, just a
small set-up cost. There is no need to keep triplicate copies of data in
sync at all times, since my proposed method doesn't make any copies of the
data whatsoever.

As I understand it, you're adding 3 pointers to each struct containing x,y,z
doubles, and the pointers are set up to point to each of the x,y,z fields.

Apart the inconvenience and extra space and extra dereferencing, I can see
this causing problems when such a struct is passed by value to a function,
or copied by assignment to another struct: the pointers will point to the
fields in the original struct, not the copy.
 
B

Bartc

Barry Schwarz said:
Antoninus Twink said:
On 1 Aug 2008 at 22:15, Richard Heathfield wrote:
typedef struct
{
double *coord[3];
double x;
double y;
double z;
} vector;
Once again, the solution is very simple: just cast vector* to double*.

For this to work the fields x,y,z need to be stored in memory in the same
way as three consecutive array elements.

Could you please expand. As near as I can see, the approach in
question does not care even if there were unrelated members
interspersed between the doubles or the doubles were defined in a
different order.

By 'this' in:

I meant this:

not RH's suggestion.
 
K

Keith Thompson

pereges said:
#include <stdio.h>

typedef struct
{
double x, y, z;

}vector;

double *vector_iterator(vector *v, int axis)
{
switch (axis)
{
case 0: return (&v->x);
case 1: return (&v->y);
case 2: return (&v->z);
}

}
[...]
By the way, with this code I get a warning :

$ gcc -W -Wall -ansi -pedantic -O2 foo.c
foo.c: In function `vector_iterator':
foo.c:18: warning: control reaches end of non-void function

I actually got this warning in many other programs when there was no
return value specified at the end of the function. I could return NULL
at the end of course(and then use assert before you store value at the
address), but the control will never reach that stage unless the
precondition within the function are not satisfied. I don't know if I
should absolutely get rid of this warning.

You can get rid of the warning by adding a default case:

...
default: return NULL;

You're getting the warning because the compiler has no way of knowing
that the value of axis will always be in the range [0, 2].

Incidentally, this might be a good place for an assert(), something
like:

double *vector_iterator(vector *v, int axis)
{
assert(axis >= 0 && axis <= 2);
switch (axis) {
case 0: return &v->x;
case 1: return &v->y;
case 2: return &v->z;
default: return NULL;
}
}

The assumption is that an axis value outside that range is a
programming error, not a data error. If there's a possibility of the
axis value being derived from user or file input, you should handle it
as a data error, not using assert().
 
C

Chris Torek

[I did some vertical compression below just for quote-shortening purposes]
... usually [the code duplication] happens when you want something
to be done with only one coordinate of vector and not all. For eg.
in my kdtree building module, I have to find the median of the
vertices(vectors) along x, y or z coordinates based on the value
of a flag called splitplane. This splitplane may take values 0, 1,
2 (0 means split along x, 1 means split along y, 2 means split
along z). If this notation was used -

typedef struct { double coord[3]; } vector;

... I simply need to specify v.coord[splitplane] ... whereas if this
notation is followed -

typedef struct { double x, y, z; } vector;

I have to do something like below :

if (splitplane == 0) { /* Find median along x */ }
if (splitplane == 1) { /* Find median along y */ }
if (splitplane == 2) { /* Find median along z */ }

This is what I mean by "triplification". Here, the logic behind
finding the median is same but it will be repeated thrice.

Elsethread, others have suggested various approaches. Here is one
more that is a variant of the pointer-to-element version. It is
not very pretty, but -- modulo errors (this code is untested) --
is guaranteed to work, and *may* have a lower runtime cost than
calling a function to find &vecp->x, &vecp->y, or &vecp->z.

[begin contents of vector.h]

/* Vector type, including its element-type. */
typedef double Vector_elt_type;
/*
* optionally, you might also includ
* typedef struct vector Vector_type;
* but I prefer to use the word "struct" for a type-name, except
* in cases where it is guaranteed to be an alias for a scalar
* type (as with Vector_elt_type, which is one of float, double,
* or long double).
*
* (Think of the "struct" keyword as a funny way to spell the
* word "type".)
*/
struct vector {
Vector_elt_type x, y, z;
};

/* Vector "plane selector" numbers. */
enum { VECTOR_X = 0, VECTOR_Y = 1, VECTOR_Z = 2 };

/*
* Note that these are byte offsets, so that they will work
* even if the "vector" struct is augmented with additional
* fields before, between, and/or after the three elements
* we care about, and regardless of any padding a compiler might
* use.
*/
extern const size_t vector_field_offsets[];

/*
* Given a pointer to a vector, pick the X, Y, or Z element based
* on the plane number, which must be one of VECTOR_X, VECTOR_Y,
* or VECTOR_Z.
*/
#define VECTOR_TO_ELT(vecp, plane) \
((Vector_elt_type *)((char *)(vecp) + vector_field_offsets[plane]))

[end of vector.h extract -- there might be more in there, e.g.,
prototypes for functions in vector.c]

[begin contents of vector.c]

#include "vector.h"

/*
* The initializers here assume that VECTOR_X is 0,
* VECTOR_Y is 1, and VECTOR_Z is 2. We could check this
* with a COMPILE_TIME_ASSERT macro (google for it), but
* here I do not bother.
*/
const size_t vector_field_offsets[] = {
offsetof(struct vector, x),
offsetof(struct vector, y),
offsetof(struct vector, z),
};

[end contents of vector.c, though in practice there would be more
code]

[begin contents of some vector-using C code]

#include <assert.h>
#include <stddef.h>
#include "vector.h"

/* some sort of median finding operation: */
void median_op(struct vector *vector_set, size_t nvectors, int plane) {
size_t i;
Vector_elt_type *p;
... other variables as needed ...

/*
* It is a programmer error to call this function with
* a plane that is not one of the three specified vector
* plane numbers.
*/
assert(plane == VECTOR_X || plane == VECTOR_Y || plane == VECTOR_Z);

for (i = 0; i < nvectors; i++) {
p = VECTOR_TO_ELT(&vector_set, plane);
... work with *p as needed to find median ...
}
}
 
P

Peter Nilsson

pereges said:
... this notation does not [suit?] some people
who are going to read my code (physics people).
They are used to x, y, z notation for vectors.

Physicists understand vectors as n-tuples perfectly
well. They even understand zero indexing, probably
better than most programmers!

Indeed, I think you're more likely to find the physicist
asking why you bother treating the vectors as xyz when
you should be treating them more generally as x0, x1, x2,
etc...

It's typically only the dumb computer programmers who
need to 'visualise' the vector manipulations in terms
of 2d or 3d. So who are you really doing this for? ;)

Your question has already been answered, but I have
a suggestion for you:

#define X 0
#define Y 1
#define Z 2

If your physicist can't understand coord[X] to mean
the X coordinate, then he's probably not much of a
physicist.

If the physicist can't understand coord[0] to mean the
'first' ordinate in the vector, then they're probably
not much of a physicist either; let alone C programmer.

In either case, if you don't think a physicist will
understand the code, why show it to them?
 
E

Edward A. Falk

#define x coord[0]

That would work, but shall we count the ways it would blow up in your face?

Frankly, I don't think there's a clean solution to the problem as stated
unless you're working with a dialect of C that allows anonymous unions or structs.
And even then, only if you can truly cast vector* to double[] as suggested elsewhere.
While that would work on every architecture I've ever used, I doubt that the
standard actually supports it.
 
A

Antoninus Twink

Bartc said:

Right again. There ain't no such thing as a free lunch. Perhaps there's a
better approach that will solve the OP's problem, but I haven't seen it
yet. Any suggestions?

To repeat it yet again: your solution is a C++ solution, not in the
sense that it's written in (C++ but not C), but in the sense that it
fits the approach and mindset of a C++ programmer.

The natural C solution is type-punning by casting structs. You want to
claim that this is not C? Networking is based completely on manipulating
various socket structs etc. in this way. GTK builds a vast
object-oriented framework in C in this way.
 
K

Kenny McCormack

To repeat it yet again: your solution is a C++ solution, not in the
sense that it's written in (C++ but not C), but in the sense that it
fits the approach and mindset of a C++ programmer.

The natural C solution is type-punning by casting structs. You want to
claim that this is not C? Networking is based completely on manipulating
various socket structs etc. in this way. GTK builds a vast
object-oriented framework in C in this way.

And yet, in the twisted minds of the regs, none of those things are
written in C...
 
J

jacob navia

Kenny said:
And yet, in the twisted minds of the regs, none of those things are
written in C...

They are not written in 'regulars C'
o no network
o no graphics
o no GUI
o no directories
o no parallelism
o no threads
o no nothing actually :)
 
K

Kenny McCormack

Kenny McCormack wrote: ....

They are not written in 'regulars C'
o no network
o no graphics
o no GUI
o no directories
o no parallelism
o no threads
o no nothing actually :)

You are absolutely right, of course.

But I want to add (and this was my original point) that in the regs
view, it isn't C. Period. Full stop, as some of them are want to say.

To which, of course, sensible people ask: Well, if it isn't C, what is it?
To which the regs say: It is "C with extensions".
To which, of course, sensible people point out that toast with jelly is
(and this, I know, will come as a big shock and a crushing blow to the
regs) still toast.
 
J

jacob navia

Eric said:
Right. It's the domain of other standards, not of a programming
language standard.


Right. It's the domain of other standards (and less formal quasi-
standards), not of a programming language standard.


You're repeating yourself.


Right. It's the domain of other standards, not of a programming
language standard.


Right. It's the domain of other standards, which some experts
consider premature: The state of the art, they say, is insufficiently
advanced to warrant standardization. In any event, it's not an
appropriate matter for a programming language standard. (And before
somebody hollers "Java," read the serious and sometimes virulent
criticisms that have been leveled at its attempts to parallelize.)


You're repeating yourself.


You're repeating yourself from many earlier threads. You've
told us more than once that C is a dead language,

You are lying, as always
that C's lack of
features (both those mentioned above and others you've promoted)
have rendered it obsolete,

same lie
that nobody uses C any more, ...

same lie


What I DID say is that people like you are killing the language
making any development impossible. Your fight against standard C,
your pushing of obsolete standards, your denial of anything
that goes beyond

int main (void)

your refusal to discuss any improvements to the language

is a sure way of killing C. Lucky for the rest of us, the
regulars of clc are not the ones that count anywhere.
 
S

santosh

jacob said:
Eric Sosman wrote:

What I DID say is that people like you are killing the language
making any development impossible.

Remarkable that you can determine the intentions of people over a
newsgroup, from seeing a few posts.
Your fight against standard C,

Er, that's you.
your pushing of obsolete standards,

No one has done this.
your denial of anything that goes beyond

int main (void)

Nonsense. Just examine a month's worth of threads here and you'll find
that all aspects of Standard C (and a little beyond too) are discussed.
your refusal to discuss any improvements to the language

You mean that you are sore that *your* proposals have not met with much
approval here and in c.s.c? Nevertheless they have been discussed a
lot. I can find huge threads on operator overloading, containers,
threads etc.

You're just sore that you did not emerge with the majority consensus.
is a sure way of killing C. Lucky for the rest of us, the
regulars of clc are not the ones that count anywhere.

One would think otherwise from all the hysterical reactions
these "regulars" seem to provoke from you and the trolls.
 

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

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,586
Members
45,087
Latest member
JeremyMedl

Latest Threads

Top