2

Is it a suitable approach to restrict the parameters of function with a 'typesafe' enum to avoid the check for array index out of bounds?

I have a module which holds the data of its instances in an Array. The data of an instance should be accessed from outside the module with an index. There will be several interface functions and i would like to avoid several if-statements.

Following example:

// In myInstanceModule.h

typedef struct { enum { FIRST, SECOND } index_e; } instance_tst;
#define FIRST_INSTANCE  (instance_tst){ FIRST }
#define SECOND_INSTANCE (instance_tst){ SECOND }

void instance_init_v();
void instance_print_v(instance_tst instance);

// In myInstanceModule.c

#define MEMBER_COUNT 2

typedef struct myArray {
    int myValue; 
}myArray_tst;

static myArray_tst myMembers_ast[MEMBER_COUNT];

void instance_init_v() {
    for (int i = 0; i < MEMBER_COUNT; i++)
    {
        myMembers_ast[i].myValue = i * 10;
    }
}

void instance_print_v(instance_tst instance) {
    printf("Value of this instance is: %d \n", myMembers_ast[instance.index_e].myValue);
}

// In main.c

#include myModule.h
int main(void)
{
    int test = 1234;
    instance_init_v();

    instance_print_v(FIRST_INSTANCE);       // ok
    instance_print_v(SECOND_INSTANCE);      // ok
    //instance_print_v((instance_tst)2);    // does not compile
    //instance_print_v(test);               // does not compile
    //instance_print_v(1);                  // does not compile
    //instance_print_v(NULL);               // does not compile
}

The example in one file: https://repl.it/repls/QuarrelsomeDotingComputation

5
  • 2
    You missed the most obvious way to abuse: try instance_print_v((instance_tst){20}); Commented Jun 29, 2018 at 7:29
  • Oh, that's right. Thank you very much. Do you have any approach to get rid of that? Commented Jun 29, 2018 at 7:35
  • No, I don't have a solution other than using a range check. But this is what you wanted to avoid. :( Commented Jun 29, 2018 at 7:37
  • 1
    Related: How to create type safe enums?. Commented Jun 29, 2018 at 8:33
  • After all the crashes and corrupted memory i have seen because of array bounds, I suggest you always always check them. Give yourself an easier life. Commented Jun 29, 2018 at 9:09

3 Answers 3

1

This approach will not prevent someone from using compound literals, like

instance_print_v(((instance_tst){2}));

So rather have

void instance_print_v(size_t index){
    if(index < sizeof(myMembers_ast)/sizeof(myMembers_ast[0]))
    {
      printf("Value of this instance is: %d \n", myMembers_ast[index].myValue);
    }
    else
    {
      printf("Value of this instance is: undefined");
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

In all fairness, a plain boundary check like this is probably the best solution. Other solutions might look elegant, but they add lots of complexity.
1

Unfortunately, C is very tolerant with enums. An enum is only a list of symbolic constants and you can always trick it by using the underlying type. Here

instance_print_v((instance_tst){2});    // does compile

does compile fine (not even a warning) and causes the access past the end of the array that you tried to prevent.

1 Comment

Disclaimer: credits should go to @Gerhardh that answered this in comment while I was testing...
1

Adapting your code to the trick I invented here: How to create type safe enums?, then I'm ending up with something like this:

#include <stdio.h>

// In myInstanceModule.h

typedef enum
{
  FIRST,
  SECOND
} instance_tst;

typedef union
{
  instance_tst FIRST;
  instance_tst SECOND;
} typesafe_instance_t;

#define instance_assign(var, val) _Generic((var), \
  instance_tst: (var) = (typesafe_instance_t){ .val = val }.val )

void instance_init_v();
void instance_print (instance_tst instance); // the actual function

// type-safe wrapper:
#define instance_print_v(val) instance_print( instance_assign((instance_tst){0}, val) )

// In myInstanceModule.c

#define MEMBER_COUNT 2

typedef struct myArray {
    int myValue; 
}myArray_tst;

static myArray_tst myMembers_ast[MEMBER_COUNT];

void instance_init_v() {
    for (int i = 0; i < MEMBER_COUNT; i++)
    {
        myMembers_ast[i].myValue = i * 10;
    }
}

void instance_print (instance_tst instance) {
    printf("Value of this instance is: %d \n", myMembers_ast[instance].myValue);
}

// In main.c

int main(void)
{
    int test = 1234;
    instance_init_v();

    instance_print_v(FIRST);       // ok
    instance_print_v(SECOND);      // ok

    //instance_print_v((instance_tst)2);    // does not compile
    //instance_print_v(test);               // does not compile
    //instance_print_v(1);                  // does not compile
    //instance_print_v(NULL);               // does not compile
    //instance_print_v((instance_tst){20}); // does not compile
}

Unfortunately this will also block a instance_tst variable from getting passed to the function, but as I understand it, that's not a problem here.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.