Reputation: 3596
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:
Assert
or system exit or exception to end the run-time. I don't think there would be much use in that because the user would have to call contains
to make sure the element was in the structure before calling index()
long
, so I can return -1
. I don't want to switch data types.**unsigned int
so that I can either point it to NULL
if does not exist or a real index value. This is not user friendly for people reading my API to understand.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
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
Upvotes: 1
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
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