2009-02-19 14 views
2

J'écris une fonction d'accès qui renvoie un pointeur vers un tampon interne et je voudrais faire savoir aux utilisateurs de ma fonction qu'ils ne devraient pas mettre à jour l'objet pointé à. Un exemple très artificiel serait:Retour d'un pointeur vide sur un objet constant dans C

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

Oui, je sais que l'aspect flakey.

Ce que je veux éviter est:

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

Je sais que je ne peux pas empêcher complètement, mais je au moins comme l'avertissement du compilateur de faire allusion à un utilisateur qu'ils ne devraient pas faire que . Si ils jettent mon pointeur, alors c'est leur problème. Y a-t-il une combinaison de const et void et * qui le fera? J'ai essayé quelque chose comme:

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

mais il semblait en finir avec le pointeur de la vacuité si un appelant a dû faire:

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

ou

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

et ne pouvait pas faire :

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

Je suis finalement venu à :

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

et si l'utilisateur fait:

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

le compilateur (GCC, au moins), ne se plaindre de jeter le qualificatif.

Répondre

10

Il serait préférable de faire quelque chose comme:

const void * myAccessFunc(); 

Où vous retournez le pointeur sur les entrailles, c'est un peu plus naturel que ce passage comme un paramètre de sortie.

Si vous deviez passer comme paramètre, vous voudriez:

void myAccessFunc(const void **p); 

qui les empêche de le faire accidentellement:

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

Prévenir est impossible, car vous pouvez éliminer la constance. Si vous créez votre paramètre de fonction const, vous devrez le rejeter dans la fonction. Vous seriez aussi menteur à quiconque utilisant votre fonction et pourrait causer toutes sortes de bugs amusants.

Vous pouvez essayer de renvoyer un pointeur à la place. Alors vous ne seriez au moins pas en train de violer votre propre const. Cela peut ne pas être approprié dans ce cas cependant.

1

Donc, vous voulez empêcher la modification de via pointeur vous retournez? Essayez ceci:

const void* myAccessFunc(bool string); 

Quel est le problème avec cela? Si vous allez voter pour une réponse valide, veuillez laisser un commentaire.

3

Vous avez deux choses que vous pouvez faire:

  • Retour Un const void *
  • Ecrire une documentation pour votre API disant aux utilisateurs de ne pas modifier la valeur retournée

Avec cette dit, tout utilisateur de l'API qui décide d'essayer et de muck avec un pointeur comme ça mérite ce qu'ils obtiennent: P.

2

De retour un pointeur serait mieux, mais si vous absolument besoin d'en faire un paramètre out, vous pouvez utiliser une struct d'intermédiaire:

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

Il est hokey, mais il devrait faire l'affaire

+1

Merci. Je pense que la guérison est pire que la maladie. –

+0

Pourquoi pas un «vide»? (<- Ajouter 'const's comme désiré) –

1

Si vous écrivez une bibliothèque C où vous ne voulez pas exposer une structure de données interne, il peut être utile d'ajouter route et de masquer l'implémentation avec une structure typedef non spécifiée. Il est également appelé un opaque types

Exemple:

Dans 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__ */ 



Dans 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 */ 
} 
+0

J'aime vraiment cette solution mais pour un problème différent. Merci pour l'exemple clair. –

0

Est-ce C ou C++? Pourquoi dans le cas où vous passez le pointeur à un const statique? L'interface est suspecte.

surcharge et de se débarrasser du booléen dans l'interface:

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

se débarrasse du test aussi. Puisque l'utilisateur sait vrai ou faux pour la fonction d'origine, il sait lequel utiliser. La frappe forte est votre ami. Élimine la classe d'erreurs où le type et le pointeur sont incompatibles ...

Le fait de passer de tels indicateurs de contrôle par rapport à une surcharge n'est pas souhaitable, en particulier dans les petites méthodes.