views:

1284

answers:

7

I'm writing an access function which returns a pointer to an internal buffer and I'd like to hint to users of my function that they shouldn't update the object that's pointed to. A very contrived example would be:

void myAccessFunc(bool string, void* p, size_t* len)
{
  static const char* s = "aha!";
  static const int i = 123;

  if (string) 
     *(char**)p = &s;
     *len = strlen(s);
  else
     *(int**)p = &i;
     *len = sizeof(i);
}

char* s;
size_t bytes;
myAccessFunc(true,&s, &bytes);
printf("Got '%s'\n", s);

Yes, I know that looks flakey.

What I want to prevent is:

char* s;
size_t bytes;
myAccessFunc(true,&s,&bytes);
s[4] = '?';

I know I can't completely prevent it but I'd at least like the compiler warning to hint to a user that they shouldn't be doing that. If they cast my pointer, then that's their problem. Is there some combination of const and void and * that will do this? I tried something like:

void myAccessFunc(bool string, const void** p, size_t* len);

but it seemed do away with the voidness of the pointer so a caller had to do:

const void* p;
size_t bytes;
myAccessFunc(true, &p, &bytes);

or

const char* s;
size_t bytes;
myAccessFunc(true, (const void**)&s, &bytes);

and couldn't do:

const int * ip;
const char* s;
size_t bytes;
myAccessFunc(true, &s, &bytes);
myAccessFunc(false, &i, &bytes);

I finally came around to:

const void* myAccessFunc(bool string, size_t* len);

and if the user does:

char* p = myAcccessFunc(true,&bytes);

the compiler (GCC, at least), does complain about throwing away the qualifier.

+2  A: 

Preventing is impossible, since you can cast away constness. If you make your function parameter const, you'll have to cast it away yourself inside the function. You'd also be lying to anyone using your function and could cause all sorts of fun bugs.

You could try returning a pointer instead. Then you'd at least not be violating your own const. That may not be appropriate in this case though.

Ryan Graham
+9  A: 

It would be best to do something like:

const void * myAccessFunc();

Where you return the pointer to the internals, this is a bit more natural than passing it as an out parameter.

If you were to pass it as an out parameter, you would want:

void myAccessFunc(const void **p);

which prevents them from doing this accidentally:

void *p;  /* error: must be 'const void *p;' */
myAccessFunc(&p);
Greg Rogers
+1  A: 

So you want to prevent modification of via the pointer you return? Try this:

const void* myAccessFunc(bool string);

What is wrong with this? If you are going to vote down a valid answer, please leave a comment.

dirkgently
+3  A: 

You have two things you can do:

  • Return a const void*
  • Write some documentation for your API telling the users not to modify the returned value

With that said, any API user who decides to try and muck with a pointer like that deserves what they get :P.

17 of 26
+2  A: 

Returning a pointer would be best, but if you absolutely need to make it an out parameter, you could use a struct as an intermediary:

typedef struct _ptr_holder {
   const void* ptr;
} ptr_holder;

void myAccessFunc( bool string, ptr_holder* ptr ) {
...
}

ptr_holder s;
myAccessFunc(true,&s);
printf("Got '%s'\n", s.ptr);

It's hokey, but it should do the trick

Eric Petroelje
Thanks. I think that cure is worse than the disease.
Chris Nelson
+1  A: 

If you are writing a C library where you don't want to expose some internal data-structure it can be good to go an extra route and hide implementation with a unspecified struct typedef. This is also called an opaque types

Example:

In my_interface.h

 #ifndef __MYINTERFACE_H__
 #define __MYINTERFACE_H__

 /* The unspecified struct statement */
 typedef struct s_my_data t_my_data;

 t_my_data  *new_my_data(int someInitializer);
 void        free_my_data(t_my_data *data);
 int         enter_my_data(t_my_data *data, int someDataToEnter);

 const char *return_str_my_data(t_my_data *data);

 #endif /* __MYINTERFACE_H__ */



In my_interface.c

 #include <my_interface.h>

 /* Keep struct in .c file */
 struct s_my_data {
     int    length;
     char  *string;
 };

 t_my_data *new_my_data(int someInitializer)
 {
     /* the exercise */
 }

 void free_my_data(t_my_data *data)
 {
     /* the exercise */
 }

 int enter_my_data(t_my_data *data, int someDataToEnter)
 {
     /* the exercise */
 }

 const char *return_str_my_data(t_my_data *data)
 {
     /* the exercise */
 }
epatel
I really like this solution but for a different problem. Thanks for the clear example.
Chris Nelson
A: