Topic: casting classes for embedded systems
Author: landtuna@hotmail.com (Jim Hunziker)
Date: Mon, 21 Apr 2003 13:06:54 +0000 (UTC) Raw View
> This is all really rather far afield for comp.std.c++, since even the
> assumptions about "likely" behavior we have been discussing are all
> either implementation-defined or undefined behavior under the C++ (or
> C) language standard. If you would like to discuss this further, I
> suggest you either email me or post to comp.arch.embedded instead of
> continuing here where we're really off-topic.
Agreed. Thank you for all your insightful remarks. One more
question, though, to satisfy my curiosity. (I might be opening a can
of worms here.) Why did the authors of the C++ standard leave so much
to be implementation-defined or undefined that has great use to
embedded programming? Is the flexibility that allowing these
behaviors affords really taken advantage of by compiler vendors? Or
was it just that hammering out these issues would have delayed an
already long-overdue standard?
--- Jim
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: allan_w@my-dejanews.com (Allan W)
Date: Mon, 21 Apr 2003 19:21:24 +0000 (UTC) Raw View
landtuna@hotmail.com (Jim Hunziker) wrote
> Sorry for not making myself clear enough. I realize that padding may
> differ from platform to platform, but I imagine any platform will have
> an option to pack bytes.
Not necessarily. It depends on what you mean "platform."
Suppose your OS is Foozle, and your compiler is Foo-C++. If that compiler
has an option to pack bytes, then probably most of the other compilers
for Foozle will have the same option. Possibly if you switch to a
different OS on the same box -- say, Barzle -- then the Bar-C++ compiler
will also have this option.
However, if you port your program to a completely different CPU, things
may drastically change.
There are 3 ways that a CPU can deal with unaligned data.
1. Completely transparently. If a CPU fetches individual bytes from
memory, it's not likely to make a significant difference if those
bytes have any sort of alignment or not. The Intel 8088 was like
this.
Surely in this case, padding is not very important -- it matters
only for compatibility with other computers that do pad.
2. Slowly. On some CPUs, misaligned reads must be handled by the
firmware. The CPU actually has to break the operation into
pieces -- first it fetches the low-order word, then the high-order
word. Then it performs a series of shift-and-mask operations until
the correct answer is obtained. In this case, the operation will
be seen as successful, except that it will go more slowly than usual.
I think that modern Pentiums are like this.
In this case, it would make a lot of sense for the compiler to
prefer padding bytes, but to have an option that packs the bytes
(for compatibility purposes).
3. Traumatically. On some CPUs, there is no firmware provision for
misaligned reads. Possibly the low-order address bits will be
ignored, and the wrong data read. Or possibly the read attempt
will cause a trap. In the latter case it's conceivable that the
operating system (or C++ run-time system) could catch the trap
and do in software what machine #2 does in firmware -- but at a
much higher performance cost. Other times, even this is not
possible. The DEC VAX was like this.
In this case, the option to pack is no option at all. If packing
is needed for compatibility purposes, it would make more sense to
require the user to do it herself:
struct buffer {
char name[80+1];
unsigned ssn[4]; // This is a 4-byte packed integer...
};
unsigned long ssn(buffer*b) {
unsigned long s;
memcpy(&s, b->ssn, 4);
return s;
}
> The portablity matters to me not because I want to compile the program
> anywhere but rather because I want to minimize rework when changing
> platforms. In this case, the register is on an external device whose
> interface will never change, but the code I'm showing would be on a
> single board computer that could be from any vendor using any
> processor.
If some processors cannot "pack," you've got an issue.
> Why don't I just hide the bit manipulation inside functions?
> Because the code ends up being much harder to read.
>
> a.clear();
> a.enable_execution = 1;
> a.set_pulse_time = 32;
> a.ignore_errors = 0;
>
> is a whole lot more understandable than:
>
> a = 0;
> a |= 0x00000001;
> a |= (32 << PULSE_TIME_BIT_SHIFT);
But this is not "hiding the bit manipulation inside functions"!
I can imagine a series of setXxx operations, which determine how you
WANt to set the flags -- followed by a send() operation, which
actually manipulates the registers.
a.setExecution(1);
a.setPulseTime(32);
a.setErrors(0);
a.send(); // Sets execution=enabled, pulsetime=32, errors=ignored
> > 3. the member a1 is located at the same address as the class itself,
> > which is not guaranteed once a class or struct has access specifiers.
>
> But if they're all the same access specifier, can I take the address
> of the first one and assume that the others follow in order? (Again,
> I assume my bytes are packed.)
IIRC (I haven't looked it up yet), the members between access specifiers
are so guaranteed.
public:
int a;
int b;
int c;
public:
int d;
int e;
int f;
private:
A,b,c are guaranteed contiguous; so are d,e,f, although they don't
neccesarily follow c -- I think.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: landtuna@hotmail.com (Jim Hunziker)
Date: Thu, 17 Apr 2003 17:46:14 +0000 (UTC) Raw View
I'm writing an embedded application where there's a memory-mapped
register that needs some bits set.
If I have a class that looks like this:
class A
{
private:
unsigned char a1;
unsigned char a2;
unsigned short a3;
public:
void set_a1(unsigned char value);
void set_a2(unsigned char value);
void set_a3(unsigned short value);
operator unsigned long () const;
};
A::operator unsigned long () const
{
return (*(reinterpret_cast<unsigned long *> (this)));
}
Can I be confident that the following will work portably?
A foo;
foo.set_a1(3); foo.set_a2(2); foo.set_a3(3);
volatile unsigned long *mapped_reg = 0xc0000000;
mapped_reg = foo;
What if I had used some bitfields in my definition of A?
Thanks.
--- Jim
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: jackklein@spamcop.net (Jack Klein)
Date: Fri, 18 Apr 2003 02:08:11 +0000 (UTC) Raw View
On Thu, 17 Apr 2003 17:46:14 +0000 (UTC), landtuna@hotmail.com (Jim
Hunziker) wrote in comp.std.c++:
> I'm writing an embedded application where there's a memory-mapped
> register that needs some bits set.
>
> If I have a class that looks like this:
>
> class A
> {
> private:
> unsigned char a1;
> unsigned char a2;
> unsigned short a3;
>
> public:
> void set_a1(unsigned char value);
> void set_a2(unsigned char value);
> void set_a3(unsigned short value);
>
> operator unsigned long () const;
> };
>
> A::operator unsigned long () const
> {
> return (*(reinterpret_cast<unsigned long *> (this)));
> }
>
> Can I be confident that the following will work portably?
That depends on what you mean by portably.
> A foo;
> foo.set_a1(3); foo.set_a2(2); foo.set_a3(3);
Assuming appropriate member function definitions for these member
functions, which you did not show, this will set the first character
in foo (a1) to 3. The second character, a2, will be set to 2. This
is probably the byte following a1, with no padding bytes in between,
but there is no guarantee nor requirement in the C++ language
concerning that. And the third character in the structure, which
might or might not be the third byte, will be set to 3.
> volatile unsigned long *mapped_reg = 0xc0000000;
This won't even compile without a cast, of course.
> mapped_reg = foo;
First, why do you want to do this? What do you hope to gain by adding
such abstraction to a low-level function like direct hardware access?
And why the concern with portability, are there lots of other
platforms with the same memory-mapped hardware device?
OK, let's make some assumptions here, none of which are actually
guaranteed by the language...
1. unsigned long is four bytes
2. the data members of your class are contiguous, with no padding.
3. the member a1 is located at the same address as the class itself,
which is not guaranteed once a class or struct has access specifiers.
4. your compiler pads your three contiguous data byte class into four
bytes, the same size as unsigned long, and sets the padding byte to 0.
Then, assuming you add missing member functions and a cast on the
assignment to the pointer, you have two likely results:
A. 0xc0000000 receives the value 3 // a1
0xc0000001 receives the value 2 // a2
0xc0000002 receives the value 3 // a3
0xc0000003 receives the value 0 // padding byte
B. 0xc0000000 receives the value 0 // padding byte
0xc0000001 receives the value 3 // a3
0xc0000002 receives the value 2 // a2
0xc0000003 receives the value 3 // a1
Other outcomes are possible, but such architectures are not common.
Note also that your compiler might put a padding byte at the end of
the data members but might not specifically place any particular value
there, so you might be writing some uninitialized value to either
0xc0000000 or 0xc0000003, depending.
As far as byte order goes, if you are using an ARM processor, for
example, you will get one result if you build the code in
little-endian mode, and the other in big-endian. I'll leave it up to
you to figure out which is which.
Finally, accessing your class through a pointer to unsigned long is
just plain undefined behavior altogether. The C++ language does not
define, or even care, what the result it. It is possible that the
value in the uninitialized byte if the class, or in memory outside the
instance of the class if the compiler does not add a padding byte,
could cause the whole thing to be a trap value for an unsigned long.
>
> What if I had used some bitfields in my definition of A?
>
> Thanks.
>
> --- Jim
Bit-fields would make matters even worse. They are among the least
portable things of all in C or C++. The storage unit size and
endianness of bit assignments is completely implementation-defined.
But again, being an embedded systems programmer, I really want to know
why you want to do this? What are you trying to achieve? Assuming
you restrict the casted pointer to driver code, what extra gain do you
perceive in attempting to hide the hardware representation inside the
private members of a class?
--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: landtuna@hotmail.com (Jim Hunziker)
Date: Fri, 18 Apr 2003 16:58:36 +0000 (UTC) Raw View
Bear with me here - I'll get back to where this is relevant to
standard C++ towards the bottom...
jackklein@spamcop.net (Jack Klein) wrote in message news:<gtku9voqggngn68eddrfq5km7jjrf1cs1d@4ax.com>...
> On Thu, 17 Apr 2003 17:46:14 +0000 (UTC), landtuna@hotmail.com (Jim
> Hunziker) wrote in comp.std.c++:
>
> > I'm writing an embedded application where there's a memory-mapped
> > register that needs some bits set.
> is probably the byte following a1, with no padding bytes in between,
> but there is no guarantee nor requirement in the C++ language
> concerning that. And the third character in the structure, which
> might or might not be the third byte, will be set to 3.
Sorry for not making myself clear enough. I realize that padding may
differ from platform to platform, but I imagine any platform will have
an option to pack bytes.
The portablity matters to me not because I want to compile the program
anywhere but rather because I want to minimize rework when changing
platforms. In this case, the register is on an external device whose
interface will never change, but the code I'm showing would be on a
single board computer that could be from any vendor using any
processor.
> > volatile unsigned long *mapped_reg = 0xc0000000;
>
> This won't even compile without a cast, of course.
Whoops. Right.
> > mapped_reg = foo;
>
> First, why do you want to do this? What do you hope to gain by adding
> such abstraction to a low-level function like direct hardware access?
> And why the concern with portability, are there lots of other
> platforms with the same memory-mapped hardware device?
This register that I've been talking about has a set of bits whose
functionalities are unrelated. I'd like to expose the functionality
of each one through an API which does not require bit manipulation
expertise of the user of the API.
So why don't I just hide the bit manipulation inside functions?
Because the code ends up being much harder to read.
a.clear();
a.enable_execution = 1;
a.set_pulse_time = 32;
a.ignore_errors = 0;
is a whole lot more understandable than:
a = 0;
a |= 0x00000001;
a |= (32 << PULSE_TIME_BIT_SHIFT);
> OK, let's make some assumptions here, none of which are actually
> guaranteed by the language...
Right. Sorry I didn't list mine.
> 1. unsigned long is four bytes
Yes.
> 2. the data members of your class are contiguous, with no padding.
These are two assumptions. Padding, as I said before, I'm assuming I
can set as an option for whatever compiler I'm using. But I'm not
sure from the standard whether I'm guaranteed data member contiguity
and ordering for anything but C structs of POD.
> 3. the member a1 is located at the same address as the class itself,
> which is not guaranteed once a class or struct has access specifiers.
But if they're all the same access specifier, can I take the address
of the first one and assume that the others follow in order? (Again,
I assume my bytes are packed.)
> 4. your compiler pads your three contiguous data byte class into four
> bytes, the same size as unsigned long, and sets the padding byte to 0.
Well, my original example had 2 chars and a short, so it shouldn't
need padding to get to a long.
> Then, assuming you add missing member functions and a cast on the
> assignment to the pointer, you have two likely results:
>
> A. 0xc0000000 receives the value 3 // a1
> 0xc0000001 receives the value 2 // a2
> 0xc0000002 receives the value 3 // a3
> 0xc0000003 receives the value 0 // padding byte
>
> B. 0xc0000000 receives the value 0 // padding byte
> 0xc0000001 receives the value 3 // a3
> 0xc0000002 receives the value 2 // a2
> 0xc0000003 receives the value 3 // a1
So you are saying that the data members can end up in either
direction? The standard allows that? Would any sane compiler
manufacturer guarantee sequential ordering for POD C structs but then
put them in the opposite order for C++ classes?
> As far as byte order goes, if you are using an ARM processor, for
> example, you will get one result if you build the code in
> little-endian mode, and the other in big-endian. I'll leave it up to
> you to figure out which is which.
I didn't think endian mattered here, except for when I set the short.
If I instead had had four chars in my class, setting those bytes would
put the data in memory in a manner of the compiler's choosing. Only
when I tried to print the number out as a long would the bytes be
interpreted as little or big endian. Since this is an off board
register and I'm not using the long as anything but a data holder for
a group of bytes, endianness doesn't mean anything.
> Finally, accessing your class through a pointer to unsigned long is
> just plain undefined behavior altogether. The C++ language does not
> define, or even care, what the result it. It is possible that the
> value in the uninitialized byte if the class, or in memory outside the
> instance of the class if the compiler does not add a padding byte,
> could cause the whole thing to be a trap value for an unsigned long.
Well, it's definitely defined for a POD C struct. If the standard
doesn't define it for any other type of struct/class, then is there
still an effective standard that all compiler vendors abide by?
> > What if I had used some bitfields in my definition of A?
> Bit-fields would make matters even worse. They are among the least
> portable things of all in C or C++. The storage unit size and
> endianness of bit assignments is completely implementation-defined.
Yeah, I'm sorry. I should never have even brought bit fields up.
> But again, being an embedded systems programmer, I really want to know
> why you want to do this? What are you trying to achieve? Assuming
> you restrict the casted pointer to driver code, what extra gain do you
> perceive in attempting to hide the hardware representation inside the
> private members of a class?
As I said above, I'd like to maintain the ability to change single
board computer vendors while talking to the same hardware while having
minimal impact to the code on the SBCs.
Hiding the hardware representation lets the code be packaged with the
external hardware that can be used with different SBCs. Whoever's
reusing the external hardware can also reuse my code.
Thanks for your response.
--- Jim
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: cdotrdotwood@verizondotnet.removeme ("C Wood")
Date: Fri, 18 Apr 2003 22:23:28 +0000 (UTC) Raw View
"Jim Hunziker" <landtuna@hotmail.com> wrote in message
news:d6af99b6.0304170725.3d3c8a2b@posting.google.com...
: I'm writing an embedded application where there's a memory-mapped
: register that needs some bits set.
:
: If I have a class that looks like this:
:
: class A
: {
: private:
: unsigned char a1;
: unsigned char a2;
: unsigned short a3;
: Can I be confident that the following will work portably?
In likeness with the other poster:
Padding is a concern.
Endianess is a concern
Use placement new optional as an alternative way to do it.
foo *myfoo = new( (void) *0xc000000) foo;
:
: A foo;
: foo.set_a1(3); foo.set_a2(2); foo.set_a3(3);
: volatile unsigned long *mapped_reg = 0xc0000000;
: mapped_reg = foo;
:
: What if I had used some bitfields in my definition of A?
You would be better off to create an object in regular free store or
stack, and then reference the memory locations in the function calls. If
some of the implemenation defined aspect of your class ended up on a
hardware memory address, you're in trouble, and that's what your sample code
could cause.
:
: Thanks.
:
: --- Jim
:
: ---
: [ comp.std.c++ is moderated. To submit articles, try just posting with ]
: [ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
: [ --- Please see the FAQ before posting. --- ]
: [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
:
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: jackklein@spamcop.net (Jack Klein)
Date: Sat, 19 Apr 2003 03:17:25 +0000 (UTC) Raw View
On Fri, 18 Apr 2003 16:58:36 +0000 (UTC), landtuna@hotmail.com (Jim
Hunziker) wrote in comp.std.c++:
> Bear with me here - I'll get back to where this is relevant to
> standard C++ towards the bottom...
>=20
> jackklein@spamcop.net (Jack Klein) wrote in message news:<gtku9voqggngn=
68eddrfq5km7jjrf1cs1d@4ax.com>...
> > On Thu, 17 Apr 2003 17:46:14 +0000 (UTC), landtuna@hotmail.com (Jim
> > Hunziker) wrote in comp.std.c++:
> >=20
> > > I'm writing an embedded application where there's a memory-mapped
> > > register that needs some bits set.
>=20
> > First, why do you want to do this? What do you hope to gain by addin=
g
> > such abstraction to a low-level function like direct hardware access?
> > And why the concern with portability, are there lots of other
> > platforms with the same memory-mapped hardware device?
>=20
> This register that I've been talking about has a set of bits whose
> functionalities are unrelated. I'd like to expose the functionality
> of each one through an API which does not require bit manipulation
> expertise of the user of the API.
>=20
> So why don't I just hide the bit manipulation inside functions?=20
> Because the code ends up being much harder to read.
>=20
> a.clear();
> a.enable_execution =3D 1;
> a.set_pulse_time =3D 32;
> a.ignore_errors =3D 0;
>=20
> is a whole lot more understandable than:
>=20
> a =3D 0;
> a |=3D 0x00000001;
> a |=3D (32 << PULSE_TIME_BIT_SHIFT);
>=20
> > OK, let's make some assumptions here, none of which are actually
> > guaranteed by the language...
>=20
> Right. Sorry I didn't list mine.
>=20
> > 1. unsigned long is four bytes
>=20
> Yes.
>=20
> > 2. the data members of your class are contiguous, with no padding.
>=20
> These are two assumptions. Padding, as I said before, I'm assuming I
> can set as an option for whatever compiler I'm using. But I'm not
> sure from the standard whether I'm guaranteed data member contiguity
> and ordering for anything but C structs of POD.
>=20
> > 3. the member a1 is located at the same address as the class itself,
> > which is not guaranteed once a class or struct has access specifiers.
>=20
> But if they're all the same access specifier, can I take the address
> of the first one and assume that the others follow in order? (Again,
> I assume my bytes are packed.)
> =20
> > 4. your compiler pads your three contiguous data byte class into fou=
r
> > bytes, the same size as unsigned long, and sets the padding byte to 0.
>=20
> Well, my original example had 2 chars and a short, so it shouldn't
> need padding to get to a long.
Sorry, I missed that a3 was an unsigned short and took all 3 as
unsigned chars. That would make a few changes in the tables below:
> > Then, assuming you add missing member functions and a cast on the
> > assignment to the pointer, you have two likely results:
> >=20
> > A. 0xc0000000 receives the value 3 // a1
> > 0xc0000001 receives the value 2 // a2
0xc0000002 receives the value 0 // high 8 bits of a3
0xc0000003 receives the value 3 // low 8 bits of a3
> >=20
B. 0xc0000000 receives the value 3 // low 8 bits of a3
0xc0000001 receives the value 0 // high 8 bits of a3
> > 0xc0000002 receives the value 2 // a2
> > 0xc0000003 receives the value 3 // a1
>=20
> So you are saying that the data members can end up in either
> direction? The standard allows that? Would any sane compiler
> manufacturer guarantee sequential ordering for POD C structs but then
> put them in the opposite order for C++ classes?
Actually, I think I made a mistake here. Heck, I know I did. So
let's back up a bit, and throw out the two illustrations above
altogether. If we disregard the simple fact of undefined behavior and
the (admittedly unlikely in the real world) possibility of trap
representations, accessing the data members of your class through a
pointer to unsigned long, and copying them to a real unsigned long,
should result in a byte-by-byte copy, just as using memcpy() would.
But given the fact that one of them is a short (we'll assume 2 bytes),
that short can still be stored in two different orders, so the end
result will be either:
A. 0xc0000000 receives the value 3 // a1
0xc0000001 receives the value 2 // a2
0xc0000002 receives the value 0 // high 8 bits of a3
0xc0000003 receives the value 3 // low 8 bits of a3
....on a big endian architecture like the Motorola 68K, or:
A. 0xc0000000 receives the value 3 // a1
0xc0000001 receives the value 2 // a2
0xc0000002 receives the value 3 // low 8 bits of a3
0xc0000003 receives the value 0 // high 8 bits of a3
....on a little endian processor like a x86/Pentium. Which is why I
said:
> > As far as byte order goes, if you are using an ARM processor, for
> > example, you will get one result if you build the code in
> > little-endian mode, and the other in big-endian. I'll leave it up to
> > you to figure out which is which.
> I didn't think endian mattered here, except for when I set the short.=20
> If I instead had had four chars in my class, setting those bytes would
> put the data in memory in a manner of the compiler's choosing. Only
> when I tried to print the number out as a long would the bytes be
> interpreted as little or big endian. Since this is an off board
> register and I'm not using the long as anything but a data holder for
> a group of bytes, endianness doesn't mean anything.
>=20
> > Finally, accessing your class through a pointer to unsigned long is
> > just plain undefined behavior altogether. The C++ language does not
> > define, or even care, what the result it. It is possible that the
> > value in the uninitialized byte if the class, or in memory outside th=
e
> > instance of the class if the compiler does not add a padding byte,
> > could cause the whole thing to be a trap value for an unsigned long.
>=20
> Well, it's definitely defined for a POD C struct. If the standard
> doesn't define it for any other type of struct/class, then is there
> still an effective standard that all compiler vendors abide by?
No, sorry, it is undefined behavior in both C and C++. Really,
regardless of the data type. From the current C standard (1999):
=3D=3D=3D=3D
6.2.6 Representation of types
6.2.6.1 General=20
[snip]
Paragraph 5: Certain object representations need not represent a
value of the object type. If the stored value of an object has such a
representation and is read by an lvalue expression that does not have
character type, the behavior is undefined. If such a representation is
produced by a side effect that modifies all or any part of the object
by an lvalue expression that does not have character type, the
behavior is undefined. Such a representation is called a trap
representation.
=3D=3D=3D=3D
....and from the one-and-only C++ standard (1998):
=3D=3D=3D=3D
3.10 Lvalues and rvalues
Paragraph 15: If a program attempts to access the stored value of an
object through an lvalue of other than one of the following types the
behavior is undefined:
=97 the dynamic type of the object,
=97 a cv-qualified version of the dynamic type of the object,
=97 a type that is the signed or unsigned type corresponding to the
dynamic type of the object,
=97 a type that is the signed or unsigned type corresponding to a
cv-qualified version of the dynamic type of the object,
=97 an aggregate or union type that includes one of the aforementioned
types among its members (including, recursively, a member of a
subaggregate or contained union),
=97 a type that is a (possibly cv-qualified) base class type of the
dynamic type of the object,
=97 a char or unsigned char type.
=3D=3D=3D=3D
So in either C or C++, accessing something as an unsigned long that is
not actually a signed or unsigned long invokes undefined behavior.
>=20
> > > What if I had used some bitfields in my definition of A?
>=20
> > Bit-fields would make matters even worse. They are among the least
> > portable things of all in C or C++. The storage unit size and
> > endianness of bit assignments is completely implementation-defined.
>=20
> Yeah, I'm sorry. I should never have even brought bit fields up.
>=20
> > But again, being an embedded systems programmer, I really want to kno=
w
> > why you want to do this? What are you trying to achieve? Assuming
> > you restrict the casted pointer to driver code, what extra gain do yo=
u
> > perceive in attempting to hide the hardware representation inside the
> > private members of a class?
>=20
> As I said above, I'd like to maintain the ability to change single
> board computer vendors while talking to the same hardware while having
> minimal impact to the code on the SBCs.
>=20
> Hiding the hardware representation lets the code be packaged with the
> external hardware that can be used with different SBCs. Whoever's
> reusing the external hardware can also reuse my code.
>=20
> Thanks for your response.
>=20
> --- Jim
When you say "from any vendor using any processor", it depends on what
you really mean. If a DSP is a possibility you have real issues. A
good many of these can't address octets at all, and their bytes
(chars) are 16, 24, or 32 bits.
This may be a case where a C++ class is actually not the best way to
expose an interface to users of the device. I would suggest providing
prototypes for free-standing functions, such as:
void mydevice_clear(void);
void mydevice_enable_execution(bool enable_disable);
void mydevice_set_pulse_time(int pulse_time);
That way, you don't need to expose the inner representation at all,
even if it's private inside a class. That in turn allows you to leave
the header with the prototypes unchanged even if you need to write two
different versions of the implementation, for big- and little-endian
platforms.
In the file that implements these functions, you can define your
shadow class and its instance (completely POD) in an unnamed
namespace, or perhaps by the C method of a file scope static,
especially since many embedded compilers lag far behind standards and
might not support unnamed namespaces properly.
You can have two different definitions of the class type for
big-endian and little-endian conditional on the setting of a macro, so
you can compile it for either type of processor.
This has all the advantages of being a classic style device driver,
with free functions as the interface and all details completely
hidden. In addition, you could use "#ifdef __cplusplus" and "extern
"C"" constructs to make the header and functions compilable as C as
well, should someone want to use them that way.
Finally, since this is a standard language group after all, you can
avoid undefined behavior completely by using memcpy() or a loop
copying unsigned chars from the shadow object to the memory mapped
address after you have finished the bit-fiddling from a user function
call.
This is all really rather far afield for comp.std.c++, since even the
assumptions about "likely" behavior we have been discussing are all
either implementation-defined or undefined behavior under the C++ (or
C) language standard. If you would like to discuss this further, I
suggest you either email me or post to comp.arch.embedded instead of
continuing here where we're really off-topic.
--=20
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: allan_w@my-dejanews.com (Allan W)
Date: Wed, 23 Apr 2003 00:34:15 +0000 (UTC) Raw View
landtuna@hotmail.com (Jim Hunziker) wrote
> (I might be opening a can of worms here.)
Yes, I think so.
> Why did the authors of the C++ standard leave so much
> to be implementation-defined or undefined that has great use to
> embedded programming? Is the flexibility that allowing these
> behaviors affords really taken advantage of by compiler vendors? Or
> was it just that hammering out these issues would have delayed an
> already long-overdue standard?
First, tell me how defining this behavior would be of any particular
benefit to embedded programmers.
Please note that the C++ compiler doesn't get to determine the order
of operations, unless you do something that gives it such freedom.
foo(bar(),baz());
here we can call either bar or baz first. To solve it:
{ int bar_result(bar());
int baz_result(baz());
foo(bar_result, baz_result);
}
Now the compiler MUST call bar first.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]