2015-05-29 2 views
5

Я пытаюсь использовать boost :: variant с типами шаблонов. Например, у меня есть шаблон типа Tag<T>, а boost :: variant AnyTag содержит типы, такие как Tag<double>, Tag<int> и Tag<std::string>. Каждый Tag<T> имеет член типа T. Теперь я хотел бы поставить эти варианты в контейнере и просто присваивать значения во время выполнения, например,Полиморфный сеттер для Boost :: variant

for(AnyTag & tag: AllTags) { 
    setValue(tag, getValueFromXml()); 
} 

Функция setValue(AnyTag &tag, T &val) следует использовать тип времени выполнения AnyTag тега чтобы правильно присвоить тегу правильное значение. Моя попытка решить проблему приведена ниже, и она использует другой вариант, который включает только возможные типы T, которые могут быть использованы в AnyTag (TagValueType).

template<typename T, typename = void> 
class Tag {}; 

template <typename T> 
class Tag<T, EnableIf<std::is_arithmetic<T>>> { 
public: 
    T value = 0; 
    std::string address = ""; 
    T maxValue = std::numeric_limits<T>::max(); 
    typedef T value_type; 
}; 

template <typename T> 
class Tag<T, DisableIf<std::is_arithmetic<T>>> { 
public: 
    T value; 
    std::string address = ""; 
    typedef T value_type; 
}; 

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

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

class tag_set_value_visitor: public boost::static_visitor<void> 
{ 
    const TagValueType & value; 
public: 
    tag_set_value_visitor(const TagValueType & val): value(val){} 
    template <typename T> 
    void operator()(T & tag) const 
    { 
     tag.value = boost::get<typename T::value_type>(value); 
    } 
}; 

inline void setValue(AnyTag & tag, const TagValueType & val) { 
    assert(tag.which() == val.which()); 
    boost::apply_visitor(tag_set_value_visitor(val), tag); 
} 

К сожалению, этот подход не является то, что я хотел бы, потому что, например, во время компиляции есть не проблема, если я делаю следующее:

AnyTag a = Tag<int>(); 
setValue(a, double(1.3)); 

, но во время выполнения, библиотека подталкивания обнаруживает несоответствие типов и сбой программы.

Итак, мое решение является своего рода стиранием типа, которое просто откладывает проблему.

Что бы я хотел иметь, это setValue (AnyTag & tag, T & val), где T - тип времени выполнения AnyTag.

Получаю, что это то, что пытается сделать посетитель варианта, но в этом случае есть проблема, потому что, когда мы строим посетителя, мы должны знать тип, который мы будем использовать.

Любые идеи или любые мысли по этой проблеме?

P.S .: Извините за длинный пост, но я не смог найти способ объяснить мой мыслительный процесс с меньшим количеством слов.

+0

В 'setValue (a, double (1.3))', как вы ожидаете, что компилятор обнаружит проблему, когда проблемой является тип _run-time_ 'a'? – Nemo

+0

Немо, я этого не делаю. Я где-то писал, что то, что я делаю, просто откладывает проблему.То, что я хотел бы иметь это (в "расслабленном" синтаксис): 1) компилятор генерировать SetValue (AnyTag :: Tag &, Int & Вэл) SetValue (AnyTag :: Tag &, двойной и вал) 2) а затем во время выполнения полиморфно выбирает правильный setValue (...) в зависимости от типа времени выполнения AnyTag. – CuriousNik

+0

Я понимаю, что диспетчеризация не может произойти таким образом на C++, поэтому я подумал, что, возможно, кто-то знает трюк (с шаблоном посетителя), который мог бы решить эту проблему. – CuriousNik

ответ

6

Использование¹ бинарный посетитель.

Внесите operator() ничего, кроме соответствующих типов.

Ручка несовпадения по вкусу (я возвращать логическое значение, указывающее успех):

Live On Coliru

#include <boost/any.hpp> 
#include <boost/variant.hpp> 
#include <boost/mpl/vector.hpp> 
#include <string> 

using namespace boost; 

template <typename T> 
struct Tag { 
    T value; 
}; 

using Types = mpl::vector<std::string, double, int>; 
using Tags = mpl::transform<Types, Tag<mpl::_1> >::type; 

using Variant = make_variant_over<Types>::type; 
using AnyTag = make_variant_over<Tags>::type; 

namespace mydetail { 
    struct assign_to : boost::static_visitor<bool> { 
     template <typename V> bool operator()(Tag<V>& tagged, V const& value) const { 
      tagged.value = value; 
      return true; 
     } 

     template <typename T, typename V> bool operator()(T&&, V&&) const { 
      return false; 
     } 
    }; 
} 

bool setValue(AnyTag &tag, Variant const& val) { 
    return boost::apply_visitor(mydetail::assign_to(), tag, val); 
} 

int main() { 
    AnyTag t; 

    t = Tag<std::string>(); 

    // corresponding type assigns and returns true: 
    assert(setValue(t, "yes works")); 

    // mismatch: no effect and returns false: 
    assert(!setValue(t, 42)); 
    assert(!setValue(t, 3.1415926)); 
} 

¹ Если я правильно понял вашу цель. Я сосредоточился на «Что бы я хотел иметь, это тег setValue (AnyTag &, T & val), где T - тип времени выполнения AnyTag». часть запроса.

+0

Большое спасибо, sehe. Это именно то, чего я хотел. К сожалению, я не могу проголосовать за ваш ответ, потому что у меня недостаточно очков репутации. Я вижу трюк, который вы сделали, и я так не думал об этом. Кроме того, вы обратили на меня внимание boost :: mpl, так что я не повторяю эти объявления типов. Еще раз спасибо! – CuriousNik

+0

Hah! Я рад, что вы заметили, что крошечная деталь с трансформируемым списком типов :) Cheers – sehe

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