2017-01-27 8 views
2

Dans mes projets j'utilise exhaustivement boost::any et boost::variant. Pour cela, une routine de conversion générale de boost::any à boost::variant a été conçue dans ma précédente question Generic function to convert boost::any to boost::variant. Merci beaucoup aux personnes qui m'ont aidé.Boost tout à Boost Variant en utilisant le préprocesseur Boost

La solution trouvée a fonctionné pour moi sans aucun problème, mais avait un sérieux inconvénient. Le gonflement du code produit par la solution entièrement modélisée peut être prohibitif et parfois inutile pour une simple conversion. (Je n'ai pas mentionné les temps de compilation.)

Maintenant j'ai eu l'idée de laisser la spécialisation de template fonctionner pour les conversions faciles (non générales), mais j'ai trouvé le code nécessaire fastidieux et sujet aux erreurs à taper. Surtout, si l'on doit faire cette tâche encore et encore.

L'extrait de code suivant illustre le problème. Image que dans une application typique on pourrait avoir jusqu'à 20 types ou plus!

#include <boost/preprocessor.hpp> 
#include <boost/variant.hpp> 
#include <boost/any.hpp> 
#include <boost/optional.hpp> 
#include <iostream> 

template<typename VARIANT> 
boost::optional<VARIANT> anyToVariant(const boost::any& any) { 
    boost::optional<VARIANT> ret; 
    // General implementation omitted. 
    // The implementation is lengthy and produces an enormous code bloat. In some cases it is the best solution. 
    return ret; 
} 

// Specialized Template reduces code bloat. But is error-prone to type write for every new variant type. 
template<> 
boost::optional <boost::variant<int, double, std::string>> anyToVariant(const boost::any& any) { 
    boost::optional<boost::variant<int, double, std::string>> ret; 
    if (any.type() == typeid(int)) { 
     ret = boost::any_cast<int>(any); 
    } 
    if (any.type() == typeid(double)) { 
     ret = boost::any_cast<double>(any); 
    } 
    if (any.type() == typeid(std::string)) { 
     ret = boost::any_cast<std::string>(any); 
    } 
    return ret; 
} 

// Should be implemented with boost preprocessor 
#define BOOST_ANY_TO_VARIANT(TypeList) 

// Better would be a macro like this 
BOOST_ANY_TO_VARIANT(int, double, std::string); // Create the above template specialization 

int main() { 
    boost::variant<int, double, std::string> v; 
    boost::any x = 4; 
    v=*anyToVariant<boost::variant<int, double, std::string>>(x); 
} 

Une meilleure solution serait bien sûr une macro, mais malheureusement je n'étais pas en mesure de mettre en œuvre cette macro avec boost-preprocessor. Je n'ai toujours pas les compétences, mais je suis sûr que cela pourrait être fait.

Quelqu'un avec expérience dans boost-preprocessor peut-il m'aider avec mon problème?

La meilleure solution que je pouvais imaginer serait une macro comme ce qui suit:

#define BOOST_ANY_TO_VARIANT(VariantType) \ 
// Magic? 

typedef boost::variant<int, std::string, double> MyVariant; 

BOOST_ANY_TO_VARIANT(MyVariant) 

Mais je doute que cette solution est réalisable.

+0

Donc, pour être clair, l'objectif de la macro est de générer la définition de la spécialisation du modèle, * pas * convertir un 'boost :: any' sur place? – Quentin

+0

Vous l'avez. La macro devrait simplement générer le code ci-dessus. – Aleph0

Répondre

2

Ici, vous allez:

#define ANY_TO_VARIANT_OP_VARIANT(typeSeq) \ 
    boost::optional<boost::variant<BOOST_PP_SEQ_ENUM(typeSeq)>> 

#define ANY_TO_VARIANT_CONVERT_AND_RETURN(r, data, elem) \ 
    if (any.type() == typeid(elem)) { \ 
     return Ret{boost::any_cast<elem>(any)}; \ 
    } 

#define SPECIALIZE_BOOST_ANY_TO_VARIANT(typeSeq) \ 
    template<> \ 
    ANY_TO_VARIANT_OPT_VARIANT(typeSeq) anyToVariant(const boost::any& any) { \ 
     using Ret = ANY_TO_VARIANT_OPT_VARIANT(typeSeq); \ 
     BOOST_PP_SEQ_FOR_EACH(ANY_TO_VARIANT_CONVERT_AND_RETURN, ~, typeSeq) \ 
     return Ret{}; \ 
    } 

Utilisation:

SPECIALIZE_BOOST_ANY_TO_VARIANT((int)(double)(std::string)) 

See it live on Coliru

+0

Merci beaucoup. Je vais vérifier votre solution. Il semble que je devrais vraiment faire un peu plus attention à 'boost-preprocessor'. La solution était beaucoup plus courte que ce à quoi je m'attendais. – Aleph0

+0

Je n'étais même pas au courant de BOOST_PP_REMOVE_PARENS! C'était déjà le point que je n'arrive pas à faire progresser. – Aleph0

+0

@FrankSimon Je pense que vous pourriez également utiliser 'BOOST_PP_SEQ_ENUM (typeSeq)'. – llonesmiz