Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type-safety in C

Tags:

c

type-safety

Is there a way to make C a little more aware of types and assure type-safety?
Consider this:

typedef unsigned cent_t; typedef unsigned dollar_t;  #define DOLLAR_2_CENT(dollar)       ((cent_t)(100*(dollar)))  void calc(cent_t amount) {     // expecting 'amount' to semantically represents cents... }  int main(int argc, char* argv[]) {     dollar_t amount = 50;     calc(DOLLAR_2_CENT(amount));  // ok     calc(amount);                 // raise warning     return 0; } 

Is there a way to make the above code at-least raise warning by the gcc?
I know I can use C-structs to wrap unsigneds and achieve the desired result, I was just wondering if there was a more elegant way to do it.
Can it be a little more than that?

like image 310
so.very.tired Avatar asked Apr 01 '16 08:04

so.very.tired


People also ask

Is C type-safe language?

What are some examples of type safe and un-safe languages? Versions of ML, Python and Java are believed to be type safe. Although C and C++ (even more so) are type-safe in many contexts, both languages also contain several common features which are not type-safe.

What is type safety and why is it important?

Type safety is important for compiled language because the types does not use the same amount of memory and the computer needs to be made aware of that to make sure it does not enter an invalid state during runtime. As for interpreted language, it is implicitly typed and the level of safety is language dependent.


2 Answers

The problem is that C doesn't treat your two typedefs as distinctive types, because they are both type unsigned.

There are various tricks to dodge this. One thing would be to change your types to enums. Good compilers will enforce stronger typing warnings on implicit conversions to/from a certain enum type to any other type.

Even if you don't have a good compiler, with enums you could do this:

typedef enum { FOO_CENT  } cent_t; typedef enum { FOO_DOLLAR} dollar_t;  #define DOLLAR_2_CENT(dollar)       ((cent_t)(100*(dollar)))  void calc(cent_t amount) {     // expecting 'amount' to semantically represents cents... }  #define type_safe_calc(amount) _Generic(amount, cent_t: calc(amount))  int main(int argc, char* argv[]) {     dollar_t amount = 50;     type_safe_calc(DOLLAR_2_CENT(amount));  // ok     type_safe_calc(amount);         // raise warning      return 0; } 

A more conventional/traditional trick is to use a generic struct wrapper, where you use a "ticket" enum to mark the type. Example:

typedef struct {   type_t type;   void*  data; } wrapper_t;  ...  cent_t my_2_cents; wrapper_t wrapper = {CENT_T, &my_2_cents};  ...  switch(wrapper.type) {   case CENT_T: calc(wrapper.data)   ... } 

The advantage is that it works with any C version. Disadvantage is code and memory overhead, and that it only allows run-time checks.

like image 143
Lundin Avatar answered Sep 29 '22 20:09

Lundin


Aliasing has a very specific narrow meaning in C, and it's not what you have in mind. You may want to say "typedefing".

And the answer is no, you can't. Not in an elegant way at any rate. You can use a struct for each numeric type, and a separate set of functions to do arithmetic with each one. Except when it comes to multiplication, you are out of luck. In order to multiply feet by pounds, you need a third type. You also need types for feet squared, feet cubed, seconds to the power of minus two and an infinite number of other types.

If this is what you are after, C is not the right language.

like image 30
n. 1.8e9-where's-my-share m. Avatar answered Sep 29 '22 21:09

n. 1.8e9-where's-my-share m.