Hatefiend
Hatefiend

Reputation: 3596

Returning error with unsigned values?

I'm making a data structure library. One function I am allowing users to call is:

unsigned int index(struct myDataStructure, void* value);

It searches my data structure and returns the index location of where that value exists in it.

ex.

{ 'A', 'D', 'C' }

char val1 = 'A';
unsigned int location = index(s, &val1); // location = 0
char val2 = 'C';
location = index(s, &val2); // location = 2

If the element does not exist in the list, then I don't know what to return. Here are the options that I've ruled out so far:

The best solution I had was to do:

// Change return type to pointer.
unsigned int* index(struct myDataStructure, void* value)
{
     static int val;

     if (value exists...)
     {
          val = correct index value
          return &val;
     }
     else
     {
          return NULL;
     }
}

But I still feel like this solution is very poor.

Upvotes: 1

Views: 1722

Answers (3)

alk
alk

Reputation: 70931

Several possibilities:

  • On error return (unsigned int) -1 using

    unsigned int index(struct myDataStructure, void* value)
    
  • On error return -1 using

    ssize_t index(struct myDataStructure, void* value)
    

    (ssize_t is POSIX)

  • Pass in the address of an unsigned int to point to the result and return -1 on error and 0 on success, using

    int index(struct myDataStructure, void* value, unsigned int * result)
    

Using

  • an assertion I feel is not appropriate here, as it ends your program.
  • a static buffer is coding style of the last millennium. It makes your library unusable in a multithreaded context.

Upvotes: 1

Gene
Gene

Reputation: 46960

You're right that returning a pointer to static is bad in many ways. It's not thread safe, and - worse - it invites users to do stuff like

int *aIndexLoc = index(data, &a);
int *bIndexLoc = index(data, &b);
if (aIndexLoc && bIndexLoc) 
  printf ("a's loc is %u; b's loc is %u\n", *aIndexLoc, *bIndexLoc);

And of course get the wrong answer.

First... If you want your library to be future-proof, then don't return unsigned for an array index. Return size_t.

Then... There are several idioms for dealing with error returns. The most common is to return an int or enum error code as the function value and the actual return value with a pointer arg. By convention, 0 means "okay" and non-zero values are various error codes. Also, if your data structure is more than a few bytes, don't pass a complete copy of it. Pass a pointer

typedef int ERROR;
ERROR index(size_t *result, struct myDataStructure *myStruct, void *valueToFind);

and thence something like:

size_t loc[1];
struct myDataStructure someData[1];
int aValue[1];

initialize(someData);
get(aValue);

ERROR error = index(loc, someData, aValue);
if (error) {
  fprintf(stderr, "Couldn't find the value. Error code: %d\n", error);
  return;
}

The 1-element array thing is a trick that lets you code the same way whether an object is allocated on the stack or heap. You can treat the name of the array as a pointer. E.g. someData->fieldName and *loc work just fine.

Upvotes: 1

snoopy
snoopy

Reputation: 318

You could pass a pointer to a bool that sets as true or false accordingly.

unsigned int index(struct myDataStructure, void *value, bool *ok);

Upvotes: 0

Related Questions