plc_get_f32() and unions (was: Big Demo ....)

M

Mario de Sousa

Gary James wrote:
>
> On Sun, 17 Jun 2001 00:28:15 Jiri Baum wrote:
>
> > Ok, according to Sarah, you can do it the simple way:
> > typedef f32 (*plc_get_f_func_t)(plc_pt_t p);
> > return ((plc_get_f_func_t) plc_get)(p);
> >
> > or the difficult way:
> > return ( (f32(*)(plc_pt_t))plc_get )(p);
> >
> >
> > I prefer the second one, because the first is not all that simpler and
> > the second at least looks neat
> >
>
> I've kinda lost track of what this thread is trying to accomplish. It seems
> like we are going off on a tangent to save literaly a couple cycles, and in
> the end creating something that is not easy to understand. If you really
> care that much about a couple cycles, then you should be programming in
> assembly language not C (imho).
>
> Why wouldn't you make the normal plc_get() just return a union type.
>
> typedef union
> {
> unsigned char v_8;
> unsigned short v_16;
> unsigned long v_32;
> float v_f;
> } ANY_TYPE;
>
> ANY_TYPE x;
>
> ANY_TYPE plc_get (void)
> {
> return x;
> }
>
> int main (void)
> {
> ANY_TYPE y;
>
> x.v_32 = 100;
> y = plc_get();
> printf ("%u %u %u\n", y.v_8, y.v_16, y.v_32);
>
> x.v_f = 33.5;
> y = plc_get();
> printf ("%f\n", y.v_f);
>
> return 0;
> }

Gary has a point! Thanks!

Is anybody against this?

Cheers,

Mario.

----------------------------------------------------------------------------
Mario J. R. de Sousa
[email protected]
----------------------------------------------------------------------------

The box said it requires Windows 95 or better, so I installed Linux

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc

J

Jiri Baum

It would complicate the interface for the most usual case, which is u32 (most often used as a plain boolean). You want to be able to say
if (plc_get(p)) ...

Also, we'd have to be very careful about alignment of the smaller types.

Using the typecast as above, the interface would be simple (one function returns u32, the other returns f32) at the cost of an obscure line in the
implementation. I think that's worth the trade.

Jiri
--
Jiri Baum <[email protected]>
"In my opinion, the GPL is optimized to build a strong software community at the expense of a strong commercial software business model."
--Craig Mundie, Senior VP, Microsoft; 17 May 2001

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc

M

Mario de Sousa

Jiri Baum wrote:
>
> Jiri Baum:
> > > > Ok, according to Sarah, you can do it the simple way:
> > > > typedef f32 (*plc_get_f_func_t)(plc_pt_t p);
> > > > return ((plc_get_f_func_t) plc_get)(p);
>
> > > > or the difficult way:
> > > > return ( (f32(*)(plc_pt_t))plc_get )(p);
>
> Gary James:
> > > Why wouldn't you make the normal plc_get() just return a union type.
>
> Mario:
> > Gary has a point! Thanks!
>
> > Is anybody against this?
>
> It would complicate the interface for the most usual case, which is u32
> (most often used as a plain boolean). You want to be able to say
> if (plc_get(p)) ...
>
> Also, we'd have to be very careful about alignment of the smaller types.
>
> Using the typecast as above, the interface would be simple (one function
> returns u32, the other returns f32) at the cost of an obscure line in the
> implementation. I think that's worth the trade.

It's really not _that_ obscure. We've used it often enough in the gmm already. It's just that the idea of typecasting the function to get to a
f32 had never crossed my mind! But it does make a lot of sense, once you had mentioned it.
I ussually prefer the first method (using the typecast). It is easier to understand, especially when you start having pointers to pointers of
functions...

Alignment in a union shouldn't be a problem. To my knowledge, you should start worrying about alignment once you start using structs. Unions and structs are different things...

Using the union, you could still use

if(plc_get(p).bool)

or

if(plc_get(p).u32) , ...

It would stop the proliferation of functions that are a simple typecast!

I think it just boils down to a matter of taste.

This is of secondary importance anyway, so let's just see what comes up...

Cheers,

Mario.

----------------------------------------------------------------------------
Mario J. R. de Sousa
[email protected]
----------------------------------------------------------------------------

The box said it requires Windows 95 or better, so I installed Linux

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc

G

Gary James

On Mon, 18 Jun 2001 05:01:38 Jiri Baum wrote:

> It would complicate the interface for the most usual case, which is u32
> (most often used as a plain boolean). You want to be able to say
> if (plc_get(p)) ...

I think somebody else said it too, but you could always do this:

if (plc_get().v_32) ...

Assuming that this is the type...

typedef union
{
unsigned char v_8;
unsigned short v_16;
unsigned long v_32;
float v_f;
} ANY_TYPE;

> Also, we'd have to be very careful about alignment of the smaller types.
>

You only have to be careful with the alignment of smaller types if you plan to do type conversions through the point table. That would be very bad
thing, regardless of whether you use structures or pointer casts.

Using the union approach...
ANY_TYPE x, y;
x.v_32 = 1;
y.v_8 = 1
if (x.v_8 == y.v_8) /* true on a PC, false on a PowerPC */

Using the pointer approach...
unsigned long x;
unsigned char y;
x = 1;
y = 1;
if (*((unsigned char *)&x) == y) /* true on a PC, false on a PowerPC */

You really don't have to be careful, if you only do things that are in the K&R book. Unfortunately after 10 years of programming x86 on DOS followed
by 10 years of programming x86 on Windows, we've all learned nasty habits that we think are proper C coding styles. I have had the luxary of using
three different RISC chips in the past six months that have shown me a few (but I'm sure not all) of my bad habits. Any way I've been bit several
times during the past few months by old code that works great on x86 but fails on RISC. In almost every case it had to do with structure alignment
and/or type conversion through pointer casting.

The endinaness of the RISC isn't the only thing that matters when you get bit by pointer cast bugs. On an little endian (Intel order) ARM7 project I got bit by something like this:
char x[5];
x[0] = 0xFF;
x[1] = 0x01;
x[2] = 0x00;
x[3] = 0x00;
x[4] = 0x00;
if (*((unsigned long *)&x[1]) == 0x00000001) /* true on x86, false on
ARM7 */
if (*((unsigned long *)&x[1]) == 0xFF000001) /* false on x86, true on
ARM7 */

I also worked on an ST40 with a real crappy compiler that probably would do the same thing, although I didn't run the same code through it.

I guess my point is, if you care about architecture indepedent code (you should care with todays mix of platforms) then you should worry less about few cycles than sticking to solid code as outlined in good old K&R. Stay
away from pointer tricks, unless you have them surrounded by something like this (and this really stinks):
#if(LITTLE_ENDIAN && ALIGNED_ACCESS)
#elif(LITTLE_ENDIAN && !ALIGNED_ACCESS)
#elif(BIG_ENDIAN & ALIGNED_ACCESS)
#elif(BIG_ENDIAN & !ALIGNED_ACCESS)
#endif

Gary James

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc

M

Mario de Sousa

Gary James wrote:
>
> On Mon, 18 Jun 2001 05:01:38 Jiri Baum wrote:
>
(...)
> >
> > Also, we'd have to be very careful about alignment of the smaller types.
> >
>
> You only have to be careful with the alignment of smaller types if you plan
> to do type conversions through the point table. That would be very bad
> thing, regardless of whether you use structures or pointer casts.
>
> Using the union approach...
> ANY_TYPE x, y;
> x.v_32 = 1;
> y.v_8 = 1
> if (x.v_8 == y.v_8) /* true on a PC, false on a PowerPC */

Completely agreed!

(...)
> I guess my point is, if you care about architecture indepedent code (you
> should care with todays mix of platforms) then you should worry less about
> few cycles than sticking to solid code as outlined in good old K&R. Stay
> away from pointer tricks, unless you have them surrounded by something like
> this (and this really stinks):
> #if(LITTLE_ENDIAN && ALIGNED_ACCESS)
> #elif(LITTLE_ENDIAN && !ALIGNED_ACCESS)
> #elif(BIG_ENDIAN & ALIGNED_ACCESS)
> #elif(BIG_ENDIAN & !ALIGNED_ACCESS)
> #endif
>

Actually, our pointer tricks work in any architecture, as long as the user is consistent in the way (s)he uses the value stored in a plc_pt. The same is true of the union approach.

Both are simply a way of telling the compiler to consider those 32 bits, not as an unsigned int, but as a float. If you always write and read as a float, you have no worries, whatever the architecture. And note that we use f32, and not float. Float is not guaranteed to be 32 bits wide in every architecture. We have the /lib/type.h file to protect us from that!

Apart from this, I still prefer your approach. The typecast approach has an extra function call, if the plc_set_f32() is not inlined (which
it still isn't). Once this function is inlined, they end up being equivalent. Choosing between either will be a mere matter of taste.

But, risking sounding repetitive, I still prefer your approach. It simply makes us having to edit all the source code to change it, and at the moment, there are more important things to do...

But it is still fun discussing these details... .

Cheers,

Mario.

----------------------------------------------------------------------------
Mario J. R. de Sousa
[email protected]
----------------------------------------------------------------------------

The box said it requires Windows 95 or better, so I installed Linux

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc

J

Jiri Baum

Mario:
> Actually, our pointer tricks work in any architecture, as long as the
> user is consistent in the way (s)he uses the value stored in a plc_pt.
> The same is true of the union approach.

The problem is that currently points narrower than 32 bits are put into the least significant bits of the point, while in the union the v8 is the first 8 bits. This may or may not be the same.

For a lot of applications, you don't care about the difference between unsigned 8 bit and unsigned 32 bit - so we have taken the approach of
promoting everything to u32.

For signed integers, it's a function call anyway because you have to sign-extend the value. There's no way around it, because point length is
run-time and anyway there are no 5-bit integers in C.

> Both are simply a way of telling the compiler to consider those 32 bits,
> not as an unsigned int, but as a float. If you always write and read as a
> float, you have no worries, whatever the architecture.

Precisely.

> Apart from this, I still prefer your approach.
...
> It simply makes us having to edit all the source code to change it, and
> at the moment, there are more important things to do...

We can have another version of the function that returns a union.

(The old make it an option' approach.)

Jiri
--
Jiri Baum <[email protected]>
"In my opinion, the GPL is optimized to build a strong software community at the expense of a strong commercial software business model."
--Craig Mundie, Senior VP, Microsoft; 17 May 2001

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc

J

Juan Carlos Orozco

Jiri Baum wrote:
>
> Mario:
> > Actually, our pointer tricks work in any architecture, as long as the
> > user is consistent in the way (s)he uses the value stored in a plc_pt.
> > The same is true of the union approach.
>
> The problem is that currently points narrower than 32 bits are
> put into the
> least significant bits of the point, while in the union the v8 is
> the first
> 8 bits. This may or may not be the same.
>
> For a lot of applications, you don't care about the difference between
> unsigned 8 bit and unsigned 32 bit - so we have taken the approach of
> promoting everything to u32.
>
> For signed integers, it's a function call anyway because you have to
> sign-extend the value. There's no way around it, because point length is
> run-time and anyway there are no 5-bit integers in C.
>
> > Both are simply a way of telling the compiler to consider those 32 bits,
> > not as an unsigned int, but as a float. If you always write and
> read as a
> > float, you have no worries, whatever the architecture.
>
> Precisely.
>
> > Apart from this, I still prefer your approach.
> ...
> > It simply makes us having to edit all the source code to change it, and
> > at the moment, there are more important things to do...
>
> We can have another version of the function that returns a union.
>
> (The old make it an option' approach.)

I like the approach of making it an option, because I agree with Mario about this being a matter of taste.

I personally prefer to have a function where I can write and read directly float values.

f = get_plc_f32(p); // f is of type f32.

plc_set_f32(p, 32.0);

Readability is nice too.

combined operations are also written in a straight forward way.

if( plc_get_f32(b) != 0.0 )
plc_set_f32(p, plc_get_f32(a) / plc_get_f32(b));

I know the union alternative is not that bad but then again it is a matter of taste... (Could someone place the union alternatives to this operations).

Regards,

Juan Carlos Orozco

ACElab Industrial Automation
[email protected]
www.ace-lab.com

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc