2012-01-21 3 views
5

Nous avons des classes de modèles complexes qui ont des méthodes qui ne fonctionneront pas avec certaines politiques ou types. Par conséquent, lorsque nous détectons ces types (au moment de la compilation, en utilisant des caractères de type), nous envoyons une assertion statique avec un bon message.Puis-je exclure certaines méthodes de l'instanciation de modèle manuelle?

Maintenant, nous faisons beaucoup d'instanciation manuelle de modèle. En partie, c'est pour que les méthodes soient obligées de compiler pour vérifier la syntaxe des méthodes. Cela réduit également le temps de compilation pour l'utilisateur de la bibliothèque. Le problème est que les assertions statiques sont toujours lancées et par conséquent nous ne pouvons pas instancier manuellement la classe de modèle en question.

Y a-t-il une solution de contournement pour cela?

EDIT: Pour le rendre plus clair, voici un exemple (l'instanciation explicite dans ce cas échouera sur someFunc1():

// header 
template <typename T> 
class someClass 
{ 
    void someFunc() {} 
    void someFunc1() { static_assert(false, assertion_failed); } 
}; 

// source 
template someClass<int>; // Explicit instantiation 

EDIT2. Voici un autre exemple Cette fois-ci vous peut compiler pour voir ce que je veux dire.Compiler d'abord tout de suite.Le code devrait compiler.Puis Décommenter [2] et l'assertion statique devrait se déclencher maintenant commenter [2] et Ne pas commenter [1]. assertion statique wi Il se déclenchera car vous instanciez explicitement le modèle. Je veux éviter de supprimer l'instanciation explicite en raison des avantages qui en découlent (voir ci-dessus pour les avantages).

namespace Loki 
{ 
    template<int> struct CompileTimeError; 
    template<> struct CompileTimeError<true> {}; 
} 

#define LOKI_STATIC_CHECK(expr, msg) \ 
    { Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 

template <typename T> 
class foo 
{ 
public: 

    void func() {} 
    void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); } 
}; 

template foo<int>; 
//template foo<double>; // [1] 

int main() 
{ 
    foo<int> a; 
    a.func1(); 

    foo<double> b; 
    //b.func1(); //[2] 

    return 0; 
} 
+0

Il n'est pas clair à partir de la description quel est le problème, mais de l'ampleur du problème, il semble que enable_if est pour vous (http://www.boost.org/doc/libs/1_48_0/libs/utility/ enable_if.html) – bobah

+0

@bobah: Ce n'est pas une mauvaise idée du tout ... Je vais m'y pencher. Si vous pouvez préparer un exemple simple que je peux vérifier, je le marquerai comme une réponse? – Samaursa

+1

Je suis d'accord avec les autres qui se plaignent du manque de détails. Mais de toute façon, cela m'a sonné: "Le problème est que les assertions statiques sont toujours tirées". http://www.boost.org/doc/libs/1_48_0/doc/html/boost_staticassert.html#boost_staticassert.templates, dernière remarque: est-ce le problème que vous rencontrez? –

Répondre

0

Je n'ai pas eu l'occasion de tester enable_if comme suggéré par BOBAH mais je suis venu avec une solution qui ne nécessite pas stimuler et qui satisfait mon exigence initiale dans une bonne mesure (je dis bonne et non pleine, expliquera à la fin)

la solution est de mettre un modèle factice sur le code qui échouera si compilé sous un sélectionné types et est bien sous d'autres. Alors:

struct dummyStruct {}; 

#define DUMMY_TEMP typename dummy 
#define DUMMY_PARAM dummyStruct 

namespace Loki 
{ 
    template<int> struct CompileTimeError; 
    template<> struct CompileTimeError<true> {}; 
} 

#define LOKI_STATIC_CHECK(expr, msg) \ 
{ Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 

template <typename T> 
class foo 
{ 
public: 

    void func() {} 
    template <typename T_Dummy> 
    void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); } 
}; 

template foo<int>; 
template foo<double>; // [1] 

int main() 
{ 
    foo<int> a; 
    a.func1<DUMMY_PARAM>(); 

    foo<double> b; 
    //b.func1<DUMMY_PARAM>(); //[2] - this is a static error 

    return 0; 
} 

Dans tous mon code de modèle, ce genre de fonctions (c.-à-celles qui ont statique Affirme ou le travail sur certains types et peut échouer sur d'autres en utilisant des traits de type [dans ce cas, il y a une sélection de plusieurs fonctions différentes pour différents types]) sont cachés du client. Donc, dans ma mise en œuvre, l'ajout du dummy parameter supplémentaire est un bon compromis.

En prime, cela me permet de savoir que cette fonction est conçue pour être utilisée uniquement par certains types. De plus, mon problème original d'instanciation explicite est résolu par cette technique simple.

2

D'accord, donc si vous forçant l'instanciation de toutes les méthodes utilisant instanciation explicite, vous ne pouvez pas vous en sortir avec des astuces de temps de compilation pour empêcher l'instanciation des méthodes incriminées, telles que enable_if. Il serait assez facile de déplacer l'erreur vers l'exécution, mais ce n'est pas souhaitable.

Je pense que le mieux que vous pouvez faire est de déplacer l'erreur vers le temps de liaison, ce qui garantit statiquement que le programme ne contient pas un chemin de code qui pourrait potentiellement appeler la fonction interdite, mais les messages d'erreur ne seront pas très utiles à quiconque ne connaît pas la restriction imposée. Quoi qu'il en soit, la solution est de déclarer une spécialisation des fonctions membres interdites mais pas les définir:

template<typename T> 
struct Foo { 
    void bar() { 
     std::cout << "bar\n"; 
    } 
    void baz() { 
     std:: cout << "baz\n"; 
    } 
}; 

template<> void Foo<int>::baz(); // use of Foo<int>::baz() will resolve to this specialization, and linking will fail 

template struct Foo<int>; 
template struct Foo<char>; 

int main() { 
    Foo<int> f; 
    f.bar(); 
    // f.baz(); // uncommenting this line results in an ugly link time error 
    Foo<char> b; 
    b.bar(); 
    b.baz(); // works with Foo<char> 
} 

La statique affirme ne plus aider à donner des messages d'erreur agréable quand une erreur est faite dans le code client, mais vous voudrez peut-être laissez-les parce qu'ils vont tirer si vous oubliez de fournir une spécialisation.

+1

C'est très bien si vous créez une instance. Dans notre cas, nous utilisons l'instanciation explicite qui cause le problème (voir edit ci-dessus) – Samaursa

+0

Pour éviter toute confusion pour tout futur SOer, mon commentaire ci-dessus est pour la réponse _avant_les modifications (qui a été faite avant que je clarifie ma question avec quelques modifications aussi – Samaursa

+0

+1 ** Cela devrait être la réponse. ** Nécessite un peu de travail redondant mais délivre :) – CodeAngry

1

enable_if est un mécanisme flexible pour le ciblage précis des méthodes de modèle, peut-être ce que vous recherchez. Exemple:

#include <string> 
#include <iostream> 

#include <boost/utility.hpp> 
#include <boost/type_traits.hpp> 
#include <boost/static_assert.hpp> 

template <class T> class mywrapper 
{ 
    T _value; 

    template <class V> 
    typename boost::enable_if<boost::is_scalar<V>, void>::type printval_(V const& value) 
    { 
    BOOST_STATIC_ASSERT(boost::is_scalar<V>::value); 
    std::cout << "scalar: " << value << std::endl; 
    } 

    template <class V> 
    typename boost::enable_if<boost::is_compound<V>, void>::type printval_(V const& value) 
    { 
    BOOST_STATIC_ASSERT(boost::is_compound<V>::value); 
    std::cout << "compound: " << value << std::endl; 
    } 

public: 
    mywrapper(T const& value):_value(value) { } 
    void printval() { printval_(_value); } 
}; 

template class mywrapper<int>; 
template class mywrapper<std::string>; 

int main() 
{ 
    mywrapper<int> ival(333); 
    mywrapper<std::string> sval("test"); 

    ival.printval(); 
    sval.printval(); 
    return 0; 
} 
+0

Je n'ai pas de 'boost' actuellement, mais cela fonctionnera-t-il aussi avec une instanciation explicite? Par exemple, juste avant 'int main()' si vous mettez 'template mywrapper ; template mywrapper ; 'et dans chacune des fonctions, vous avez une assertion statique pour vous assurer que vous travaillez avec des types scalaires et composés respectivement; compilera-t-il alors? – Samaursa

+0

a mis à jour l'exemple avec les assertions statiques et l'instanciation – bobah

+0

Merci bobah! Je vais le tester dès que je pourrai "boost". – Samaursa

3

Vous ne pouvez pas avoir les deux: vous ne pouvez pas avoir une assertion statique pour éviter instanciation et instancier explicitement le type! C'est une contradiction évidente. Ce que vous pouvez avoir, cependant, est une fonctionnalité conditionnelle incluse même si c'est un peu une douleur dans le cou: Si une certaine fonction membre n'est pas censée être supportée pour certains types, vous pouvez déplacer cette fonction dans une classe de base qui l'a conditionnellement . De cette façon, vous n'utilisez pas une assertion statique mais supprimez simplement la fonction membre. Je réalise que cela introduit d'autres problèmes intéressants, par ex. en ce qui concerne l'emplacement des variables membres, mais je pense que dans le contexte que vous décrivez, c'est le meilleur que vous puissiez obtenir.

Voici un exemple rapide de la façon dont cela pourrait ressembler à:

template <typename T, bool = std::numeric_limits<T>::is_integer> struct foo_base; 
template <typename T> struct foo_base<T, false> { /* intentionally left blank */ }; 
template <typename T> struct foo_base<T, true> { void foo() { /*...*/ } }; 

template <typename T> 
struct Foo: foo_base<T> { /* .... */ }; 

template struct Foo<int>; // will have foo() 
template struct Foo<double>; // will not have foo() 
Questions connexes