2010-05-25 5 views
4

Difficile de trouver un bon titre pour cette question. Ce dont j'ai réellement besoin, c'est de pouvoir fournir des paramètres de gabarit avec un nombre différent d'arguments à la place d'un seul paramètre. Ne fait pas beaucoup de sens, donc je vais aller sur la raison:Conception basée sur des règles et valeurs par défaut

template < typename T, template <typename,typename> class Policy = default_policy > 
struct policy_based : Policy<T, policy_based<T,Policy> > 
{ 
    // inherits R Policy::fun(arg0, arg1, arg2,...,argn) 
}; 

// normal use: 
policy_base<type_a> instance; 

// abnormal use: 
template < typename PolicyBased > // No T since T is always the same when you use this 
struct custom_policy {}; 

policy_base<type_b,custom_policy> instance; 

L'affaire est que pour beaucoup anormal utilise la politique sera basé sur un seul type T, et ne peut pas vraiment être paramétrées sur T, cela n'a aucun sens de prendre T comme paramètre. Pour d'autres utilisations, y compris la valeur par défaut, une politique peut avoir du sens avec n'importe quel T.

J'ai quelques idées mais aucune d'elles n'est vraiment favorite. Je pensais que j'avais une meilleure réponse - en utilisant la composition au lieu de politiques - mais ensuite je me suis rendu compte que cette affaire avait besoin d'informations supplémentaires que la classe elle-même n'aurait pas. C'est comme la troisième fois que j'ai refaçonné cette construction stupide et j'en ai quelques versions personnalisées que j'essaie de consolider. Je voudrais obtenir quelque chose cloué cette fois plutôt que de simplement pêcher et espérer que cela fonctionne cette fois. Donc, je suis en train de pêcher des idées en ce moment en espérant que quelqu'un a quelque chose que je serai tellement impressionné par cela que je vais changer de divinités. Quelqu'un a une bonne idée? Editer: Vous vous demandez peut-être pourquoi je ne récupère pas simplement T de la définition de politique basée sur le modèle pour default_policy. La raison en est que default_policy est vraiment spécialisé pour certains types T. Depuis que j'ai posé la question, j'ai trouvé quelque chose qui peut être ce dont j'ai besoin, ce qui va suivre, mais je pourrais encore utiliser d'autres idées.

template < typename T > 
struct default_policy; 

template < typename T, template <typename> class Policy = default_policy > 
struct test : Policy<test<T,Policy>> 
{}; 


template < typename T > 
struct default_policy< test<T, default_policy> > 
{ 
    void f() {} 
}; 

template < > 
struct default_policy< test<int, default_policy> > 
{ 
    void f(int) {} 
}; 

Edit: déconner encore avec elle. Je n'étais pas trop friand de ce qui précède car il fait en sorte que default_policy est couplé en permanence à "test" et ne peut donc pas être réutilisé dans une autre méthode, comme avec plusieurs modèles comme suggéré ci-dessous. Il ne s'échelonne pas du tout et nécessite une liste de paramètres au moins aussi longue que "test". Essayé quelques approches différentes qui ont échoué jusqu'à ce que je trouve une autre qui semble fonctionner jusqu'à présent:

template < typename T > 
struct default_policy; 

template < typename T, template <typename> class Policy = default_policy > 
struct test : Policy<test<T,Policy>> 
{}; 

template < typename PolicyBased > 
struct fetch_t; 

template < typename PolicyBased, typename T > struct default_policy_base; 

template < typename PolicyBased > 
struct default_policy : default_policy_base<PolicyBased, typename fetch_t<PolicyBased>::type> {}; 

template < typename T, template <typename> class Policy > 
struct fetch_t< test<T,Policy> > { typedef T type; }; 

template < typename PolicyBased, typename T > 
struct default_policy_base 
{ 
    void f() {} 
}; 

template < typename PolicyBased > 
struct default_policy_base<PolicyBased,int> 
{ 
    void f(int) {} 
}; 

Répondre

1

J'ai eu un problème similaire, et ne pas trouver une réponse souhaitable. D'après ce que je sais, C++ n'a pas un support élégant pour des quantités variables d'arguments de modèles, donc vous devez contourner le problème en "enveloppant" des arguments supplémentaires dans une autre classe;

policy_base< twoArgs<part1, part2> > 

Vous pouvez à peu ce un peu en mettant faisant typedefs pour twoArgs, mais pas par beaucoup dans ce cas de base. (Rappelez-vous, vous pouvez "faire" des typedefs basés sur des modèles en utilisant des typedefs de membres dans des classes modélisées.)

ou en ayant de nombreuses déclarations différentes du modèle de base, avec des arguments différents;

template< typename T1 > struct base {...} 
template< typename T1, typename t2 > struct base {...} 
//etc 
+0

Initialement, je n'aime pas l'idée des déclarations multiples puisque le but de ceci est de consolider beaucoup d'objets similaires mais légèrement différents pour partager du code. Avec un classement de base correct, bien que la plupart des trucs copier/coller puissent être retirés; Je devrais tirer plus haut que prévu initialement mais ... L'idée de twoArgs est aussi intéressante et pourrait l'être davantage si les paramètres nommés étaient considérés. Merci. +1 –

1

Avez-vous examiné les paramètres de modèle nommés? Cela vous permet d'avoir de nombreux paramètres avec une valeur par défaut cachée, chacun pouvant être remplacé par un nom (c'est-à-dire dans un ordre arbitraire). C'est une astuce reposant sur plusieurs niveaux d'indirection et d'héritage multiple. Il est décrit au chapitre 16.1 du livre "Templates the Complete Guide" par Vandevoorde & Josuttis. Pour une exposition en ligne, voir here. L'idée est implémentée dans Boost.Parameter

Voici un court résumé de la bibliothèque Boost.Parameter.

Étape 1) vous déclarez chaque paramètre par0 par parN à travers les macros suivantes:

BOOST_PARAMETER_TEMPLATE_KEYWORD(par0) 
BOOST_PARAMETER_TEMPLATE_KEYWORD(par1) 
// ... 
BOOST_PARAMETER_TEMPLATE_KEYWORD(parN)  

Chaque macro définir à la fois une classe régulière par0 dans l'espace de noms tag et un modèle de classe par0 à périmètre d'espace de noms

namespace tag { struct par0; } // keyword tag type 
template <class T> 
struct par0 
: 
    parameter::template_keyword<tag::par0, T> 
{}; 

Étape 2) y ous déclarez votre signature de classe avec les paramètres obligatoires et optionnels:

using boost::mpl::_; 

typedef parameter::parameters< 
    parameter::required<tag::par0> 
    , parameter::optional<tag::par1> 
    // ... 
    , parameter::optional<tag::parN> 
> your_signature; 

Étape 3) vous déclarez votre classe politique

template < 
    class a0 
    , class a1 = parameter::void_ 
    // ... 
    , class aN = parameter::void_ 
> 
struct your_policy 
{ 
    // Create ArgumentPack 
    typedef typename 
     your_signature::bind<a0, a1, /* ... */ ,aN>::type 
    args; 

    typedef typename parameter::value_type< 
     args, tag::par0>::type par0; 

    typedef typename parameter::value_type< 
     args, tag::par1, your_par1_default>::type par1; 

    // ... 

    typedef typename parameter::value_type< 
     args, tag::parN, your_parN_default>::type parN; 
}; 

étape 4) définir vos politiques et spécialisations

template<typename T> 
class default_policy 
: 
    your_policy<T> // all hidden template parameters equal to their defaults 
{}; 

template<typename T> 
class some_par2_specialized_policy 
: 
    your_policy<T, par2<some_override_for_par2> > // note par1 does not have to be overriden!! 
{}; 

Pour une conception basée sur des règles flexible et évolutive, mon expérience est que Boost.Pa rameter fonctionne comme un charme (vous devrez peut-être surcharger BOOST_PARAMETER_MAX_ARITY si vous avez plus de 8 paramètres cachés).

Il devrait être possible d'adapter votre conception à cette technique. Bien sûr, une fois que vous avez introduit des paramètres de modèle supplémentaires qui ont des valeurs par défaut masquées, vous devez sélectionner les cas où ils sont remplacés par le code qui utilise votre classe de stratégie. Ce n'est pas différent lorsque vous utilisez plusieurs traits pour communiquer votre comportement spécialisé au code client, sauf qu'avec les politiques, tout est bien assemblé.

Questions connexes