64 votes

Intégrer le nom du type dans la sortie de static_assert ?

J'aime donner des erreurs / messages utiles, et je veux également le faire pour mes static_assert. Le problème, c'est qu'ils dépendent des paramètres de modèle. Normalement, ces paramètres seront affichés d'une manière ou d'une autre en raison de l'erreur levée, mais ils sont soit obscurs, soit non regroupés pour qu'ils aient du sens. Exemple:

template
struct fake_dependency{
  static bool const value = false;
};

template
struct Foo{
  Foo(){}

  template
  Foo(Foo const&){
    static_assert(fake_dependency::value, "Impossible de créer Foo à partir de Foo.");
  }
};

int main(){
    Foo fA;
    Foo fB(fA);
}

Sortie sur MSVC:

src\main.cpp(74): error C2338: Impossible de créer Foo à partir de Foo.
          src\main.cpp(84) : voir la référence à l'instanciation de modèle de fonction 'Foo::Foo(const Foo &)' en cours de compilation
          with
          [
              T=int,
              Tag=main::TagB
          ]

Un tag est mentionné dans le modèle de fonction lui-même, l'autre ci-dessous avec le modèle de classe. Pas très agréable. Voyons ce que GCC produit:

prog.cpp: Dans le constructeur 'Foo::Foo(const Foo&) [avec OtherTag = main()::TagA, T = int, Tag = main()::TagB]':
prog.cpp:18:32:   instancié à partir d'ici
prog.cpp:12:5: erreur: assertion statique échouée: "Impossible de créer Foo à partir de Foo."

Beaucoup mieux, mais pas vraiment là où se trouve le static_assert. Et maintenant imaginez encore plus de paramètres, ou plus de modèles, ou les deux. frissons

Une façon de contourner cela est d'utiliser une structure intermédiaire, qui prend les deux Tags en tant que paramètres de modèle:

template
struct static_Foo_assert{
    static_assert(fake_dependency::value, "Impossible de créer Foo à partir de Foo.");
};

template
struct Foo{
  Foo(){}

  template
  Foo(Foo const&){
      static_Foo_assert x;
  }
};

Voyons maintenant la sortie à nouveau:

src\main.cpp(70): error C2338: Impossible de créer Foo à partir de Foo.
          src\main.cpp(79) : voir la référence à l'instanciation du modèle de classe 'static_Foo_assert' en cours de compilation
          with
          [
              Tag=main::TagB,
              OtherTag=main::TagA
          ]

Beaucoup mieux! Voici ce que GCC dit:

prog.cpp: Lors de l'instantiation de 'static_Foo_assert':
prog.cpp:17:40:   instancié à partir de 'Foo::Foo(const Foo&) [with OtherTag = main()::TagA, T = int, Tag = main()::TagB]'
prog.cpp:23:32:   instancié à partir de ici
prog.cpp:8:5: erreur: assertion statique échouée: "Impossible de créer Foo à partir de Foo."

Ça ne semble pas mal. Le problème: je dois créer une telle structure pour chaque modèle, car le message d'erreur dans static_assert doit être un littéral de chaîne...

Maintenant, pour ma question: Pouvons-nous inclure d'une manière ou d'une autre les noms de type directement dans le static_assert? Par exemple

static_assert(..., "Impossible de créer Foo<" T "," Tag "> à partir de Foo<" T "," OtherTag ">.");

Exemple de sortie:

Impossible de créer Foo à partir de Foo.

Ou, si cela n'est pas réalisable, pouvons-nous d'une manière ou d'une autre rendre le message d'erreur un paramètre de modèle supplémentaire, pour le rendre transmissible?

11 votes

Je voudrais voir les compilateurs devenir meilleurs ici. Il doit être possible de montrer la condition qui a échoué. Il pourrait dire note: dans la vérification static_assert pour fake_dependency::value [avec T = ...] (entre crochets, il énumère tous les paramètres de modèle utilisés dans l'expression). Espérons !

0 votes

@Johannes: Je pense que cela pourrait être fait avec les modèles d'expression constexpr, tu ne trouves pas ? Comme vous pouvez déjà disséquer les expressions / conditions d'exécution comme le fait le framework de test unitaire Check.

0 votes

C'est dommage que les concepts n'aient pas été intégrés dans C++0x, car cela aurait grandement réduit le besoin comme cela se présente actuellement typeid ou dynamic_cast sont la seule façon de déterminer le type et nécessitent tous deux une instance que vous n'avez pas avec un argument de modèle.

15voto

Bob Fincheimer Points 6849

Mon Hack

Code :

template 
struct AssertValue : AssertionChecker
{
    static_assert(AssertionValue, "Assertion failed ");
    static bool const value = Assertion::value;
};

Cela vous permet de vérifier toute assertion :: value et d'afficher les types en cas d'échec.

Utilisation :

// Mauvaise indentation utilisée pour montrer les parties
static_assert(
    AssertValue<
        std::my_check<
            T0, decltype(*somethingComplicated), T7::value_type
        >
    >, 
    "quelque chose d'horrible s'est produit"
);

std.my_check<...>:: value est le résultat booléen de la vérification

Exemple

Pour un exemple complet SSCCE voir: Exemple IDEOne

Message d'erreur de l'exemple :

prog.cpp: lors de l'instantiation de 'AssertValue >':
prog.cpp:37:69:   instancié de 'void MyFunction(IteratorType, IteratorType) [avec IteratorType = __gnu_cxx::__normal_iterator >]'
prog.cpp:60:38:   instancié de ici
prog.cpp:9:5: erreur: assertion statique échouée : "Assertion failed "
prog.cpp: lors de l'appel de 'void MyFunction(IteratorType, IteratorType) [avec IteratorType = __gnu_cxx::__normal_iterator >]':
prog.cpp:60:38:   instancié de ici
prog.cpp:39:5: erreur: assertion statique échouée : "iterator passé ne fait pas référence à des éléments IMyInterface"

Explication

Si l'assertion échoue, elle affichera les arguments de modèle d'AssertValue et affichera donc l'expansion complète du modèle de votre vérification. Par exemple, si vous vérifiez un std::is_base_of cela affichera le type complet de la vérification, par exemple : std::is_base_of. Vous savez alors exactement quels types ont été utilisés dans l'assertion en échec.

Le seul problème est que cela ne fonctionne que sur des modèles qui placent leur résultat dans :: value . Cependant, type_traits utilise principalement cela et est la norme à suivre.

0 votes

Pas aussi agréable que je le voudrais, mais ce n'est pas non plus mal. Je pense que tu veux dire cependant, en se basant sur les erreurs de GCC et de MSVC.

0 votes

@Xeo J'ai vu ci-dessous parce que je pense que MSVC met d'abord l'assertion échouée avant de sortir les types... GCC fait l'inverse

17 votes

Exemple Ideone plus disponible... ideone.com/AcwsCA Et le code de réponse n'est pas complet, AssertionChecker manquant.

3voto

Alan Stokes Points 9095

Il est possible d'obtenir une chaîne littérale passée en tant que paramètre non typé de modèle, avec un peu de saut de cerceau. Mais comme le deuxième argument de static_assert est contraint d'être une chaîne littérale plutôt qu'une expression constante d'adresse, malheureusement, cela n'est pas très utile.

Malheureusement, je crains que votre meilleure option soit de faire pression sur le comité ou les rédacteurs du compilateur pour étendre la fonctionnalité.

3voto

Spencer Simpson Points 38

Si votre compilateur fournit le macro __FUNCTION__, vous pouvez effectuer une substitution vraiment simple en l'utilisant et en effectuant une concaténation littérale. Cependant, l'implémentation de gcc et de clang n'est pas faite en tant que macro, donc cette solution ne fonctionnera pas pour eux.

#include "stdafx.h"
#include <type_traits>

template <class T>
class must_be_pod
{
    static void test() { static_assert (std::is_pod<T>::value, __FUNCTION__ ": not a POD"); }
public:
    must_be_pod() { test(); }
};

class not_a_pod
{
public:
    not_a_pod() {}
    virtual ~not_a_pod() {}
};

int main()
{
    must_be_pod<not_a_pod> should_fail; // et c'est le cas
    return 0;
}

Cela produit la sortie suivante lorsqu'il est compilé par VS2015 :

static_assert_test.cpp(10): error C2338: must_be_pod<class not_a_pod>::test: not a POD

-1voto

Daniel Duvilanski Points 133

Je vois que cela a été répondu il y a un moment, mais la réponse complète a été perdue, et j'ai trouvé un moyen très simple d'obtenir le résultat souhaité.

template 
static typename std::enable_if::type FunctionWithReadableErrorMessage()
{
}

int main()
{
    FunctionWithReadableErrorMessage();
    return 0;
}

Cette fonction se compilera et n'aura aucun effet si value=true, sinon nous obtiendrons ce message d'erreur :

main.cpp: In function 'int main()': main.cpp:16:50: error: no matching function for call to 'FunctionWithReadableErrorMessage()' FunctionWithReadableErrorMessage(); ^

Si on veut être un peu plus général, on peut le mettre dans un macro

-4voto

pezcode Points 2596

std::type_info a un membre const char* name():

#include 
using namespace std;

//...

const char* name = type_info(T).name();

4 votes

Ce n'est pas une chaîne littérale et ne peut donc pas être utilisée dans static_assert. De plus, le "nom" est dépendant du compilateur et/ou de la plate-forme. :/

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