Domanda

Sto scrivendo una funzione di accesso che restituisce un puntatore a un buffer interno e mi piacerebbe suggerire agli utenti di mia funzione che non dovrebbero aggiornare l'oggetto che ha puntato. Un esempio molto artificiosa potrebbe essere:

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);

Sì, lo so che sembra flakey.

Quello che voglio evitare è:

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

Lo so che non posso impedirlo del tutto ma mi piacerebbe almeno come l'avviso del compilatore per accennare a un utente che non dovrebbero fare questo. Se gettano il mio puntatore, allora questo è il loro problema. C'è qualche combinazione di const e vuoto e * che farà questo? Ho provato qualcosa di simile:

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

ma sembrava farla finita con il vuoto del puntatore in modo un chiamante aveva a che fare:

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

o

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

e non poteva fare:

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

ho finalmente venuto intorno a:

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

e se l'utente fa:

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

il compilatore (GCC, almeno), non si lamentano buttare via il qualificatore.

È stato utile?

Soluzione

Sarebbe meglio fare qualcosa di simile:

const void * myAccessFunc();

Dove si ritorna il puntatore per gli interni, questo è un po 'più naturale di passarlo come parametro out.

Se si dovesse passare come un parametro out, si vorrebbe:

void myAccessFunc(const void **p);

, che impedisce loro di fare questo accidentalmente:

void *p;  /* error: must be 'const void *p;' */
myAccessFunc(&p);

Altri suggerimenti

Hai due cose che puoi fare:

  • Restituisce un vuoto const *
  • Scrivi po 'di documentazione per la vostra API raccontare gli utenti di non modificare il valore restituito

Detto questo, qualsiasi utente API che decide di cercare di fango con un puntatore del genere merita quello che ottiene:.

La prevenzione è impossibile, dal momento che si può gettare via costanza. Se fate il vostro parametro di funzione const, dovrete lanciare via da soli all'interno della funzione. Faresti anche essere mentendo a chiunque di utilizzare la funzione e potrebbero causare ogni sorta di insetti divertenti.

Si potrebbe provare a restituire un puntatore, invece. Poi almeno non sarebbe violare il proprio const. Che potrebbe non essere appropriato in questo caso però.

Restituzione di un puntatore sarebbe meglio, ma se si assolutamente è necessario renderlo un parametro out, è possibile utilizzare una struttura come intermediario:

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);

E 'hokey, ma dovrebbe fare il trucco

Così si vuole impedire la modifica di via del puntatore si torna? Prova questo:

const void* myAccessFunc(bool string);

Cosa c'è di sbagliato in questo? Se avete intenzione di votare giù una risposta valida, si prega di lasciare un commento.

Se si sta scrivendo una libreria C in cui non si desidera esporre alcuni dati-struttura interna può essere buona per andare un extra di route e nascondere implementazione con un typedef struct non specificato. Questo è chiamato anche un tipi opachi

Esempio:

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 */
 }

E 'questa C o C ++? Perché nel caso int si sta passando il puntatore ad una static const? L'interfaccia è sospetto.

di sovraccarico e di sbarazzarsi del booleano nell'interfaccia:

void myAccessFunc( const char* * const p, size_t* len){
   *p = "blahblahblah";
   *len = 12;
}

void myAccessFunc( const int* * const ppI, size_t* len){
   static const int i = 123;
   *ppI = &i;
   *len = 4;
}

Si libera del test troppo. Dal momento che l'utente conosce vero o falso per la funzione originaria sa quale usare. Tipizzazione forte è tuo amico. Si libera della classe di errori in cui tipo e puntatore sono incompatibili ...

A proposito passando tali flag di controllo contro il sovraccarico è indesiderabile, specialmente in piccoli metodi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top