58 votes

C++ argument de fonction de sécurité

Dans une fonction qui prend plusieurs arguments du même type, comment pouvons-nous garantir que l'appelant ne pas gâcher la commande?

Par exemple

void allocate_things(int num_buffers, int pages_per_buffer, int default_value ...

et plus tard

// uhmm.. lets see which was which uhh..
allocate_things(40,22,80,...

69voto

Dietrich Epp Points 72865

Une solution classique est de mettre les paramètres dans une structure, avec des champs nommés.

AllocateParams p;
p.num_buffers = 1;
p.pages_per_buffer = 10;
p.default_value = 93;
allocate_things(p);

Vous n'avez pas à utiliser des champs, bien sûr. Vous pouvez utiliser les fonctions de membre ou ce que vous voulez.

30voto

Nir Friedman Points 3165

Deux bonnes réponses, un de plus: une autre approche serait d'essayer de tirer parti de la type de système dans la mesure du possible, et de créer des typedefs. Par exemple, à l'aide de favoriser une forte typedef (http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/strong_typedef.html).

BOOST_STRONG_TYPEDEF(int , num_buffers);
BOOST_STRONG_TYPEDEF(int , num_pages);

void func(num_buffers b, num_pages p);

L'appel de la touche func avec des arguments dans le mauvais ordre maintenant serait une erreur de compilation.

Un couple de notes sur ce. Tout d'abord, de boost forte typedef est plutôt daté dans son approche; vous pouvez faire beaucoup plus agréable des choses avec variadic PFI et éviter les macros complètement. Deuxièmement, évidemment, cela présente une surcharge, que vous avez souvent de convertir explicitement. Donc, en général, vous ne voulez pas en abuser. C'est vraiment sympa pour des choses qui reviennent encore et encore dans votre bibliothèque. Pas très bon pour les choses qui viennent comme une. Ainsi, par exemple, si vous écrivez un GPS de la bibliothèque, vous devez avoir un bon double typedef pour les distances en mètres, une forte int64 typedef pour le temps passé à l'époque, en nanosecondes, et ainsi de suite.

30voto

sergej Points 1749

Si vous avez un compilateur C++11, vous pouvez utiliser définis par l'utilisateur littéraux en combinaison avec les types définis par l'utilisateur. Voici une approche naïve:

struct num_buffers_t {
    constexpr num_buffers_t(int n) : n(n) {}  // constexpr constructor requires C++14
    int n;
};

struct pages_per_buffer_t {
    constexpr pages_per_buffer_t(int n) : n(n) {}
    int n;
};

constexpr num_buffers_t operator"" _buffers(unsigned long long int n) {
    return num_buffers_t(n);
}

constexpr pages_per_buffer_t operator"" _pages_per_buffer(unsigned long long int n) {
    return pages_per_buffer_t(n);
}

void allocate_things(num_buffers_t num_buffers, pages_per_buffer_t pages_per_buffer) {
    // do stuff...
}

template <typename S, typename T>
void allocate_things(S, T) = delete; // forbid calling with other types, eg. integer literals

int main() {
    // now we see which is which ...
    allocate_things(40_buffers, 22_pages_per_buffer);

    // the following does not compile (see the 'deleted' function):
    // allocate_things(40, 22);
    // allocate_things(40, 22_pages_per_buffer);
    // allocate_things(22_pages_per_buffer, 40_buffers);
}

9voto

chux Points 13185

(Remarque: le post a été à l'origine tagged 'C`)

Le C99 à partir de permet une extension à @Dietrich Ppe idée: littéral composé

struct things {
  int num_buffers;
  int pages_per_buffer;
  int default_value 
};
allocate_things(struct things);

// Use a compound literal
allocate_things((struct things){.default_value=80, .num_buffers=40, .pages_per_buffer=22});

Pourrait même passer l'adresse de la structure.

allocate_things(struct things *);

// Use a compound literal
allocate_things(&((struct things){.default_value=80,.num_buffers=40,.pages_per_buffer=22}));

7voto

Frank Puffer Points 5064

Vous ne pouvez pas. C'est pourquoi il est recommandé d'avoir aussi peu d'arguments de la fonction que possible.

Dans votre exemple, vous pourriez avoir des fonctions distinctes comme set_num_buffers(int num_buffers), set_pages_per_buffer(int pages_per_buffer) etc.

Vous avez probablement remarqué vous-même qu' allocate_things n'est pas un bon nom, car il ne peut pas exprimer à ce que la fonction est en train de faire. Surtout je ne m'attends pas à définir une valeur par défaut.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X