Bartc
4/9/2011 10:10:00 AM
"pozz" <pozzugno@gmail.com> wrote in message
news:277ccf24-f849-46e1-ab6e-4574007503a9@s9g2000yqm.googlegroups.com...
> In C I can have a small set of numeric type variables: char, short,
> int and long (ignoring long long), and their unsigned counterpart.
> In one of my program, I have a lot of (about 200) numeric variables of
> different types, signed and unsigned.
You say (elsewhere) that these are in a different module that you can't
change?
How many type combinations are there in all? (You say in yet another post
that you are only presenting a small part of the overall problem.)
> Now I want to create a generic function that shows the value of a
> variable on the display with a format string dependent on the
> variable, increase/decrease it inside a range dependent on the
> variable and set it the new value.
>
> I create the following code... do you have any suggestions or
> improvements? It seems to me too complex to read (a lot of if(p-
>>type)... )
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <limits.h>
>
> signed char sc = -1;
> unsigned char uc = UCHAR_MAX;
> signed short ss = -1;
> unsigned short us = SHRT_MAX;
> signed int si = -1;
> unsigned int ui = UINT_MAX;
> signed long int sl = -1;
> unsigned long int ul = ULONG_MAX;
>
> typedef struct {
> enum {
> NUM_TYPE_SCHAR,
> NUM_TYPE_UCHAR,
> NUM_TYPE_SSHRT,
> NUM_TYPE_USHRT,
> NUM_TYPE_SINT,
> NUM_TYPE_UINT,
> NUM_TYPE_SLONG,
> NUM_TYPE_ULONG,
> } type;
> void *ptr;
> const char *fmt;
> union {
> signed char min_sc;
> unsigned char min_uc;
> signed short min_ss;
> unsigned short min_us;
> signed int min_si;
> unsigned int min_ui;
> signed long min_sl;
> unsigned long min_ul;
> } min;
> union {
> signed char max_sc;
> unsigned char max_uc;
> signed short max_ss;
> unsigned short max_us;
> signed int max_si;
> unsigned int max_ui;
> signed long max_sl;
> unsigned long max_ul;
> } max;
> } num_t;
Declare the enum stuff separately. That then suggests the type tag names can
be used anywhere, rather than belonging to 'num_t'.
From your other examples, it seems the lower/upper limits can be different
for every variable, rather than every type.
> /* Just some example variables */
> num_t num_sc = { NUM_TYPE_SCHAR, &sc, "%d degrees", { .min_sc = -10 },
> { .max_sc = 10 } };
> num_t num_uc = { NUM_TYPE_UCHAR, &uc, "%u apples", { .min_sc = 0 },
> { .max_sc = UCHAR_MAX } };
> num_t num_ss = { NUM_TYPE_SSHRT, &ss, "%d points", { .min_ss = -10 },
> { .max_ss = 10 } };
> num_t num_us = { NUM_TYPE_USHRT, &us, "%u meters", { .min_ss = 0 },
> { .max_ss = USHRT_MAX } };
> num_t num_si = { NUM_TYPE_SINT, &si, "%d dollars", { .min_si = -10 },
> { .max_si = 10 } };
> num_t num_ui = { NUM_TYPE_UINT, &ui, "%u seconds", { .min_si = 0 },
> { .max_si = UINT_MAX } };
> num_t num_sl = { NUM_TYPE_SLONG, &sl, "%d depth", { .min_sl = -10 },
> { .max_sl = 10 } };
> num_t num_ul = { NUM_TYPE_ULONG, &ul, "%u km", { .min_sl = 0 },
> { .max_sl = ULONG_MAX } };
>
> void
> num_print(num_t *num)
> {
> if (num->type == NUM_TYPE_SCHAR) {
> printf (num->fmt, *(signed char *)num->ptr);
> } else if (num->type == NUM_TYPE_UCHAR) {
> printf (num->fmt, *(unsigned char *)num->ptr);
> } else if (num->type == NUM_TYPE_SSHRT) {
> printf (num->fmt, *(signed short *)num->ptr);
> } else if (num->type == NUM_TYPE_USHRT) {
> printf (num->fmt, *(unsigned short *)num->ptr);
> }
> /* ... */
> }
That should be a switch statement here, rather than a chain of
if-statements.
But in any case, there seems to much code going on, when you could be using
tables.
To summarise, you have:
* About 200 variables somewhere in your program
* You know, statically, the addresses and types of these variables
* Each variable can be 1, 2, 4 or 8 bytes, and can be signed or unsigned
* Each variable can have an upper and lower limit, expressed as the same
type as the variable
This suggests you might have a 200-entry table for the variables, but it's
not clear how these variables are used in your project. If there are
variables, x, y, and z, would it be feasible to create corresponding indices
x_num, y_num, and z_num, which are used to fetch info from the table.
For printing values, you can use a table of format strings, indexed by the
type tag of a variable.
For extracting the value of a variable, you need a function that takes a
variable index, and expands it's value (1, 2, 4 or 8 bytes, signed or
unsigned) to 8 bytes, if that is the maximum width. (If the majority of your
variables are smaller, then can be inefficient, if your processor is
16-bits.)
And for updating a variable, the same sort of thing. In other words, use a
pair get/set functions:
setvariable(var_index, x);
x = getvariable(var_index);
Where x must be the largest width in use. I don't think it matters whether x
is signed or unsigned.
Limit checks can be done inside the set-function, or elsewhere.
An increment function might look like:
void increment(int v){
unsigned long x;
x=get_variable(v);
if (x<varupper[v]) set_variable(x+1);
}
If some limits are in between max of signed long, and max of unsigned long,
or some upper limits are negative, then you need to treat signed/unsigned
values differently here. However, if the limit check is built-in to
set_variable (which will itself deal with signedness), then increment can be
just:
set_variable(v,get_variable(v)+1);
Copying one variable to another can be done using set/get, or, if
compatible, using memcpy, using a table of sizes (1 to 8 bytes), indexed by
the variable's type tag.
Alternatively, if there are really only 8 type combinations, you can just
have 8 lots of functions! This would be simplest, and likely fastest.
However this business of every variable having customised upper/lower limits
doesn't quite comfortably into such a scheme; possibly I've misunderstood
that.
--
Bartc