2016-02-12 2 views
2

Предположим, что у вас есть объект boost::any и объект boost::variant.Общая функция для конвертирования boost :: any to boost :: variant

Я ищу универсальную функцию convert, которая принимает параметр шаблона T, являющийся специализированным boost::variant, например. boost::variant<int, std::string> и магически преобразует boost::any в один из доступных типов данных boost::variant.

template<T> 
T convert(const boost::any& any) { 
    // Some generic conversion code here or throw exception if conversion is not possible! 
} 

int main(int argc, char** args) { 
    typedef boost::variant<int, std::string> TVar; 

    boost::any any="Hello World"; 
    TVar variant=convert<TVar>(any); 
    // variant contains "Hello World" 
    return 0; 
} 

Мне интересно, можно ли написать такую ​​функцию или если это может быть невозможно по какой-то причине?

+0

Что-то вроде 'return any_cast (& any)'? Вы должны поймать исключения, и в таком случае вы можете вернуть построенный по умолчанию 'T' или что-то еще, но это то, что вы хотите? – skypjack

+0

Дело в том, что метод convert не должен знать заранее, какой именно boost :: variant <....> он получит. Следовательно, вы не можете реализовать преобразование, используя цепочку ifs, содержащую 'any_cast (& any)'. – Aleph0

ответ

1

Давайте заключите весь код в структура шаблонного по типу варианта

template<class VAR> 
struct ExtractorGenerator 
{ 
    using Extractor = std::function<boost::optional<VAR>(boost::any const &)>; 
    std::vector<Extractor> extractors; 

    template<class T> 
    static boost::optional<VAR> tryCast(boost::any const & arg); 

    template<class T> 
    void operator()(T); 
}; 

Вы можете легко написать функцию, которая для данного типа пытается преобразовать импульс :: любого к варианту этого типа

template<class VAR> 
template<class T> 
boost::optional<VAR> ExtractorGenerator<VAR>::tryCast(boost::any const & arg) 
{ 
    T const * val = boost::any_cast<T>(&arg); 
    return val == nullptr ? boost::none : boost::make_optional(VAR{*val}); 
} 

Теперь, используя boost :: mpl, вы можете выполнять итерацию по всем типам вариантов для генерации функции для типов каждого варианта

template<class VAR> 
template<class T> void ExtractorGenerator<VAR>::operator()(T) 
{ 
    extractors.push_back(Extractor::tryCast<T>); 
} 

typedef boost::variant<int, std::string, char> MyVariant; 
ExtractorGenerator<MyVariant> generator; 
boost::mpl::for_each<MyVariant::types>(boost::ref(generator)); 

И теперь вы просто применить все созданные функции:

std::vector<MyVariant> extractedVals; 
for (auto fun : extractor.extractors) 
{ 
    boost::optional<MyVariant> extracted = fun(val); 
    if (extracted) 
     extractedVals.push_back(extracted.get()); 
} 
+0

Спасибо за помощь. Строка 'using Extractor = std :: function (boost :: any const &)>;' не компилируется с моим компилятором MSVC 2010. Любая идея, что вы хотели выразить? – Aleph0

+0

Ваше решение, наконец, сработало для меня, после небольшого изменения вашего опубликованного кода. Еще раз спасибо. – Aleph0

+0

@FrankSimon Я реорганизовал код, поскольку он был немного грязным. Строка, о которой вы спрашиваете, равнозначна 'typedef std :: function (boost :: any const &)> Extractor', но использует синтаксис псевдонима типа, который является предпочтительным в C++ 11. –

1

Вы можете позвонить boost::any_cast для каждого из типов в boost::variant и остановится, когда первая литая удалось:

#include <iostream> 
#include <utility> 
#include <stdexcept> 
#include <sstream> 

#include <boost/any.hpp> 
#include <boost/variant.hpp> 
#include <boost/type_index.hpp> 
#include <boost/mpl/size.hpp> 
#include <boost/mpl/at.hpp> 

template <typename Sequence> 
struct mpl_sequence_to_std_tuple 
{ 
template <std::size_t... Is> 
static auto x(std::index_sequence<Is...>) -> std::tuple<typename boost::mpl::at_c<Sequence, Is>::type...>; 

using type = decltype(x(std::make_index_sequence<boost::mpl::size<Sequence>::type::value>{})); 
}; 

struct signal_conversion_success{}; 

template <typename T, typename Variant> 
void try_convert(const boost::any& any, Variant& var) 
{ 
    try 
    { 
     var = boost::any_cast<T>(any); 
     throw signal_conversion_success{}; 
    } 
    catch(const boost::bad_any_cast &) 
    { 
    } 
} 

template <typename T, typename... Ts> 
std::string parameter_pack_to_string(const std::string& separator = ", ") 
{ 
    std::stringstream ss; 
    ss << boost::typeindex::type_id<T>().pretty_name(); 
    auto l = {0, (void(ss << separator << boost::typeindex::type_id<Ts>().pretty_name()),0)...}; 
    std::ignore = l; 
    return ss.str(); 
} 

template <typename Variant, typename...Ts> 
void do_convert(const boost::any& any, Variant& var, std::tuple<Ts...>) 
{ 
    bool success = false; 

    try { 
     auto l = {0, (void(try_convert<Ts>(any, var)), 0)... }; 
     std::ignore = l; 
    } 
    catch(const signal_conversion_success&) 
    { 
     success = true; 
    } 

    if (!success) 
    { 
     std::stringstream ss; 
     ss << "cannot convert this boost::any instance to any of the following types: "; 
     ss << parameter_pack_to_string<Ts...>(); 
     throw std::invalid_argument(ss.str()); 
    } 
} 

template<typename Variant> 
void convert(const boost::any& any, Variant& var) 
{ 
    using Tuple = typename mpl_sequence_to_std_tuple<typename Variant::types>::type; 
    do_convert(any, var, Tuple{}); 
} 

struct print_visitor : public boost::static_visitor<void> 
{ 
    template <typename T> 
    void operator()(T&& t) const 
    { 
     std::cout << boost::typeindex::type_id<T>().pretty_name() << ": " << std::forward<T>(t) << std::endl; 
    } 
}; 

int main() 
{ 
    using Variant = boost::variant<int, std::string>; 
    boost::any any = std::string("Hello World"); 
    Variant var; 
    convert(any, var); 
    boost::apply_visitor(print_visitor(), var); 
} 

live example

В случае, если ни одна из бросков не удалась, генерируется исключение, см. Следующие live example.

+1

Огромное спасибо за это гениальное решение. К сожалению, я застрял в MSVC 2010, но сейчас мы переходим на MSVC 2015. До сих пор MSVC 2010 не поддерживает переменную адические шаблоны. Извините, что я этого не говорил. Тем не менее решение, похоже, работает в живом примере. – Aleph0

Смежные вопросы