EmbeddedRelated.com
Forums

How to transmit the value of an enumeration

Started by pozz October 24, 2018
enum type_t {
   TYPE_1,
   TYPE_2,
   ...
   TYPE_100,
}

void tx_byte(unsigned char c);

enum type_t t;
...
tx_byte((unsigned char)t);


If the maximum value in the enumeration is lower than 256, you can cast 
to (unsigned char) and transmit it as a byte.


However the receiver must have the same enumeration.  The transmitter 
and the receiver must agreed on *the exact values* of the enumeration.

What happens if the developer of the transmitter thinks that it is 
better to swap some values during a future release?  The enumeration 
isn't used only for transmission, it is also used for general logic of 
the software.

Until now I didn't found a good solution.  I only write some comments 
before the enumeration declaration:

/* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
  * They are also used in the transmission */

I sometimes prefer explicit the value transmitted:

if (t == TYPE_1) tx_byte(0);
if (t == TYPE_2) tx_byte(1);
...


What is your strategy?
On Wednesday, October 24, 2018 at 1:18:20 PM UTC+2, pozz wrote:
> enum type_t { > TYPE_1, > TYPE_2, > ... > TYPE_100, > } > > void tx_byte(unsigned char c); > > enum type_t t; > ... > tx_byte((unsigned char)t); > > > If the maximum value in the enumeration is lower than 256, you can cast > to (unsigned char) and transmit it as a byte. > > > However the receiver must have the same enumeration. The transmitter > and the receiver must agreed on *the exact values* of the enumeration. > > What happens if the developer of the transmitter thinks that it is > better to swap some values during a future release? The enumeration > isn't used only for transmission, it is also used for general logic of > the software. > > Until now I didn't found a good solution. I only write some comments > before the enumeration declaration: > > /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION > * They are also used in the transmission */ > > I sometimes prefer explicit the value transmitted: > > if (t == TYPE_1) tx_byte(0); > if (t == TYPE_2) tx_byte(1); > ... > > > What is your strategy?
Long answer: read the definition of "enumeration" Short answer: An enumeration is a normal type disguised and is implementation dependent. Usually is signed int (whatever it means) but you have to check your compiler doc to be sure. You can also explicitly set the value of every member of an enum: (and if you use an enum in a packet that is sent to someone else it may be a very good idea to do so) enum type_t { TYPE_1 = 34, TYPE_2 = 56, TYPE_3, TYPE_4 = 0, TYPE_5 = 0, TYPE_100, } Exercise left to the reader: what's the value of TYPE_3 and TYPE_100? If you need to send something to someone else there is a thing called "protocol" that explain in detail (hopefully) what's inside every byte you send. If for some reason the protocol changes...both transmitter and receiver needs to be modified. Bye Jack
On 24/10/18 13:18, pozz wrote:
> enum type_t { > TYPE_1, > TYPE_2, > ... > TYPE_100, > } > > void tx_byte(unsigned char c); > > enum type_t t; > ... > tx_byte((unsigned char)t); > > > If the maximum value in the enumeration is lower than 256, you can cast > to (unsigned char) and transmit it as a byte. > > > However the receiver must have the same enumeration. The transmitter > and the receiver must agreed on *the exact values* of the enumeration. > > What happens if the developer of the transmitter thinks that it is > better to swap some values during a future release? The enumeration > isn't used only for transmission, it is also used for general logic of > the software. > > Until now I didn't found a good solution. I only write some comments > before the enumeration declaration: > > /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION > * They are also used in the transmission */ > > I sometimes prefer explicit the value transmitted: > > if (t == TYPE_1) tx_byte(0); > if (t == TYPE_2) tx_byte(1); > ... > > > What is your strategy?
It doesn't matter whether you are sending "raw" integers, enumerations, text strings, lead balloons - the sender and the receiver need to agree on the protocol and the messages sent. The easiest way is to make sure things like your enumeration here are in a common header that both the receiver and the transmitter include in their code - then it is hard to get out of sync. It can also be worth including some sort of version information in the initial handshake of the protocol - any changes in the enumeration or other details should be accompanied by a change in the version number, so that the other end can see that something has changed.
Il 24/10/2018 13:45, Jack ha scritto:
> On Wednesday, October 24, 2018 at 1:18:20 PM UTC+2, pozz wrote: >> enum type_t { >> TYPE_1, >> TYPE_2, >> ... >> TYPE_100, >> } >>
[...]
>> >> What is your strategy? > > Long answer: read the definition of "enumeration" > > Short answer: > An enumeration is a normal type disguised and is implementation dependent. Usually is signed int (whatever it means) but you have to check your compiler doc to be sure.
These details are generally important, but not for my question. I don't know if C standard guarantees that, if not specified, the first value is zero and the others are contiguous. If it is guaranteed, my example above works. Otherwise, the declaration of my enumeration should have been: enum type_t { TYPE_1 = 0, TYPE_2, ... TYPE_100, }
> You can also explicitly set the value of every member of an enum: > (and if you use an enum in a packet that is sent to someone else it may be a very good idea to do so) > > enum type_t { > TYPE_1 = 34, > TYPE_2 = 56, > TYPE_3, > TYPE_4 = 0, > TYPE_5 = 0, > TYPE_100, > } > > Exercise left to the reader: what's the value of TYPE_3 and TYPE_100? > > If you need to send something to someone else there is a thing called "protocol" that explain in detail (hopefully) what's inside every byte you send. If for some reason the protocol changes...both transmitter and receiver needs to be modified.
I know that, but I often have a piece of code that manages values from a long enumeration. It's ok to swap the members or change explicitly the values of them. The code will always work while you insist using the name and not the value in the code, as it should be (otherwise what is the purpose of the enumeration?). I often need to share some info with another device (or PC) connected in some way (serial line, Ethernet, Internet...)[*]. So I often need to transfer the value of an enumeration. The simplest way would be to transfer the (explicitly or implicitly) integer value of the enumeration (here it's not important if signed or unsigned, 1 2 or 4 bytes). This is very handy, because I only need a cast. I understand this isn't the more robust way, because the enumeration could be changed in the future, broking the protocol. What is the alternative? Re-define the long enumeration with another long list of values used in the protocol only and write a long piece of code: enum proto_type_t { PROTO_TYPE_1, PROTO_TYPE_2, ... PROTO_TYPE_100 }; if (type == TYPE_1) transmit(PROTO_TYPE_1); if (type == TYPE_2) transmit(PROTO_TYPE_2); ... Most of the time, both enumerations are identical, so this long piece of error-prone code seems stupid. Another alternative is to be sure the enumeration members aren't changed in the future. But is a simple comment sufficient? [*] This will be the origin of another post in this ng.
Il 24/10/2018 15:10, David Brown ha scritto:
> On 24/10/18 13:18, pozz wrote: >> enum type_t { >> TYPE_1, >> TYPE_2, >> ... >> TYPE_100, >> } >> >> void tx_byte(unsigned char c); >> >> enum type_t t; >> ... >> tx_byte((unsigned char)t); >> >> >> If the maximum value in the enumeration is lower than 256, you can cast >> to (unsigned char) and transmit it as a byte. >> >> >> However the receiver must have the same enumeration. The transmitter >> and the receiver must agreed on *the exact values* of the enumeration. >> >> What happens if the developer of the transmitter thinks that it is >> better to swap some values during a future release? The enumeration >> isn't used only for transmission, it is also used for general logic of >> the software. >> >> Until now I didn't found a good solution. I only write some comments >> before the enumeration declaration: >> >> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION >> * They are also used in the transmission */ >> >> I sometimes prefer explicit the value transmitted: >> >> if (t == TYPE_1) tx_byte(0); >> if (t == TYPE_2) tx_byte(1); >> ... >> >> >> What is your strategy? > > It doesn't matter whether you are sending "raw" integers, enumerations, > text strings, lead balloons - the sender and the receiver need to agree > on the protocol and the messages sent. The easiest way is to make sure > things like your enumeration here are in a common header that both the > receiver and the transmitter include in their code - then it is hard to > get out of sync. > > It can also be worth including some sort of version information in the > initial handshake of the protocol - any changes in the enumeration or > other details should be accompanied by a change in the version number, > so that the other end can see that something has changed.
The problem here is not to share a new version of the protocol, but to avoid that a developer could change some part of the code (an enumeration) and broke the protocol *inadvertently*.
On 24/10/18 15:27, pozz wrote:
> Il 24/10/2018 15:10, David Brown ha scritto: >> On 24/10/18 13:18, pozz wrote: >>> enum type_t { >>> TYPE_1, >>> TYPE_2, >>> ... >>> TYPE_100, >>> } >>> >>> void tx_byte(unsigned char c); >>> >>> enum type_t t; >>> ... >>> tx_byte((unsigned char)t); >>> >>> >>> If the maximum value in the enumeration is lower than 256, you can cast >>> to (unsigned char) and transmit it as a byte. >>> >>> >>> However the receiver must have the same enumeration. The transmitter >>> and the receiver must agreed on *the exact values* of the enumeration. >>> >>> What happens if the developer of the transmitter thinks that it is >>> better to swap some values during a future release? The enumeration >>> isn't used only for transmission, it is also used for general logic of >>> the software. >>> >>> Until now I didn't found a good solution. I only write some comments >>> before the enumeration declaration: >>> >>> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION >>> * They are also used in the transmission */ >>> >>> I sometimes prefer explicit the value transmitted: >>> >>> if (t == TYPE_1) tx_byte(0); >>> if (t == TYPE_2) tx_byte(1); >>> ... >>> >>> >>> What is your strategy? >> >> It doesn't matter whether you are sending "raw" integers, enumerations, >> text strings, lead balloons - the sender and the receiver need to agree >> on the protocol and the messages sent. The easiest way is to make sure >> things like your enumeration here are in a common header that both the >> receiver and the transmitter include in their code - then it is hard to >> get out of sync. >> >> It can also be worth including some sort of version information in the >> initial handshake of the protocol - any changes in the enumeration or >> other details should be accompanied by a change in the version number, >> so that the other end can see that something has changed. > > The problem here is not to share a new version of the protocol, but to > avoid that a developer could change some part of the code (an > enumeration) and broke the protocol *inadvertently*.
That is a people problem, not a technical one.
On 24/10/18 15:18, pozz wrote:
> Il 24/10/2018 13:45, Jack ha scritto: >> On Wednesday, October 24, 2018 at 1:18:20 PM UTC+2, pozz wrote: >>> enum type_t { >>> TYPE_1, >>> TYPE_2, >>> ... >>> TYPE_100, >>> } >>> > [...] >>> >>> What is your strategy? >> >> Long answer: read the definition of "enumeration" >> >> Short answer: >> An enumeration is a normal type disguised and is implementation >> dependent. Usually is signed int (whatever it means) but you have to >> check your compiler doc to be sure. > > These details are generally important, but not for my question. I don't > know if C standard guarantees that, if not specified, the first value is > zero and the others are contiguous. If it is guaranteed, my example > above works. Otherwise, the declaration of my enumeration should have > been: >
Enumeration constants in C are always of type "int", unless your compiler has non-conforming modes that it documents and that you actively use. (In C++, you can specify the underlying type and the enumeration itself is a type.) The first enumerator has value 0 if it is not given explicitly. Each other enumerator is one more than the previous enumerator, if there is no explicit value given. When giving enumerators explicitly, it is fine to have duplicates and overlaps.
On Wed, 24 Oct 2018 13:18:15 +0200, pozz wrote:

> enum type_t { > TYPE_1, > TYPE_2, > ... > TYPE_100, > } > > void tx_byte(unsigned char c); > > enum type_t t; > ... > tx_byte((unsigned char)t); > > > If the maximum value in the enumeration is lower than 256, you can cast > to (unsigned char) and transmit it as a byte. > > > However the receiver must have the same enumeration. The transmitter > and the receiver must agreed on *the exact values* of the enumeration. > > What happens if the developer of the transmitter thinks that it is > better to swap some values during a future release? The enumeration > isn't used only for transmission, it is also used for general logic of > the software. > > Until now I didn't found a good solution. I only write some comments > before the enumeration declaration: > > /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION > * They are also used in the transmission */ > > I sometimes prefer explicit the value transmitted: > > if (t == TYPE_1) tx_byte(0); > if (t == TYPE_2) tx_byte(1); > ... > > > What is your strategy?
Your protocol document defines what is sent on the communications channel. In your code you may have: enum type_t { TYPE_2, TYPE_1, ... TYPE_100 } But in your protocol handler you have enum protocol_type_t { PTYPE_1, PTYPE_2, ... PTYPE_100 } switch(t) { case TYPE_1: tx_byte(PTYPE_1); .... You convert your internal type_t to the external protocol protocol_type_t and protocol_type_t always (well should always) matches the spec. If the code passes a type_t that does not match a protocol_type_t you have a error and about to violate the protocol - logs and alarms happen. It's up to the receiver to decide what to do if a protocol_type_t does not match a value it can deal with. If your protocol says protocol_type_t is sent as 8 bits I think it's a good idea to fill out the enum with all 256 possibilities defined. PTYPE_100, SPARE_PTYPE_101, SPARE_PTYPE_102, ... The protocol doc defines what PTYPE_1 to PTYPE_100 mean and notes 101 to max value are reserved for future use. -- Chisolm Republic of Texas
On Wed, 24 Oct 2018 13:18:15 +0200, pozz <pozzugno@gmail.com> wrote:

>enum type_t { > TYPE_1, > TYPE_2, > ... > TYPE_100, >} > >void tx_byte(unsigned char c); > >enum type_t t; >... >tx_byte((unsigned char)t); > > >If the maximum value in the enumeration is lower than 256, you can cast >to (unsigned char) and transmit it as a byte.
And if not you can agree to use a "network" interchange format. IP specifies big-endian byte order for binary integers, and socket libraries provide the functions: htons - host to network short htonl - host to network long ntohs - network to host short ntohl - network to host long for conversions between host and network formats. If the host is big-endian, these functions are no-ops. [Also sometimes htonll and ntohll (longlong for 64-bit data.]
>However the receiver must have the same enumeration. The transmitter >and the receiver must agreed on *the exact values* of the enumeration. > >What happens if the developer of the transmitter thinks that it is >better to swap some values during a future release? The enumeration >isn't used only for transmission, it is also used for general logic of >the software.
If this really is an issue, use indexed tables instead of an enumerations and make exchange/distribution of the tables part of your protocol. YMMV, George
Il 24/10/2018 19:48, Joe Chisolm ha scritto:
 > On Wed, 24 Oct 2018 13:18:15 +0200, pozz wrote:
 >
 >> enum type_t {
 >>     TYPE_1,
 >>     TYPE_2,
 >>     ...
 >>     TYPE_100,
 >> }
 >>
 >> void tx_byte(unsigned char c);
 >>
 >> enum type_t t;
 >> ...
 >> tx_byte((unsigned char)t);
 >>
 >>
 >> If the maximum value in the enumeration is lower than 256, you can cast
 >> to (unsigned char) and transmit it as a byte.
 >>
 >>
 >> However the receiver must have the same enumeration.  The transmitter
 >> and the receiver must agreed on *the exact values* of the enumeration.
 >>
 >> What happens if the developer of the transmitter thinks that it is
 >> better to swap some values during a future release?  The enumeration
 >> isn't used only for transmission, it is also used for general logic of
 >> the software.
 >>
 >> Until now I didn't found a good solution.  I only write some comments
 >> before the enumeration declaration:
 >>
 >> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
 >>    * They are also used in the transmission */
 >>
 >> I sometimes prefer explicit the value transmitted:
 >>
 >> if (t == TYPE_1) tx_byte(0);
 >> if (t == TYPE_2) tx_byte(1);
 >> ...
 >>
 >>
 >> What is your strategy?
 >
 > Your protocol document defines what is sent on the communications 
channel.
 > In your code you may have:
 >   enum type_t {
 >      TYPE_2,
 >      TYPE_1,
 >      ...
 >      TYPE_100
 >   }
 >
 > But in your protocol handler you have
 >   enum protocol_type_t {
 >      PTYPE_1,
 >      PTYPE_2,
 >      ...
 >      PTYPE_100
 >   }
 > switch(t) {
 > case TYPE_1:
 >    tx_byte(PTYPE_1);
 >
 > .....
 >
 > You convert your internal type_t to the external protocol protocol_type_t
 > and protocol_type_t always (well should always) matches the spec.

Yes, it seems *THE* best solution in order to avoid problems.  Even if 
it is tedious, because you have two enumerations instead of one and you 
have a long piece of translation code that is silly.