2017-03-02 5 views
3

J'utilise boost-variant dans mes projets. Dans un scénario, j'ai besoin de classer les types contenus dans mon boost-variant en plusieurs classes. Comme j'ai beaucoup de types dans ma variante, je suis venu à l'idée de définir plusieurs variantes à l'intérieur de mon visiteur. Ces variantes sont essentiellement le mappage type-> classe.Classifier les types dans Boost Variante en utilisant Visitor ne compile pas

Le code ci-dessous illustre ce que je voulais réaliser.

#include <iostream> 
    #include <boost/variant.hpp> 
    #include <string> 

    enum class Type { 
     Integer, 
     Float, 
     NonNumeric 
    }; 

    struct IsNum : public boost::static_visitor<Type> { 
     typedef boost::variant<int, size_t> IntegerTypes; 
     typedef boost::variant<float, double> FloatTypes; 
     typedef boost::variant<std::string> NonNumericTypes; 

     result_type operator()(const IntegerTypes& t) const { 
      return Type::Integer; 
     } 

     result_type operator()(const FloatTypes& t) const { 
      return Type::Float; 
     } 

     result_type operator()(const NonNumericTypes& t) const { 
      return Type::NonNumeric; 
     } 
    }; 

    int main() { 
     boost::variant<int, std::string, double> value; 
     value = 5; 
     IsNum visitor; 
     auto result = value.apply_visitor(visitor); 
    } 

Malheureusement, le code ne sera pas compilé. MSVC se termine par l'erreur de compilation C3066. Il existe différentes possibilités d'appeler un objet de ce type avec ces arguments? Ce peut être l'une des trois fonctions operator().

Mais fondamentalement, je ne peux convertir 5 que vers le type de variante IntegerTypes.

Quelle pourrait être la solution pour ce genre de problème?

Solution propre

Après avoir essayé d'utiliser boost-mpl je suis arrivé à cette solution. La fonction Contains représente un morceau de logiciel réutilisable, qui pourrait être inclus dans d'autres parties de mon programme. Je souhaitais néanmoins que la solution soit plus proche de mon code source original.

#include <iostream> 
#include <boost/variant.hpp> 
#include <string> 
#include <boost/mpl/contains.hpp> 

enum class Type { 
    Integer, 
    Float, 
    NonNumeric 
}; 

template<typename V, typename T> 
using ContainsImpl = typename boost::mpl::contains<typename V::types, T>::type; 

template<typename V, typename T> 
bool Contains(const T&) { 
    return ContainsImpl<V, T>::value; 
} 

struct IsNum : public boost::static_visitor<Type> { 
    typedef boost::variant<int, size_t> IntegerTypes; 
    typedef boost::variant<float, double> FloatTypes; 
    typedef boost::variant<std::string> NonNumericTypes; 

    template<typename T> 
    result_type operator()(const T& t) const { 
     if (Contains<IntegerTypes>(t)) { 
      return Type::Integer; 
     } else if (Contains<FloatTypes>(t)) { 
      return Type::Float; 
     } else if (Contains<NonNumericTypes>(t)) { 
      return Type::NonNumeric; 
     } 

     return Type::NonNumeric; 
    } 
}; 

int main() { 
    boost::variant<int, std::string, double> value; 
    value = 5.; 
    IsNum visitor; 
    auto result = value.apply_visitor(visitor); 
    if (result == Type::Integer) { 
     std::cout << "Integer" << std::endl; 
    } 
    if (result == Type::Float) { 
     std::cout << "Float" << std::endl; 
    } 
    if (result == Type::NonNumeric) { 
     std::cout << "Non Numeric" << std::endl; 
    } 
} 

Répondre

2
#include <string> 
#include <boost/variant.hpp> 
#include <boost/mpl/list.hpp> 
#include <boost/mpl/contains.hpp> 
#include <boost/type_traits.hpp> 

enum class Type 
{ 
    Integer, 
    Float, 
    NonNumeric 
}; 

struct IsNum : public boost::static_visitor<Type> 
{ 
    typedef boost::mpl::list<int, size_t> IntegerTypes; 
    typedef boost::mpl::list<float, double> FloatTypes; 
    typedef boost::mpl::list<std::string> NonNumericTypes; 

    template <typename T> 
    typename boost::enable_if<boost::mpl::contains<IntegerTypes, T>, result_type>::type 
    operator()(const T& t) const 
    { 
     return Type::Integer; 
    } 

    template <typename T> 
    typename boost::enable_if<boost::mpl::contains<FloatTypes, T>, result_type>::type 
    operator()(const T& t) const 
    { 
     return Type::Float; 
    } 

    template <typename T> 
    typename boost::enable_if<boost::mpl::contains<NonNumericTypes, T>, result_type>::type 
    operator()(const T& t) const 
    { 
     return Type::NonNumeric; 
    } 
}; 

DEMO

+0

cool. J'ai également dérivé cette solution maintenant. Cela semble être la seule approche réalisable. Mais lire cela pourrait vous faire mal à la tête. :-) Je posterai aussi ma solution. Merci beaucoup. – Aleph0

+1

@FrankSimon plus d'alternatives incluent: tag-dispatching et en ajoutant une surcharge distincte pour chaque type –

+0

Aucune idée de ce tag-dispatching est. Je pense que je dois apprendre un peu plus. :-) – Aleph0