[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

A generic interface for numeric variables

pozz

4/4/2011 3:39:00 PM

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.

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;

/* 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);
}
/* ... */
}
void
num_increase(num_t *num)
{
if (num->type == NUM_TYPE_SCHAR) {
signed char value = *(signed char *)num->ptr;
if (value < num->max.max_sc) {
*(signed char *)(num->ptr) = ++value;
}
} else if (num->type == NUM_TYPE_UCHAR) {
unsigned char value = *(unsigned char *)num->ptr;
if (value < num->max.max_uc) {
*(unsigned char *)(num->ptr) = ++value;
}
} else if (num->type == NUM_TYPE_SSHRT) {
signed short value = *(signed short *)num->ptr;
if (value < num->max.max_ss) {
*(signed short *)(num->ptr) = ++value;
}
} else if (num->type == NUM_TYPE_USHRT) {
unsigned short value = *(unsigned short *)num->ptr;
if (value < num->max.max_us) {
*(unsigned short *)(num->ptr) = ++value;
}
}
/* ... */
}

void
num_decrease(num_t *num)
{
if (num->type == NUM_TYPE_SCHAR) {
signed char value = *(signed char *)num->ptr;
if (value > num->min.min_sc) {
*(signed char *)(num->ptr) = --value;
}
} else if (num->type == NUM_TYPE_UCHAR) {
unsigned char value = *(unsigned char *)num->ptr;
if (value > num->min.min_uc) {
*(unsigned char *)(num->ptr) = --value;
}
} else if (num->type == NUM_TYPE_SSHRT) {
signed short value = *(signed short *)num->ptr;
if (value > num->min.min_ss) {
*(signed short *)(num->ptr) = --value;
}
} else if (num->type == NUM_TYPE_USHRT) {
unsigned short value = *(unsigned short *)num->ptr;
if (value > num->min.min_us) {
*(unsigned short *)(num->ptr) = --value;
}
}
/* ... */
}

void
num_cpyvalue(num_t *dst, const num_t *src)
{
if (dst->type == NUM_TYPE_SCHAR) {
*(signed char *)dst->ptr = *(signed char *)src->ptr;
} else if (dst->type == NUM_TYPE_UCHAR) {
*(unsigned char *)dst->ptr = *(unsigned char *)src->ptr;
} else if (dst->type == NUM_TYPE_SSHRT) {
*(signed short *)dst->ptr = *(signed short *)src->ptr;
} else if (dst->type == NUM_TYPE_USHRT) {
*(unsigned short *)dst->ptr = *(unsigned short *)src->ptr;
}
/* ... */
}

void
num_change(num_t *num)
{
char c;
num_t num_new = *num;
signed char new_sc;
unsigned char new_uc;
signed short new_ss;
unsigned short new_us;
/* ... */

if (num->type == NUM_TYPE_SCHAR) {
new_sc = *(signed char *)num->ptr;
num_new.ptr = &new_sc;
} else if (num->type == NUM_TYPE_UCHAR) {
new_uc = *(unsigned char *)num->ptr;
num_new.ptr = &new_uc;
} else if (num->type == NUM_TYPE_SSHRT) {
new_ss = *(signed short *)num->ptr;
num_new.ptr = &new_ss;
} else if (num->type == NUM_TYPE_USHRT) {
new_us = *(unsigned short *)num->ptr;
num_new.ptr = &new_us;
}

printf("The value is: ");
num_print(num);
putchar('\n');

printf ("Press U/D/O: ");
do {
c = getchar();
if (c == '\n') {
continue;
} else if ((c == 'U') || (c == 'u')) {
num_increase (&num_new);
} else if ((c == 'D') || (c == 'd')) {
num_decrease (&num_new);
} else if ((c == 'O') || (c == 'o')) {
break;
}
printf("The value is: ");
num_print(&num_new);
putchar('\n');
printf ("Press U/D/O: ");
} while ((c != 'O') && (c != 'o'));
num_cpyvalue (num, &num_new);
}

int
main (int argc, char *argv[])
{
num_change (&num_sc);
printf ("The new value is: ");
num_print(&num_sc);
putchar('\n');
return 0;
}
10 Answers

John Doe

4/4/2011 7:35:00 PM

0

On Mon, 04 Apr 2011 08:39:14 -0700, pozz wrote:

> I create the following code... do you have any suggestions or
> improvements?

Use C++ ;)


pozz

4/5/2011 9:05:00 AM

0

On 4 Apr, 21:34, Nobody <nob...@nowhere.com> wrote:
> On Mon, 04 Apr 2011 08:39:14 -0700, pozz wrote:
> > I create the following code... do you have any suggestions or
> > improvements?
>
> Use C++ ;)

Unfortunately I can't :-)

Eric Sosman

4/6/2011 1:31:00 AM

0

On 4/4/2011 11:39 AM, pozz wrote:
> 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.
>
> 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)... )
> [...]

I agree: It's too complex. Let's try to simplify, shall we?

First, I see no need for all of `signed char', `signed short',
`signed int', and `signed long': Just let `signed long' stand in
for the whole batch. You're placing upper and lower bounds on
each value anyway, so if the value's bounds are within the range
of `signed short' they are also within the range of `signed long'.
Thanks to the bounds you will never over- or underflow, so you
needn't worry about `signed char' and `signed int' behaving
differently with out-of-range results; the out-of-range results
will never occur in the first place.

Similar remarks apply to the `unsigned' variants. Since the
wrap-around behavior of `unsigned' arithmetic is well-defined, there
are circumstances where `unsigned short' and `unsigned long' will in
fact behave differently but reliably. However, your own upper and
lower bounds still lurk in the background: If you never try to go
below the lower nor above the upper, you never encounter wrap-around
situations anyhow. So if wrap-around doesn't happen, you don't rely
on `unsigned char' and `unsigned int' having different ranges, and
you can use `unsigned long' as a proxy for the entire suite.

Okay, we've simplified by reducing eight possibilities to just
two. (Somebody's sure to say we're wasting space by using a `long'
where a `char' would suffice, but any such Somebody hasn't been
paying attention: Your present scheme uses a `union' of all the
candidate types, so you use a `long's worth of space even for a
`char' variable. Using `long' for everything takes no more space
than you're already using.)

You could go ahead with a three-quarters-reduced version of
your current scheme, but now that the possibilities are down to two
(signed vs. unsigned) I'd be strongly tempted to say "Two is a
manageable number; I'll handle the two types separately." This is
optional, of course, but it has the advantage of eliminating all
those `if's you find so objectionable. You'd get something like

typedef struct {
signed long val;
signed long min;
signed long max;
} Signed;

typedef struct {
unsigned long val;
unsigned long min;
unsigned long max;
} Unsigned;

void increaseSigned(Signed *thing);
...
void decreaseUnsigned(Unsigned *thing);
...

Signed celsius = { 0, -273, LONG_MAX };
Unsigned kelvin = { 273, 0, ULONG_MAX );
...

increaseSigned(&celsius);
decreaseUnsigned(&kelvin);

This has the disadvantage that you must remember whether to use
the signedXxx() or unsignedXxx() function -- but then, the compiler
will tell you if you get it wrong. With two possibilities instead of
eight this seems not too great a burden, but if you dislike even that
much you can always stick a type code into the `struct', as in your
original scheme.

Another thing to consider is that the range limits probably aren't
specific to each variable instance, but to whatever the variable's
value measures. It's so-and-so many splonders in a bank account, or
so-and-so many poods per dunam of fertilizer, or so-and-so many
centicenturies of post-secondary education. This suggests separating
the ranges from the values, leading to a rather different scheme:

typedef struct {
signed long min;
signed long max;
} SignedRange;

typedef struct {
unsigned long min;
unsigned long max;
} UnsignedRange;

signed long increaseSigned(signed long value,
const SignedRange *range);
...
unsigned long decreaseUnsigned(unsigned long value,
const UnsignedRange *range);
...

const SignedRange celsiusRange = { -273, LONG_MAX };
const UnsignedRange kelvinRange = { 0, ULONG_MAX };
...

signed long celsius = 0;
unsigned long kelvin = 273;
...

celsius = increaseSigned(celsius, &celsiusRange);
kelvin = decreaseSigned(kelvin, &kelvinRange);

Note that this works (with suitable ranges) even if `celsius'
is `signed short' and `kelvin' is `unsigned char': C's argument
promotions and assignment conversions allow you to use the `long'
functions with narrower quantities. Of course, you *can* stick
with per-instance ranges, if that makes more sense for your problem,
but per-"scale" ranges may turn out to be more convenient.

Summary: Three-quarters of your complexity can be done away with
at one stroke. You may choose to live with what remains, or to adopt
a slightly different API.

--
Eric Sosman
esosman@ieee-dot-org.invalid

pozz

4/7/2011 10:33:00 AM

0

On 6 Apr, 03:30, Eric Sosman <esos...@ieee-dot-org.invalid> wrote:
> [...]
> Okay, we've simplified by reducing eight possibilities to just
> two. (Somebody's sure to say we're wasting space by using a `long'
> where a `char' would suffice, but any such Somebody hasn't been
> paying attention: Your present scheme uses a `union' of all the
> candidate types, so you use a `long's worth of space even for a
> `char' variable. Using `long' for everything takes no more space
> than you're already using.)

The approach I presented is a small variation of the one I'm really
using.
I need to declare const structures to save precious RAM space on
my embedded platform, but I can't initialize whatever member of
unions/structs (a limit of the compiler). So my real scheme doesn't
use unions but a struct/substruct approach:

typedef struct {
signed char *ptr;
signed char min;
signed char max;
} scnum_t;

typedef struct {
unsigned char *ptr;
unsigned char min;
unsigned char max;
} ucnum_t;

/*...*/

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 *subnum; /* Pointer to scnum_t, ucnum_t, ... */
} num_t;

In this case I can write:

signed char temp; /* The variable */
const scnum_t snTemperature = { &temp, -10, +10 };
const num_t nTemperature = { NUM_TYPE_SCHAR, &snTemperature };

With an additional pointer (subnum in num_t), I can declare min/max
values as the correct type, so without losing space declaring long or
unsigned long min/max.
Most of the variables have small values, so they are already declared
as signed/unsigned char/short. In that case, on a 16-bit platform
(like the one I'm using), I need one num_t struct (4 bytes) and one
"subnum" struct (4 bytes).
If subnum types are incorporated into num_t with min/max as long, I
need only one num_t struct (12 bytes) for all variables.
With my approach I can save 4 bytes for each "small values" variable.

Anyway this isn't a very big problem, because I have a lot of "const"
space in non-volatile memory.


> You could go ahead with a three-quarters-reduced version of
> your current scheme, but now that the possibilities are down to two
> (signed vs. unsigned) I'd be strongly tempted to say "Two is a
> manageable number; I'll handle the two types separately." This is
> optional, of course, but it has the advantage of eliminating all
> those `if's you find so objectionable. You'd get something like
>
> typedef struct {
> signed long val;
> signed long min;
> signed long max;
> } Signed;
>
> typedef struct {
> unsigned long val;
> unsigned long min;
> unsigned long max;
> } Unsigned;

I can't do in this way. The variables are also declared and used in
several parts of the code and in different modules I can't touch.
So the numeric value can't reside inside Signed/Unsigned struct.
I have to use a pointer to the variable.

typedef struct {
enum {
NUM_TYPE_SCHAR,
NUM_TYPE_SSHRT,
NUM_TYPE_SINT,
NUM_TYPE_SLONG
} type;
void *ptr; /* Pointer to signed char/short/int as in type
*/
signed long min;
signed long max;
} Signed;

typedef struct {
enum {
NUM_TYPE_UCHAR,
NUM_TYPE_USHRT,
NUM_TYPE_UINT,
NUM_TYPE_ULONG
} type;
void *ptr; /* Pointer to unsigned char/short/int as in
type */
unsigned long min;
unsigned long max;
} Unsigned;

But, in this case, the compiler can't give me warning if I wrongly
assign to ptr member of Signed/Unsigned struct a variable with a
different type as indicated in the type member.
Also this can be a minor problem (in my struct/substruct approach
above, I have the same problem with subnum that is void*).


> [...]
> Another thing to consider is that the range limits probably aren't
> specific to each variable instance, but to whatever the variable's
> value measures. It's so-and-so many splonders in a bank account, or
> so-and-so many poods per dunam of fertilizer, or so-and-so many
> centicenturies of post-secondary education. This suggests separating
> the ranges from the values, leading to a rather different scheme:
>
> typedef struct {
> signed long min;
> signed long max;
> } SignedRange;
>
> typedef struct {
> unsigned long min;
> unsigned long max;
> } UnsignedRange;
>
> signed long increaseSigned(signed long value,
> const SignedRange *range);
> ...
> unsigned long decreaseUnsigned(unsigned long value,
> const UnsignedRange *range);
> ...
>
> const SignedRange celsiusRange = { -273, LONG_MAX };
> const UnsignedRange kelvinRange = { 0, ULONG_MAX };
> ...
>
> signed long celsius = 0;
> unsigned long kelvin = 273;
> ...
>
> celsius = increaseSigned(celsius, &celsiusRange);
> kelvin = decreaseSigned(kelvin, &kelvinRange);
>
> Note that this works (with suitable ranges) even if `celsius'
> is `signed short' and `kelvin' is `unsigned char': C's argument
> promotions and assignment conversions allow you to use the `long'
> functions with narrower quantities. Of course, you *can* stick
> with per-instance ranges, if that makes more sense for your problem,
> but per-"scale" ranges may turn out to be more convenient.

This sounds very good. I can continue using already defined variables
with their "natural" type and use the automatic promotion/assignment
conversion of C.


> Summary: Three-quarters of your complexity can be done away with
> at one stroke. You may choose to live with what remains, or to adopt
> a slightly different API.

Thank you very much for your suggestions and for spending some time
to help me. I appreciated it.

Eric Sosman

4/8/2011 1:50:00 AM

0

On 4/7/2011 6:32 AM, pozz wrote:
> On 6 Apr, 03:30, Eric Sosman<esos...@ieee-dot-org.invalid> wrote:
>> [...]
>> Okay, we've simplified by reducing eight possibilities to just
>> two. (Somebody's sure to say we're wasting space by using a `long'
>> where a `char' would suffice, but any such Somebody hasn't been
>> paying attention: Your present scheme uses a `union' of all the
>> candidate types, so you use a `long's worth of space even for a
>> `char' variable. Using `long' for everything takes no more space
>> than you're already using.)
>
> The approach I presented is a small variation of the one I'm really
> using.

Oh, g-r-e-a-t! Well, then, the suggestions I offer are "small
variations of helpful."

> I need to declare const structures to save precious RAM space on
> my embedded platform, but I can't initialize whatever member of
> unions/structs (a limit of the compiler). So my real scheme doesn't
> use unions but a struct/substruct approach:
>
> typedef struct {
> signed char *ptr;
> signed char min;
> signed char max;
> } scnum_t;
>
> typedef struct {
> unsigned char *ptr;
> unsigned char min;
> unsigned char max;
> } ucnum_t;
>
> /*...*/
>
> 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 *subnum; /* Pointer to scnum_t, ucnum_t, ... */
> } num_t;
>
> In this case I can write:
>
> signed char temp; /* The variable */
> const scnum_t snTemperature = {&temp, -10, +10 };
> const num_t nTemperature = { NUM_TYPE_SCHAR,&snTemperature };

And you do all this to "save precious RAM?" Ye gods! You plan
to "save" RAM by replacing every variable-and-two-limits with a
variable *and* a struct containing the limits *and* a pointer to
the variable *and* a type code?

> With an additional pointer (subnum in num_t), I can declare min/max
> values as the correct type, so without losing space declaring long or
> unsigned long min/max.

R-i-i-i-g-h-t.

> Most of the variables have small values, so they are already declared
> as signed/unsigned char/short. In that case, on a 16-bit platform
> (like the one I'm using), I need one num_t struct (4 bytes) and one
> "subnum" struct (4 bytes).

Plus the variable itself, for 9 bytes in all (for chars). If
you jettisoned the damn pointers and the type code and just put the
value and limits into one struct, you'd use 3 bytes per variable --
maybe 4, if the compiler pads structs rather freely -- instead of
the 9 you use in your current scheme (*if* it's truly your current
scheme, and not yet another "small variation").

If RAM is so "precious" that you eagerly seek ways to triple
your usage thereof, I'd like to sell you a truly precious bridge.

> If subnum types are incorporated into num_t with min/max as long, I
> need only one num_t struct (12 bytes) for all variables.
> With my approach I can save 4 bytes for each "small values" variable.

Adding 200% overhead is a funny form of "savings."

> Anyway this isn't a very big problem, because I have a lot of "const"
> space in non-volatile memory.

Please reconcile "isn't a very big problem" with "precious."
Also, please explain why you need upper/lower range limits to bracket
the values of const variables.

>> You could go ahead with a three-quarters-reduced version of
>> your current scheme, but now that the possibilities are down to two
>> (signed vs. unsigned) I'd be strongly tempted to say "Two is a
>> manageable number; I'll handle the two types separately." This is
>> optional, of course, but it has the advantage of eliminating all
>> those `if's you find so objectionable. You'd get something like
>>
>> typedef struct {
>> signed long val;
>> signed long min;
>> signed long max;
>> } Signed;
>>
>> typedef struct {
>> unsigned long val;
>> unsigned long min;
>> unsigned long max;
>> } Unsigned;
>
> I can't do in this way. The variables are also declared and used in
> several parts of the code and in different modules I can't touch.
> So the numeric value can't reside inside Signed/Unsigned struct.
> I have to use a pointer to the variable.

You don't know how to point at an element of a struct?

> typedef struct {
> enum {
> NUM_TYPE_SCHAR,
> NUM_TYPE_SSHRT,
> NUM_TYPE_SINT,
> NUM_TYPE_SLONG
> } type;
> void *ptr; /* Pointer to signed char/short/int as in type
> */
> signed long min;
> signed long max;
> } Signed;
>
> typedef struct {
> enum {
> NUM_TYPE_UCHAR,
> NUM_TYPE_USHRT,
> NUM_TYPE_UINT,
> NUM_TYPE_ULONG
> } type;
> void *ptr; /* Pointer to unsigned char/short/int as in
> type */
> unsigned long min;
> unsigned long max;
> } Unsigned;
>
> But, in this case, the compiler can't give me warning if I wrongly
> assign to ptr member of Signed/Unsigned struct a variable with a
> different type as indicated in the type member.

Under your current (?) scheme(s?), the compiler can't warn you
about *any* mismatches. Failure to warn about *some* mismatches
is therefore an improvement.

> Also this can be a minor problem (in my struct/substruct approach
> above, I have the same problem with subnum that is void*).

Non capisco.

>> [...]

--
Eric Sosman
esosman@ieee-dot-org.invalid

gazelle

4/8/2011 2:05:00 PM

0

In article <inlpkl$cof$1@dont-email.me>,
Eric Sosman <esosman@ieee-dot-org.invalid> ranted:
>On 4/7/2011 6:32 AM, pozz wrote:
>> On 6 Apr, 03:30, Eric Sosman<esos...@ieee-dot-org.invalid> wrote:
>>> [...]
>>> Okay, we've simplified by reducing eight possibilities to just
>>> two. (Somebody's sure to say we're wasting space by using a `long'
>>> where a `char' would suffice, but any such Somebody hasn't been
>>> paying attention: Your present scheme uses a `union' of all the
>>> candidate types, so you use a `long's worth of space even for a
>>> `char' variable. Using `long' for everything takes no more space
>>> than you're already using.)
>>
>> The approach I presented is a small variation of the one I'm really
>> using.
>
> Oh, g-r-e-a-t! Well, then, the suggestions I offer are "small
>variations of helpful."
(etc)

The boy has issues.

--
Windows 95 n. (Win-doze): A 32 bit extension to a 16 bit user interface for
an 8 bit operating system based on a 4 bit architecture from a 2 bit company
that can't stand 1 bit of competition.

Modern day upgrade --> Windows XP Professional x64: Windows is now a 64 bit
tweak of a 32 bit extension to a 16 bit user interface for an 8 bit
operating system based on a 4 bit architecture from a 2 bit company that
can't stand 1 bit of competition.

pozz

4/8/2011 9:57:00 PM

0

Il 08/04/2011 03:49, Eric Sosman ha scritto:
>> I need to declare const structures to save precious RAM space on
>> my embedded platform, but I can't initialize whatever member of
>> unions/structs (a limit of the compiler). So my real scheme doesn't
>> use unions but a struct/substruct approach:
>>
>> typedef struct {
>> signed char *ptr;
>> signed char min;
>> signed char max;
>> } scnum_t;
>>
>> typedef struct {
>> unsigned char *ptr;
>> unsigned char min;
>> unsigned char max;
>> } ucnum_t;
>>
>> /*...*/
>>
>> 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 *subnum; /* Pointer to scnum_t, ucnum_t, ... */
>> } num_t;
>>
>> In this case I can write:
>>
>> signed char temp; /* The variable */
>> const scnum_t snTemperature = {&temp, -10, +10 };
>> const num_t nTemperature = { NUM_TYPE_SCHAR,&snTemperature };
>
> And you do all this to "save precious RAM?" Ye gods! You plan
> to "save" RAM by replacing every variable-and-two-limits with a
> variable *and* a struct containing the limits *and* a pointer to
> the variable *and* a type code?

Oh, I'm sorry, I created some confusions on this point.
I need to declare limits and other stuff as constants to save RAM space
(when I declare const variables they are stored in Flash memory) and
because their values don't change at runtime.
I posted some functions based on structs with unions member, but I can't
use unions in constant struct with my compiler, because it doesn't
accept new initialization syntax with the name of the member.
In other words I can't do:

const struct {
int type;
union {
unsigned char x;
unsigned int y;
} u;
} mystruct = { 0, { .y=3 } };

even if it is perfectly legal for a C99 compiler. So I invented the
struct/substruct mechanism to declare constant structs of different
types with my limited compiler, not to save space.

You wrote you don't waste space declaring limits as long even for char,
short and int variables because my original approach used unions. And
you were right.

I just wanted to note that with my real approach (struct and substruct)
I effectively save some space (in Flash memory) respect your choice to
use always long.
But I have also said that Flash memory is more than sufficient, so this
(waste a small amount of Flash memory with long limits) is a minor drawback.


>> With an additional pointer (subnum in num_t), I can declare min/max
>> values as the correct type, so without losing space declaring long or
>> unsigned long min/max.
>
> R-i-i-i-g-h-t.
>
>> Most of the variables have small values, so they are already declared
>> as signed/unsigned char/short. In that case, on a 16-bit platform
>> (like the one I'm using), I need one num_t struct (4 bytes) and one
>> "subnum" struct (4 bytes).
>
> Plus the variable itself, for 9 bytes in all (for chars). If
> you jettisoned the damn pointers and the type code and just put the
> value and limits into one struct, you'd use 3 bytes per variable --
> maybe 4, if the compiler pads structs rather freely -- instead of
> the 9 you use in your current scheme (*if* it's truly your current
> scheme, and not yet another "small variation").
>
> If RAM is so "precious" that you eagerly seek ways to triple
> your usage thereof, I'd like to sell you a truly precious bridge.

I'm sorry also for this confusion. I was comparing my struct/substruct
approach with the correct types (8 bytes for chars):

typedef struct {
signed char *ptr;
signed char min;
signed char max;
} scnum_t;
typedef struct {
unsigned char *ptr;
unsigned char min;
unsigned char max;
} ucnum_t;
...
typedef struct {
enum {
NUM_TYPE_SCHAR,
...
} type;
void *subnum; /* Pointer to scnum_t, ucnum_t, ... */
} num_t;

with your first suggestion (to not use unions but only long, 12 bytes
for chars):

typedef struct {
enum {
NUM_TYPE_SCHAR,
NUM_TYPE_SSHRT,
NUM_TYPE_SINT,
NUM_TYPE_SLONG,
} type;
void *ptr; /* pointer to the variable */
signed long min;
signed long max;
} numSigned;
... the same for numUnsigned

I wasn't considering your last suggestion to separate values and limits
with a different API (passing the values as longs to the functions).


>> Anyway this isn't a very big problem, because I have a lot of "const"
>> space in non-volatile memory.
>
> Please reconcile "isn't a very big problem" with "precious."

Isn't a big problem to waste Flash memory, is a big problem to waste
RAM. So I'd like to declare consts where it is possible, for example for
limits that don't change.


> Also, please explain why you need upper/lower range limits to bracket
> the values of const variables.

Limits are constants, not values of the variables to must be keeped
inside those limits.


>>> You could go ahead with a three-quarters-reduced version of
>>> your current scheme, but now that the possibilities are down to two
>>> (signed vs. unsigned) I'd be strongly tempted to say "Two is a
>>> manageable number; I'll handle the two types separately." This is
>>> optional, of course, but it has the advantage of eliminating all
>>> those `if's you find so objectionable. You'd get something like
>>>
>>> typedef struct {
>>> signed long val;
>>> signed long min;
>>> signed long max;
>>> } Signed;
>>>
>>> typedef struct {
>>> unsigned long val;
>>> unsigned long min;
>>> unsigned long max;
>>> } Unsigned;
>>
>> I can't do in this way. The variables are also declared and used in
>> several parts of the code and in different modules I can't touch.
>> So the numeric value can't reside inside Signed/Unsigned struct.
>> I have to use a pointer to the variable.
>
> You don't know how to point at an element of a struct?

I have many modules (I can't touch) that uses internally many variables
and export them in a .h file (with extern keyword).
I need to change these extern variables with my functions, but I can't
move the declarations of those variables.


>> typedef struct {
>> enum {
>> NUM_TYPE_SCHAR,
>> NUM_TYPE_SSHRT,
>> NUM_TYPE_SINT,
>> NUM_TYPE_SLONG
>> } type;
>> void *ptr; /* Pointer to signed char/short/int as in type
>> */
>> signed long min;
>> signed long max;
>> } Signed;
>>
>> typedef struct {
>> enum {
>> NUM_TYPE_UCHAR,
>> NUM_TYPE_USHRT,
>> NUM_TYPE_UINT,
>> NUM_TYPE_ULONG
>> } type;
>> void *ptr; /* Pointer to unsigned char/short/int as in
>> type */
>> unsigned long min;
>> unsigned long max;
>> } Unsigned;
>>
>> But, in this case, the compiler can't give me warning if I wrongly
>> assign to ptr member of Signed/Unsigned struct a variable with a
>> different type as indicated in the type member.
>
> Under your current (?) scheme(s?), the compiler can't warn you
> about *any* mismatches. Failure to warn about *some* mismatches
> is therefore an improvement.

With the struct/substruct approach (see above), I can be advised if I
wrongly assign an incompatible pointer to substruct member ptr:

int temp;
const scnum_t sTemperature = { &temp, -10, +10 };
const num_t Temperature = { NUM_TYPE_SCHAR, &sTemperature };

Here temp is int, but scnum_t struct want a signed char pointer, so the
compiler can warn me.

With the second approach (with void *ptr in Signed and Unsigned structs,
see above), the compiler can't warn me of incompatible types.
But I must say this second approach is a my personal idea (borned after
your suggestions), not yours.


>> Also this can be a minor problem (in my struct/substruct approach
>> above, I have the same problem with subnum that is void*).
>
> Non capisco.

With struct/substruct approach (see above) the compiler can warn me
about incompatible pointer types for ptr member of substructs, but can't
warn me about incompatible pointer of subnum pointer of num_t struct,
like in the following example:

int temp;
const scnum_t sTemperature = { &temp, -10, +10 };
const num_t Temperature = { NUM_TYPE_SINT, &sTemperature };

pozz

4/8/2011 10:00:00 PM

0

Il 08/04/2011 16:05, Kenny McCormack ha scritto:
> The boy has issues.

Oh yes, I have many issues... but I think a lot of boys are in the same
situations. The difference is I know it, they don't.

I'm sorry if I'm disturbing this ng with my stupid questions or
considerations. I'm not a C guru and I have many things to learn yet
about this language. If newbies can't post here, I stop immediately.

Keith Thompson

4/8/2011 11:01:00 PM

0

pozz <pozzugno@gmail.com> writes:
> Il 08/04/2011 16:05, Kenny McCormack ha scritto:
>> The boy has issues.
>
> Oh yes, I have many issues... but I think a lot of boys are in the same
> situations. The difference is I know it, they don't.
>
> I'm sorry if I'm disturbing this ng with my stupid questions or
> considerations. I'm not a C guru and I have many things to learn yet
> about this language. If newbies can't post here, I stop immediately.

A quick look at Kenny McCormack's posting history here should
tell you whether there's any point in paying any attention to him.
(Hint: there isn't.)

--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

gazelle

4/8/2011 11:55:00 PM

0

In article <ino0gf$dn7$1@nnrp.ngi.it>, pozz <pozzugno@gmail.com> wrote:
>Il 08/04/2011 16:05, Kenny McCormack ha scritto:
>> The boy has issues.
>
>Oh yes, I have many issues... but I think a lot of boys are in the same
>situations. The difference is I know it, they don't.
>
>I'm sorry if I'm disturbing this ng with my stupid questions or
>considerations. I'm not a C guru and I have many things to learn yet
>about this language. If newbies can't post here, I stop immediately.

The boy I was talking about was Eric

Not you.

Pay no attention to the troll "Kiki" Thompson.

--
(This discussion group is about C, ...)

Wrong. It is only OCCASIONALLY a discussion group
about C; mostly, like most "discussion" groups, it is
off-topic Rorsharch [sic] revelations of the childhood
traumas of the participants...