Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #67649 > unrolled thread
| Started by | mathog <dmathog@gmail.com> |
|---|---|
| First post | 2015-08-17 15:52 -0700 |
| Last post | 2015-08-21 13:47 -0700 |
| Articles | 5 — 4 participants |
Back to article view | Back to comp.lang.c
type safety on simple data types - without structs? mathog <dmathog@gmail.com> - 2015-08-17 15:52 -0700
Re: type safety on simple data types - without structs? Ian Collins <ian-news@hotmail.com> - 2015-08-18 11:11 +1200
Re: type safety on simple data types - without structs? Jorgen Grahn <grahn+nntp@snipabacken.se> - 2015-08-18 18:16 +0000
Re: type safety on simple data types - without structs? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-08-18 04:14 -0700
Re: type safety on simple data types - without structs? mathog <dmathog@gmail.com> - 2015-08-21 13:47 -0700
| From | mathog <dmathog@gmail.com> |
|---|---|
| Date | 2015-08-17 15:52 -0700 |
| Subject | type safety on simple data types - without structs? |
| Message-ID | <mqtoj6$8co$1@dont-email.me> |
I'm working on a bit of code which has some packed data types. These
have records that are funny lengths, like 11 bytes, so the data is
accessed by a bunch of get()/set() routines which either take a char *
pointer to the start of one of these records, or an integer index for
the record number. For the sake of argument, let all of those indices
be uin64_t.
The question is, is there some magic way to make a type safe version of
these simple types in C, perhaps something added in c99 or later?
I mean:
magic_here char * type_safe_char_ptr_A;
magic_here char * type_safe_char_ptr_B;
where "magic_here" has the same syntax as typedef, such that
int get_A(type_safe_char_ptr_A ptr){
// do something
}
will throw an error at compilation time if it is called like:
type_safe_char_ptr_B bptr;
type_safe_char_ptr_A aptr;
// bptr assigned
int result = get_A(bptr); // wrong type in call
aptr=bptr; // wront type in assignment
Ditto for the uint64_t values.
typedef definitely does not fit the bill. For instance, this compiles
with no warnings;
typedef unsigned long long BUNSLLI;
typedef unsigned long long UNSLLI;
int wacko(UNSLLI var){
if(var)return(1);
return(0);
}
// later, in another routine
BUNSLLI foo=1;
UNSLLI bar=0;
bar=foo;
fprintf(stderr,"foo %llu bar %llu\n",foo,bar);
fprintf(stderr,"wacko %d\n",wacko(foo));
Type safety can be obtained by declaring a bunch of structs that contain
only the one variable, like
typedef struct is_safe_char_ptr_A SAFE_PTR_A;
typedef struct is_safe_char_ptr_B SAFE_PTR_B;
typedef struct is_safe_uint64_A SAFE_UINT64_A;
typedef struct is_safe_uint64_B SAFE_UINT64_B;
struct is_safe_char_ptr_A { char *ptr; };
struct is_safe_uint64_A { uint64_t val; };
struct is_safe_char_ptr_B { char *ptr; };
struct is_safe_uint64_B { uint64_t val; };
then
int get_A(SAFE_PTR_A first, SAFE_UINT64_A index){
}
will not accept SAFE_PTR_B for the first, and SAFE_UINT64_B for the
second. The down side is that with this approach one must use
first.ptr
and
index.val
to get to the desired values. In this example there is no offset
to either of the desired values, and there are no other values in
the structs. So the struct method seems a bit clunky, in that one must
use an "access member" syntax when it seems like it shouldn't be needed.
I can see why this might not exist though. For instance what is the
compiler supposed to do with:
magic_here UINT64_t type_safe_UINT64_B;
magic_here UINT64_t type_safe_UINT64_A;
type_safe_UINT64_B bvar=100;
if it is supposed to throw an error on assignment of
type_safe_UINT64_A to an type_safe_UINT64_B, it would probably have to
do so for an integer assignment as well, unless there is an explicit cast.
Thanks,
David Mathog
[toc] | [next] | [standalone]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2015-08-18 11:11 +1200 |
| Message-ID | <d3f80eF2rqoU1@mid.individual.net> |
| In reply to | #67649 |
mathog wrote: > I'm working on a bit of code which has some packed data types. These > have records that are funny lengths, like 11 bytes, so the data is > accessed by a bunch of get()/set() routines which either take a char * > pointer to the start of one of these records, or an integer index for > the record number. For the sake of argument, let all of those indices > be uin64_t. > > The question is, is there some magic way to make a type safe version of > these simple types in C, perhaps something added in c99 or later? I don't think there is in C99, but if you consider C++ to be something added later then the answer is yes :) -- Ian Collins
[toc] | [prev] | [next] | [standalone]
| From | Jorgen Grahn <grahn+nntp@snipabacken.se> |
|---|---|
| Date | 2015-08-18 18:16 +0000 |
| Message-ID | <slrnmt6tkb.eij.grahn+nntp@frailea.sa.invalid> |
| In reply to | #67653 |
On Mon, 2015-08-17, Ian Collins wrote: > mathog wrote: >> I'm working on a bit of code which has some packed data types. These >> have records that are funny lengths, like 11 bytes, so the data is >> accessed by a bunch of get()/set() routines which either take a char * >> pointer to the start of one of these records, or an integer index for >> the record number. For the sake of argument, let all of those indices >> be uin64_t. >> >> The question is, is there some magic way to make a type safe version of >> these simple types in C, perhaps something added in c99 or later? > > I don't think there is in C99, but if you consider C++ to be something > added later then the answer is yes :) And: to some of us it's one of the big features of C++: it lets you arrange your code around a lot of small and rather trivial types. The way to do it in C is indeed using structs. It works, but it's a lot more clunky and not always worth it. /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o .
[toc] | [prev] | [next] | [standalone]
| From | Malcolm McLean <malcolm.mclean5@btinternet.com> |
|---|---|
| Date | 2015-08-18 04:14 -0700 |
| Message-ID | <f1de5bec-5e5f-4848-b751-b389ddc69ee9@googlegroups.com> |
| In reply to | #67649 |
On Monday, August 17, 2015 at 11:54:11 PM UTC+1, mathog wrote:
>
> magic_here char * type_safe_char_ptr_A;
> magic_here char * type_safe_char_ptr_B;
>
> where "magic_here" has the same syntax as typedef, such that
>
> int get_A(type_safe_char_ptr_A ptr){
> // do something
> }
>
> will throw an error at compilation time if it is called like:
>
> In this example there is no offset
> to either of the desired values, and there are no other values in
> the structs. So the struct method seems a bit clunky, in that one must
> use an "access member" syntax when it seems like it shouldn't be needed.
>
I'd say that the struct method isn't clunky, unless you need to
micro-optimise.
We've got a buffer of packed bytes somewhere. As it happens, it must
be either all A or all B, and A and B are always a whole number of bytes.
So we can use an unsigned char *. But should any of that change,
you're going to need extra bits in what is effectively a smart pointer.
So the struct method is robust, it scales to changes in requirements.
In fact what I'd do is just have one struct, buffer_ptr. Then have a
field that says whether it is A or B, and assert fail if you try to read
a A from a B pointer, or vice versa. That's essentially as good as type
safety, and it allows you to unify a lot of higher-level code
because you can switch on the type field in a low-level subroutine.
(That might not make sense, you have't given any details about the real
application, but there's a good chance it does make sense).
[toc] | [prev] | [next] | [standalone]
| From | mathog <dmathog@gmail.com> |
|---|---|
| Date | 2015-08-21 13:47 -0700 |
| Message-ID | <mr82ms$u7e$1@dont-email.me> |
| In reply to | #67701 |
Malcolm McLean wrote:
> On Monday, August 17, 2015 at 11:54:11 PM UTC+1, mathog wrote:
>>
>> magic_here char * type_safe_char_ptr_A;
>> magic_here char * type_safe_char_ptr_B;
>>
>> where "magic_here" has the same syntax as typedef, such that
>>
>> int get_A(type_safe_char_ptr_A ptr){
>> // do something
>> }
>>
>> will throw an error at compilation time if it is called like:
>>
>> In this example there is no offset
>> to either of the desired values, and there are no other values in
>> the structs. So the struct method seems a bit clunky, in that one must
>> use an "access member" syntax when it seems like it shouldn't be needed.
>>
> I'd say that the struct method isn't clunky, unless you need to
> micro-optimise.
I tried it and it wasn't all that terrible looking when it was finished.
It wasn't perfect though. One of the types was
a uint32_t record number which was stored in an array.
A struct with a "payload" like this:
uint32_t val;
has a sizeof() on the 64bit system of 8 and not 4, so it wasn't possible
to cast the array and pull out the struct based types directly - even
though all the uint32_t's were aligned properly for the data type, they
were not aligned for the struct.
All in all though, it was still better with with the extra level of type
safety, since there were locations with 2 or 3 different types of
integer indices and their associated char * pointers in play, and this
keeps them from being used accidentally in the wrong places.
I suppose some compilers may have a switch that would force the size of
the struct down to something smaller than the native machine's word
size, but that isn't portable as far as the language is concerned.
Of course, the problem mentioned above for the 32 bit type would also
occur for all 64bit ints on a 128 bit machine, should this code ever
encounter such a beast.
Regards,
David Mathog
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web