#defines and strings

G

GMM50

Hello:

I am working on a design that has a large number of messages. These
messages are saved in the system and used by the system. I need to
convert from message names to internal storage location.

I will force a naming convention something like names from SM1001 to
SM1999. Not all SMxxxx numbers are used. And as the design changes
old SMxxxx will be deleted and new ones added.

In the code I use these messages as constants as:
PlayMsg(SM1023, OutputChannel, Volume);
So I'm planning to use a define to convert SM1023 to the internal
storage number (= location).

#define SM1023 19 // Hello Message

But then when the user downloads a message I need to programatically
determine it's internal storage number (= location).

So a table of structures
struct MSG_CVRT {
int msg;
int loc;
}

struct MSG_CVRG MsgTbl[] = {
1023, 19; // Hello Message
0, 0;
}

And I can look for MsgTbl.msg and then extract the location.

My problem is I now have data in two places and they will get out of
sync. Anyone come across this issue and have advice.

This is an embedded system so memory usage and speed of operation are
considered.

Thanks in advance
george
 
W

Walter Roberson

In the code I use these messages as constants as:
PlayMsg(SM1023, OutputChannel, Volume);
So I'm planning to use a define to convert SM1023 to the internal
storage number (= location).
#define SM1023 19 // Hello Message
But then when the user downloads a message I need to programatically
determine it's internal storage number (= location).
struct MSG_CVRG MsgTbl[] = {
1023, 19; // Hello Message
0, 0;
}
My problem is I now have data in two places and they will get out of
sync.


Perhaps I'm missing something, but why not use

struct MSG_CVRG MsgTbl[] = {
1023, SM1023, // Hello Message
0, 0,
};

(Note: semicolons converted to commas)


Potentially you might also want to consider

#define PASTE(T1,T1) T1##T2
#define DclMsg(N) {N, PASTE(SM,N)}

struct MSG_CVRG MsgTbl[] = {
DclMsg(1023),
DclMsg(1196),
DclMsg(1197),
{0, 0},
};


Also, you might not need the trailing {0, 0} as you can take
sizeof(MsgTabl) / sizeof(struct MSG_CVRG)
to find out how many elements there are.
 
E

Eric Sosman

GMM50 said:
Hello:

I am working on a design that has a large number of messages. These
messages are saved in the system and used by the system. I need to
convert from message names to internal storage location.

I will force a naming convention something like names from SM1001 to
SM1999. Not all SMxxxx numbers are used. And as the design changes
old SMxxxx will be deleted and new ones added.

In the code I use these messages as constants as:
PlayMsg(SM1023, OutputChannel, Volume);
So I'm planning to use a define to convert SM1023 to the internal
storage number (= location).

#define SM1023 19 // Hello Message

But then when the user downloads a message I need to programatically
determine it's internal storage number (= location).

So a table of structures
struct MSG_CVRT {
int msg;
int loc;
}

struct MSG_CVRG MsgTbl[] = {
1023, 19; // Hello Message
0, 0;
}

And I can look for MsgTbl.msg and then extract the location.

My problem is I now have data in two places and they will get out of
sync. Anyone come across this issue and have advice.


I'm not 100% sure I understand exactly what happens when
"the user downloads a message." The download must be tagged
with some kind of identifier, which I'm guessing is the numeric
value 1023 -- if that's not it, then just disregard the rest
of this message.

Your problem (if I've understood it) is how to keep the
#define and the translation table synchronized, that is, how
to be sure that if either 1023 or 19 changes it changes in
both places simultaneously.

One way is to eliminate the #define, or to replace it
with something vacuous like `#define SM1023 1023'. Functions
like PlayMsg() could be changed so they didn't understand the
first argument as a "storage number," but as a value to be
looked up in MsgTbl[]. According to your description there
will be fewer than one thousand entries in the table, so the
lookup cost will be small (binary search will average fewer
than nine probes).

Another way is to retain both, but to generate them from
a single common source. For example, you might treat MsgTbl[]
as The Authoritative Word, and run a little "helper" program
to generate a .h file from the table's contents. For more
complicated situations, the "helper" could read some other
kind of data file and generate both the #define lines and the
content of MsgTbl[], but that might be overkill in your case.
 
G

GMM50

I'm not 100% sure I understand exactly what happens when
"the user downloads a message." The download must be tagged
with some kind of identifier, which I'm guessing is the numeric
value 1023 -- if that's not it, then just disregard the rest
of this message.

I need to convert from the message being downloaded (ie SME1023) into
the internal numbering system and that message name comes in the
download port as a text string.
Your problem (if I've understood it) is how to keep the
#define and the translation table synchronized, that is, how
to be sure that if either 1023 or 19 changes it changes in
both places simultaneously.
Exactly.

One way is to eliminate the #define, or to replace it
with something vacuous like `#define SM1023 1023'. Functions
like PlayMsg() could be changed so they didn't understand the
first argument as a "storage number," but as a value to be
looked up in MsgTbl[]. According to your description there
will be fewer than one thousand entries in the table, so the
lookup cost will be small (binary search will average fewer
than nine probes).

That works for the internal playing of messages but not the donwload
event.
Another way is to retain both, but to generate them from
a single common source. For example, you might treat MsgTbl[]
as The Authoritative Word, and run a little "helper" program
to generate a .h file from the table's contents. For more
complicated situations, the "helper" could read some other
kind of data file and generate both the #define lines and the
content of MsgTbl[], but that might be overkill in your case.

I've come to the same conclusion. I have a .h file

#define SME1001 2003 // Good Morning Welcome message

And then a program that converts the defines into a table. So one file
contains all the facts. I'm also considering putting the conversion
code in the product as one less it item forget about.

thanks
 
E

Eric Sosman

GMM50 said:
Eric said:
[...]
One way is to eliminate the #define, or to replace it
with something vacuous like `#define SM1023 1023'. Functions
like PlayMsg() could be changed so they didn't understand the
first argument as a "storage number," but as a value to be
looked up in MsgTbl[]. According to your description there
will be fewer than one thousand entries in the table, so the
lookup cost will be small (binary search will average fewer
than nine probes).

That works for the internal playing of messages but not the donwload
event.

I don't see why not: it's exactly the mechanism you described
in your original message! The user downloads a message tagged
with the string SM1023; you extract the number 1023 from this
and look it up in MsgTbl[] to learn the "storage number." My
suggestion amounts to always trafficking in the SMxxxx numbers,
forcing *everybody* to go through MsgTbl[] and eliminating all
other routes. If there's only one route, you don't need to
worry two different routes having different destinations.
 
G

GMM50

I agree with you on the download case when the SM1234 is presented as
data on a download port.

The other use is of these names in in the code when I try to use them
in PlayMsg(SM1023, OutputChannel, Volume);

In the above example the messages names are used as defines which can't
go through the MsgTbl[].

I could use PlayMsg("SM1023", OutputChannel, Volume);
But then I need to convert "SM1023" to a numberic and use the table for
each call to PlayMsg(). Larger slower code but then only one
controling MsgTbl[].

Am i missing something. Wouldn't be the first time.
your thoughts.
 
E

Eric Sosman

GMM50 said:
I agree with you on the download case when the SM1234 is presented as
data on a download port.

The other use is of these names in in the code when I try to use them
in PlayMsg(SM1023, OutputChannel, Volume);

In the above example the messages names are used as defines which can't
go through the MsgTbl[].

I could use PlayMsg("SM1023", OutputChannel, Volume);
But then I need to convert "SM1023" to a numberic and use the table for
each call to PlayMsg(). Larger slower code but then only one
controling MsgTbl[].

Am i missing something. Wouldn't be the first time.
your thoughts.

Perhaps I'm the one who's missing something (it won't
be the last time). In your original post, you wrote
So a table of structures
struct MSG_CVRT {
int msg;
int loc;
}

struct MSG_CVRG MsgTbl[] = {
1023, 19; // Hello Message
0, 0;
}

And I can look for MsgTbl.msg and then extract the location.


.... which means you know how to take the number 1023 and
find the corresponding "location" 19. Fine. Now, your
current implementation of PlayMsg() probably looks like

void PlayMsg(int loc, channel_t chan, volume_t vol) {
/* machine-specific code to play the message
* at loc on channel chan at volume vol
*/
}

I'm suggesting you change the way PlayMsg() is defined and
implemented, so that the first argument is not the "location"
but the message ID. You'd have

void PlayMsg(int msg, channel_t chan, volume_t vol) {
int loc;
loc = look_up_in_MsgTbl(msg);
/* machine-specific code as before, using
* the loc value from MsgTbl[]
*/
}

(If PlayMsg() isn't something you can modify, write a
"wrapper" function like MessagePlay() that accepts a
message ID, looks it up in MsgTbl[] to find the "location,"
and calls PlayMsg() with the result. Forbid any calls to
PlayMsg() except the one in MessagePlay() itself.)

The benefit is that there's now only one "designator"
for a message (the 1023 of your example) rather than two
(1023 and 19). PlayMsg() and related low-level functions
like StoreNewMsg() are the only users of 19, and they are
never handed it directly but always get it by looking up
1023 in MsgTbl[]. Thus, MsgTbl[] is the one and only
place where the 1023<->19 connection is made, and you need
never worry about two "authorities" that might disagree.
MsgTbl[] is always in step with itself.

The drawback is that you must spend cycles performing
the lookup every time you play (or do anything else with)
a message. Lookup time in a table with no more than 1000
entries is almost certainly trivial; the 4MHz 8-bit Z80
machine I had twenty years ago could have done it in less
than 200 microseconds, maybe less than 100. I seriously
doubt that this "drawback" is in the least bit important.
 

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

Similar Threads


Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top