2010-10-26 7 views
2

J'ai besoin d'un moyen facile d'affirmer à l'intérieur d'un modèle qu'un paramètre de modèle implémente une méthode (ou l'une de ses classes parentes). J'ai lu la bibliothèque de chèques Concept, mais il est difficile de trouver un exemple simple pour faire des contrôles simples comme celui-ci.méthode compile time assertion; toujours ne fonctionne pas

J'ai essayé de suivre d'autres postes (comme this one et this other one), que j'ai modifié pour que je puisse le rendre générique pour de nombreux types de méthode (dans mon exemple Foo (methodName) et has_foo (nom Checker) sera, une fois fonctionne correctement, être enveloppé comme arguments macro de sorte qu'il peut être utilisé pour toute méthode)

le code que j'ai pour le moment est celui-ci:

template <typename TypeToBeChecked, typename Sign> 
class has_foo { 
    static_assert(false , "inside root declaration of " "has_foo"); 
public: 
    static const bool result = false; 
}; 

template <typename TypeToBeChecked , typename R> 
class has_foo < TypeToBeChecked , R(void) > 
{ 
    static_assert(false , "inside specialization of " "has_foo" " for R(void)"); 
    class yes { char m;}; 
    class no { yes m[2];}; 
    struct BaseMixin { R Foo(){} }; 
    struct Base : public TypeToBeChecked, public BaseMixin {}; 
    template <typename T, T t> class Helper{}; 
    template <typename U> static no deduce(U*, Helper<R (BaseMixin::*)(), &U::Foo>* = 0); 
    static yes deduce(...); 
public: 
    static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
}; 

template <typename TypeToBeChecked , typename R , typename ARG1> 
class has_foo< TypeToBeChecked , R(ARG1) > 
{ 
    static_assert(false , "inside specialization of " "has_foo" " for R(ARG1)"); 
    class yes { char m;}; 
    class no { yes m[2];}; 
    struct BaseMixin { R Foo(ARG1){} }; 
    struct Base : public TypeToBeChecked, public BaseMixin {}; 
    template <typename T, T t> class Helper{}; 
    template <typename U> 
    static no deduce(U*, Helper<R (BaseMixin::*)(ARG1), &U::Foo>* = 0); 
    static yes deduce(...); 
public: 
    static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
}; 

template <typename TypeToBeChecked , typename R , typename ARG1 , typename ARG2> 
class has_foo< TypeToBeChecked , R(ARG1, ARG2) > 
{ 
    static_assert(false , "inside specialization of " "has_foo" " for R(ARG1 , ARG2)"); 
    class yes { char m;}; 
    class no { yes m[2];}; 
    struct BaseMixin { R Foo(ARG1,ARG2){} }; 
    struct Base : public TypeToBeChecked, public BaseMixin {}; 
    template <typename T, T t> 
    class Helper{}; 
    template <typename U> 
    static no deduce(U*, Helper<R (BaseMixin::*)(ARG1,ARG2), &U::Foo>* = 0); 
    static yes deduce(...); 
public: 
    static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
}; 

template< typename Type > 
struct Connector 
{ 
    static_assert(has_foo< Type , int(int, double) >::result , "Type has no Foo method"); 
    void Operate() { 
     Type t; 
     t.Foo(3); 
    } 
}; 

struct Bla1 { int Foo(double f) { return (int)f; } }; 
struct Bla2 { int Foo(int g, double h) { return g+(int)h;} }; 

int main() 
{ 
    //Connector<Bla1> a; 
    Connector<Bla2> b; 
}; 

Quand je compile ce code exemple (g ++ 4.4. 3 ubuntu avec l'option -std = C++ 0x pour que le static_assert soit reconnu) je reçois ceci:

$ g++ test.cpp -std=c++0x -o test 
test.cpp:72: error: static assertion failed: "inside root declaration of has_foo" 
test.cpp:79: error: static assertion failed: "inside specialization of has_foo for R(void)" 
test.cpp:93: error: static assertion failed: "inside specialization of has_foo for R(ARG1)" 
test.cpp:108: error: static assertion failed: "inside specialization of has_foo for R(ARG1 , ARG2)" 

attente juste là, (notez que connecteur < Bla1> a est commenté) ma première question est:

1) Ai-je raison de supposer que si l'affirmation est en cours d'évaluation, le modèle contenant est étant instancié?

EDIT: répondu par GMan: static_assert est évalué pendant l'analyse, et non lorsque le modèle est instancié. Remplacer false par sizeof (TypeToBeChecked) == 0 le lie à l'heure de compilation

2) Ai-je raison de supposer que puisque l'assertion statique à l'intérieur de la classe du modèle Connecteur instancie has_foo avec la signature int (int, double), alors les spécialisations sans paramètre et sans paramètre NE DOIVENT PAS être instanciées? Qu'est-ce qui ne va pas avec mes hypothèses?

EDIT: cette hypothèse est juste, mais maintenant que je fixe selon 1) réponse, le processus d'instanciation est maintenant comporte comme prévu

3) si je décommenter le connecteur < Bla1> une ligne, j'attendre échouer (puisque Bla1 n'a qu'un Foo avec une seule signature de paramètre, mais ce n'est pas le cas.) Toute idée de ce qui pourrait être erroné, en tenant particulièrement compte du premier article lié

+1

La statique aFFIRME toujours déclencher. 'false' est faux, indépendamment de tout ce que vous pourriez éventuellement faire, donc le compilateur doit le déclencher ici et là. Si vous voulez qu'il soit dépendant, faites quelque chose comme 'sizeof (TypeToBeChecked) == 0', qui est à la fois faux et dépendant. – GManNickG

+0

intéressant, donc static_assert est évalué au moment de l'analyse, plutôt que de compiler – lurscher

Répondre

2

En tenant compte également des commentaires sur la réponse dans le première question liée (this one), votre template vérifie s'il y a un membre Foo, mais il ne vérifie pas la signature de ce mber.

Pour vérifier la signature, vous avez besoin du code comme ceci (les chèques pour operator() qui peuvent être invoquées avec des arguments spécifiés, reproduits à partir this usenet après le comp.lang.C++ animés par [email protected]):

template <typename Type> 
class has_member 
{ 
    class yes { char m;}; 
    class no { yes m[2];}; 

    struct BaseMixin 
    { 
    void operator()(){} 
    }; 

    struct Base : public Type, public BaseMixin {}; 

    template <typename T, T t> class Helper{}; 

    template <typename U> 
    static no deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* 
= 0); 
    static yes deduce(...); 

public: 
    static const bool result = sizeof(yes) == sizeof(deduce((Base*) 
(0))); 

}; 

namespace details 
{ 
    template <typename type> 
    class void_exp_result 
    {}; 

    template <typename type, typename U> 
    U const& operator,(U const&, void_exp_result<type>); 

    template <typename type, typename U> 
    U& operator,(U&, void_exp_result<type>); 

    template <typename src_type, typename dest_type> 
    struct clone_constness 
    { 
    typedef dest_type type; 
    }; 

    template <typename src_type, typename dest_type> 
    struct clone_constness<const src_type, dest_type> 
    { 
    typedef const dest_type type; 
    }; 

} 

template <typename type, typename call_details> 
struct is_call_possible 
{ 
private: 
    class yes {}; 
    class no { yes m[2]; }; 

    struct derived : public type 
    { 
    using type::operator(); 
    no operator()(...) const; 
    }; 

    typedef typename details::clone_constness<type, derived>::type 
derived_type; 

    template <typename T, typename due_type> 
    struct return_value_check 
    { 
    static yes deduce(due_type); 
    static no deduce(...); 
    static no deduce(no); 
    static no deduce(details::void_exp_result<type>); 
    }; 

    template <typename T> 
    struct return_value_check<T, void> 
    { 
    static yes deduce(...); 
    static no deduce(no); 
    }; 

    template <bool has, typename F> 
    struct impl 
    { 
    static const bool value = false; 
    }; 

    template <typename arg1, typename r> 
    struct impl<true, r(arg1)> 
    { 
    static const bool value = 
     sizeof(
      return_value_check<type, r>::deduce(
      (((derived_type*)0)->operator()(*(arg1*)0), 
details::void_exp_result<type>()) 
         ) 
      ) == sizeof(yes); 

    }; 

    // specializations of impl for 2 args, 3 args,.. 
public: 
    static const bool value = impl<has_member<type>::result, 
call_details>::value; 

}; 

exemple d'utilisation:

struct Foo 
{ 
    void operator()(double) const {} 
    void operator()(std::string) const {} 

}; 

int main() 
{ 
    STATIC_ASSERT((is_call_possible<Foo, void(double)>::value)); 
    STATIC_ASSERT((is_call_possible<Foo, void(int)>::value)); 
    STATIC_ASSERT((is_call_possible<Foo, void(const char *)>::value)); 
    STATIC_ASSERT((!is_call_possible<Foo, void(void *)>::value)); 

} 
Questions connexes