Macros accepting structs as input argument


M

matt

Dear all,

I'm not actually working with the data struct that I'm going to use
in my example, indeed the data structs which I work with are by far more
complex, but I think it should easily show the point.

Let us suppose that I want implement a library handling (among other
things) points in 3D space. I would choose between the 2 following
implementations:

1.
typedef struct point3d_ {
double x;
double y;
double z;
} point3d;

2.
typedef struct point3d_ {
double data[3];
} point3d;

Let us say that I choose solution 2, but also that I want write my
code so that I can change the internal point3d representation in future,
without having to change the whole code I (and other users) wrote. For
this purpose, I would define some macros like

Point3DInit(point, xval, yval, zval) /* initialize the 3D point */
Point3DSetX(point, xval) /* set the value xval in the X-component
of point */
Point3DSetY(point, yval) /* ... */
Point3DSetZ(point, zval) /* ... */
Point3DExtX(xval, point) /* extract the X-component of point and
put its value in xval */
Point3DExtY(yval, point) /* ... */
Point3DExtZ(zval, point) /* ... */

which, of course, vary as the internal implementation of point3d change.

My question is: which would be, in your opinion, the best way to
consider the variable point in the macros above? A point3d *object* or a
*pointer* to point3d objects?

In my opinion, to consider the variable point a *pointer* to point3d
objects would be the most suitable solution, since it would make the use
of the generic macro more similar to the use of a typical C function
modifying structs, and this should facilitate all the users using it (at
least for those macros which actually change the values of the 3D point,
i.e. Init and SetX,Y,Z). Anyway, are you aware of which the most elegant
solution to this problem is (if any)?

Also, I'm wondering if the following piece of code

point3d point;
Point3DInit(&point, 1, 2, 3);

which is preprocessed to

point3d point;
((&point)->data[0]=1, (&point)->data[1]=2, (&point)->data[2]=3);

leads to an object code whose performances are worse than those of the
object code produced by the compilation of the following code

point3d point;
point.data[0]=1, point.data[1]=2, point.data[2]=3;

Of course this depends on the adopted compiler, but I'm referring to
wide-use compiler like gcc or MS Visual C++.

Thanks in advance.
 
Ad

Advertisements

B

Ben Pfaff

matt said:
Let us suppose that I want implement a library handling (among other
things) points in 3D space. I would choose between the 2 following
implementations:

1.
typedef struct point3d_ {
double x;
double y;
double z;
} point3d;

2.
typedef struct point3d_ {
double data[3];
} point3d;

Let us say that I choose solution 2, but also that I want write my
code so that I can change the internal point3d representation in
future, without having to change the whole code I (and other users)
wrote. For this purpose, I would define some macros like

Point3DInit(point, xval, yval, zval) /* initialize the 3D point */
Point3DSetX(point, xval) /* set the value xval in the X-component
of point */
Point3DSetY(point, yval) /* ... */
Point3DSetZ(point, zval) /* ... */
Point3DExtX(xval, point) /* extract the X-component of point and
put its value in xval */
Point3DExtY(yval, point) /* ... */
Point3DExtZ(zval, point) /* ... */

which, of course, vary as the internal implementation of point3d change.

My question is: which would be, in your opinion, the best way to
consider the variable point in the macros above? A point3d *object* or
a *pointer* to point3d objects?

I would use a pointer, because that lets you implement the above
as functions instead of as macros. I would also change the "Ext"
macros/functions to return the components instead of setting a
variable to their values, for the same reason.
 
N

Nick Birnie

Dear all,

I'm not actually working with the data struct that I'm going to use
in my example, indeed the data structs which I work with are by far
more complex, but I think it should easily show the point.

Let us suppose that I want implement a library handling (among other
things) points in 3D space. I would choose between the 2 following
implementations:

1. typedef struct point3d_ { double x; double y; double z; }
point3d;

2. typedef struct point3d_ { double data[3]; } point3d;

Let us say that I choose solution 2, but also that I want write my
code so that I can change the internal point3d representation in
future, without having to change the whole code I (and other users)
wrote. For this purpose, I would define some macros like

Point3DInit(point, xval, yval, zval) /* initialize the 3D point */
Point3DSetX(point, xval) /* set the value xval in the X-component of
point */ Point3DSetY(point, yval) /* ... */ Point3DSetZ(point, zval)
/* ... */ Point3DExtX(xval, point) /* extract the X-component of
point and put its value in xval */ Point3DExtY(yval, point) /* ...
*/ Point3DExtZ(zval, point) /* ... */

which, of course, vary as the internal implementation of point3d
change.

My question is: which would be, in your opinion, the best way to
consider the variable point in the macros above? A point3d *object*
or a *pointer* to point3d objects?

In my opinion, to consider the variable point a *pointer* to point3d
objects would be the most suitable solution, since it would make the
use of the generic macro more similar to the use of a typical C
function modifying structs, and this should facilitate all the users
using it (at least for those macros which actually change the values
of the 3D point, i.e. Init and SetX,Y,Z). Anyway, are you aware of
which the most elegant solution to this problem is (if any)?

Also, I'm wondering if the following piece of code

point3d point; Point3DInit(&point, 1, 2, 3);

which is preprocessed to

point3d point; ((&point)->data[0]=1, (&point)->data[1]=2,
(&point)->data[2]=3);

leads to an object code whose performances are worse than those of
the object code produced by the compilation of the following code

point3d point; point.data[0]=1, point.data[1]=2, point.data[2]=3;

Of course this depends on the adopted compiler, but I'm referring to
wide-use compiler like gcc or MS Visual C++.

The low level implementation of the address operator is probably not
specified in anywhere in the C standard. Instead you may want to
generate assembly listings for a trivial example. For gcc, this can be
done with -S.

For the below example, gcc was smart enough to not leal base+disp, %tmp,
movl $const, %tmp

[[email protected] ~]$ cat ptr.s

[[email protected] ~]$ gcc ptr.c -S -pedantic -std=c99
.file "ptr.c"
.text
..globl method1
.type method1, @function
method1:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $0x3f800000, %eax
movl %eax, -12(%ebp)
leave
ret
.size method1, .-method1
..globl method2
.type method2, @function
method2:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $0x3f800000, %eax
movl %eax, -12(%ebp)
leave
ret
.size method2, .-method2
..globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
call method1
call method2
movl $0, %eax
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.3.2 20081105 (Red Hat 4.3.2-7)"
.section .note.GNU-stack,"",@progbits
[[email protected] ~]$
 
N

Nick Birnie

Dear all,

I'm not actually working with the data struct that I'm going to use
in my example, indeed the data structs which I work with are by far
more complex, but I think it should easily show the point.

Let us suppose that I want implement a library handling (among other
things) points in 3D space. I would choose between the 2 following
implementations:

1. typedef struct point3d_ { double x; double y; double z; }
point3d;

2. typedef struct point3d_ { double data[3]; } point3d;

Let us say that I choose solution 2, but also that I want write my
code so that I can change the internal point3d representation in
future, without having to change the whole code I (and other users)
wrote. For this purpose, I would define some macros like

Point3DInit(point, xval, yval, zval) /* initialize the 3D point */
Point3DSetX(point, xval) /* set the value xval in the X-component of
point */ Point3DSetY(point, yval) /* ... */ Point3DSetZ(point, zval)
/* ... */ Point3DExtX(xval, point) /* extract the X-component of
point and put its value in xval */ Point3DExtY(yval, point) /* ...
*/ Point3DExtZ(zval, point) /* ... */

which, of course, vary as the internal implementation of point3d
change.

My question is: which would be, in your opinion, the best way to
consider the variable point in the macros above? A point3d *object*
or a *pointer* to point3d objects?

In my opinion, to consider the variable point a *pointer* to point3d
objects would be the most suitable solution, since it would make the
use of the generic macro more similar to the use of a typical C
function modifying structs, and this should facilitate all the users
using it (at least for those macros which actually change the values
of the 3D point, i.e. Init and SetX,Y,Z). Anyway, are you aware of
which the most elegant solution to this problem is (if any)?

Also, I'm wondering if the following piece of code

point3d point; Point3DInit(&point, 1, 2, 3);

which is preprocessed to

point3d point; ((&point)->data[0]=1, (&point)->data[1]=2,
(&point)->data[2]=3);

leads to an object code whose performances are worse than those of
the object code produced by the compilation of the following code

point3d point; point.data[0]=1, point.data[1]=2, point.data[2]=3;

Of course this depends on the adopted compiler, but I'm referring to
wide-use compiler like gcc or MS Visual C++.
The low level implementation of the address operator is probably not
specified in in the C standard. Instead you may want to generate
assembly listings for a trivial example. For gcc, this can be
done with -S.

For the below example, gcc was smart enough to not leal base+disp, %tmp,
movl $const, %tmp

[[email protected] ~]$ cat ptr.s
struct point {
float data[3];
};

void method1() {
struct point p;
p.data[0] = 1.0f;
}

void method2() {
struct point p;
(&p)->data[0] = 1.0f;
}

int main(void) {
method1();
method2();

return 0;
}
[[email protected] ~]$ gcc ptr.c -S -pedantic -std=c99
[[email protected] ~]$ cat ptr.s
.file "ptr.c"
.text
..globl method1
.type method1, @function
method1:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $0x3f800000, %eax
movl %eax, -12(%ebp)
leave
ret
.size method1, .-method1
..globl method2
.type method2, @function
method2:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $0x3f800000, %eax
movl %eax, -12(%ebp)
leave
ret
.size method2, .-method2
..globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
call method1
call method2
movl $0, %eax
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.3.2 20081105 (Red Hat 4.3.2-7)"
.section .note.GNU-stack,"",@progbits
[[email protected] ~]$
 
N

Nick Birnie

Dear all,

I'm not actually working with the data struct that I'm going to use in
my example, indeed the data structs which I work with are by far more
complex, but I think it should easily show the point.

Let us suppose that I want implement a library handling (among other
things) points in 3D space. I would choose between the 2 following
implementations:

1.
typedef struct point3d_ {
double x;
double y;
double z;
} point3d;

2.
typedef struct point3d_ {
double data[3];
} point3d;

Let us say that I choose solution 2, but also that I want write my code
so that I can change the internal point3d representation in future,
without having to change the whole code I (and other users) wrote. For
this purpose, I would define some macros like

Point3DInit(point, xval, yval, zval) /* initialize the 3D point */
Point3DSetX(point, xval) /* set the value xval in the X-component of
point */
Point3DSetY(point, yval) /* ... */
Point3DSetZ(point, zval) /* ... */
Point3DExtX(xval, point) /* extract the X-component of point and put its
value in xval */
Point3DExtY(yval, point) /* ... */
Point3DExtZ(zval, point) /* ... */

which, of course, vary as the internal implementation of point3d change.

My question is: which would be, in your opinion, the best way to
consider the variable point in the macros above? A point3d *object* or a
*pointer* to point3d objects?

In my opinion, to consider the variable point a *pointer* to point3d
objects would be the most suitable solution, since it would make the use
of the generic macro more similar to the use of a typical C function
modifying structs, and this should facilitate all the users using it (at
least for those macros which actually change the values of the 3D point,
i.e. Init and SetX,Y,Z). Anyway, are you aware of which the most elegant
solution to this problem is (if any)?

Also, I'm wondering if the following piece of code

point3d point;
Point3DInit(&point, 1, 2, 3);

which is preprocessed to

point3d point;
((&point)->data[0]=1, (&point)->data[1]=2, (&point)->data[2]=3);

leads to an object code whose performances are worse than those of the
object code produced by the compilation of the following code

point3d point;
point.data[0]=1, point.data[1]=2, point.data[2]=3;

Of course this depends on the adopted compiler, but I'm referring to
wide-use compiler like gcc or MS Visual C++.

Thanks in advance.
The low level implementation of the address operator is probably not
specified in in the C standard. Instead you may want to generate
assembly listings for a trivial example. For gcc, this can be
done with -S.

For the below example, gcc was smart enough to not leal base+disp, %tmp,
movl $const, %tmp

[[email protected] ~]$ cat ptr.c
struct point {
float data[3];
};

void method1() {
struct point p;
p.data[0] = 1.0f;
}

void method2() {
struct point p;
(&p)->data[0] = 1.0f;
}

int main(void) {
method1();
method2();

return 0;
}
[[email protected] ~]$ gcc ptr.c -S -pedantic -std=c99
[[email protected] ~]$ cat ptr.s
.file "ptr.c"
.text
..globl method1
.type method1, @function
method1:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $0x3f800000, %eax
movl %eax, -12(%ebp)
leave
ret
.size method1, .-method1
..globl method2
.type method2, @function
method2:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $0x3f800000, %eax
movl %eax, -12(%ebp)
leave
ret
.size method2, .-method2
..globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
call method1
call method2
movl $0, %eax
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.3.2 20081105 (Red Hat 4.3.2-7)"
.section .note.GNU-stack,"",@progbits
[[email protected] ~]$
 
N

Nick Birnie

Dear all,

I'm not actually working with the data struct that I'm going to use
in my example, indeed the data structs which I work with are by far
more complex, but I think it should easily show the point.

Let us suppose that I want implement a library handling (among other
things) points in 3D space. I would choose between the 2 following
implementations:

1. typedef struct point3d_ { double x; double y; double z; }
point3d;

2. typedef struct point3d_ { double data[3]; } point3d;

Let us say that I choose solution 2, but also that I want write my
code so that I can change the internal point3d representation in
future, without having to change the whole code I (and other users)
wrote. For this purpose, I would define some macros like

Point3DInit(point, xval, yval, zval) /* initialize the 3D point */
Point3DSetX(point, xval) /* set the value xval in the X-component of
point */ Point3DSetY(point, yval) /* ... */ Point3DSetZ(point, zval)
/* ... */ Point3DExtX(xval, point) /* extract the X-component of
point and put its value in xval */ Point3DExtY(yval, point) /* ...
*/ Point3DExtZ(zval, point) /* ... */

which, of course, vary as the internal implementation of point3d
change.

My question is: which would be, in your opinion, the best way to
consider the variable point in the macros above? A point3d *object*
or a *pointer* to point3d objects?

In my opinion, to consider the variable point a *pointer* to point3d
objects would be the most suitable solution, since it would make the
use of the generic macro more similar to the use of a typical C
function modifying structs, and this should facilitate all the users
using it (at least for those macros which actually change the values
of the 3D point, i.e. Init and SetX,Y,Z). Anyway, are you aware of
which the most elegant solution to this problem is (if any)?

Also, I'm wondering if the following piece of code

point3d point; Point3DInit(&point, 1, 2, 3);

which is preprocessed to

point3d point; ((&point)->data[0]=1, (&point)->data[1]=2,
(&point)->data[2]=3);

leads to an object code whose performances are worse than those of
the object code produced by the compilation of the following code

point3d point; point.data[0]=1, point.data[1]=2, point.data[2]=3;

Of course this depends on the adopted compiler, but I'm referring to
wide-use compiler like gcc or MS Visual C++.

Thanks in advance.

The more that I think about this, the more that it seems impossible to
give a reasonable answer. The compiler is free to implement the
assignments however it sees fit, and the generated assembly language can
be subject to many optimisations. If you are assigning by dereferencing
an pointer, subject to the address of the struct actually being stored
in memory, then you will incur an additional load to read the
destination address.
 
Ad

Advertisements

D

David Thompson

Also, I'm wondering if the following piece of code

point3d point;
Point3DInit(&point, 1, 2, 3);

which is preprocessed to

point3d point;
((&point)->data[0]=1, (&point)->data[1]=2, (&point)->data[2]=3);

leads to an object code whose performances are worse than those of the
object code produced by the compilation of the following code

point3d point;
point.data[0]=1, point.data[1]=2, point.data[2]=3;

Of course this depends on the adopted compiler, but I'm referring to
wide-use compiler like gcc or MS Visual C++.
<caveat> Nothing is guaranteed/required by the C standard but </>
when I was in college over 30 years ago any compiler that didn't
correctly merge simple fixed cases like this was considered lame.
Today I wouldn't even waste my time worrying about it.

The oldest versions I have conveniently to hand do it right: gcc 3.4.2
even with -O0 (which is quite dumb) and vc++03 (default /Od).
 

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