2017-01-27 4 views
2

In meinen Projekten verwende ich boost::any und boost::variant erschöpfend. Dazu wurde in meiner vorherigen Frage Generic function to convert boost::any to boost::variant eine allgemeine Umwandlungsroutine von boost::any zu boost::variant entwickelt. Vielen Dank an die Leute, die mir geholfen haben.Boost Any to Boost Variante mit Boost Preprocessor

Die gefundene Lösung funktionierte bei mir ohne Probleme, hatte aber einige gravierende Nachteile. Der durch die vollständig templatisierte Lösung erzeugte Code-Blop könnte prohibitiv sein und manchmal für eine einfache Umwandlung unnötig sein. (Ich habe die Kompilierungszeiten nicht erwähnt.)

Jetzt kam ich auf die Idee, die Template-Spezialisierung für die einfachen (nicht-allgemeinen) Konvertierungen arbeiten zu lassen, aber ich fand den notwendigen Code mühsam und fehleranfällig zu tippen. Vor allem, wenn man diese Aufgabe immer wieder machen muss.

Das folgende Code-Snippet veranschaulicht das Problem. Stellen Sie sich vor, dass Sie in einer typischen Anwendung bis zu 20 oder mehr Typen haben können!

#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); 
} 

Eine bessere Lösung wäre ein Makro natürlich sein, aber leider war ich nicht in der Lage diesen Makro mit boost-preprocessor zu implementieren. Mir fehlen immer noch die Fähigkeiten, aber ich bin mir sicher, dass es getan werden könnte.

Kann jemand mit Erfahrung in boost-preprocessor mir mit meinem Problem helfen?

#define BOOST_ANY_TO_VARIANT(VariantType) \ 
// Magic? 

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

BOOST_ANY_TO_VARIANT(MyVariant) 

Aber ich bezweifle, dass diese Lösung erreichbar ist:

Die beste Lösung, die ich vorstellen könnte ein Makro wie folgt wäre.

+0

Also, nur klar zu sein, ist das Ziel des Makros ist die Definition der Template-Spezialisierung zu erzeugen, * nicht * einen 'boost :: any' an Ort und Stelle konvertieren? – Quentin

+0

Sie haben es verstanden. Das Makro sollte nur den obigen Code generieren. – Aleph0

Antwort

2

Hier gehen Sie:

#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{}; \ 
    } 

Verbrauch:

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

See it live on Coliru

+0

Vielen Dank. Ich überprüfe deine Lösung. Scheint, ich muss mich wirklich mit "boost-preprocessor" etwas mehr beschäftigen. Die Lösung war viel kürzer als erwartet. – Aleph0

+0

Ich war nicht einmal BOOST_PP_REMOVE_PARENS bekannt! Das war schon der Punkt, an dem ich keine Fortschritte machen konnte. – Aleph0

+0

@FrankSimon Ich denke du könntest auch 'BOOST_PP_SEQ_ENUM (typeSeq)' benutzen. – llonesmiz