2015-05-29 16 views
5

Sto cercando di utilizzare la variante boost :: con i tipi di modello. Ad esempio, ho un modello tipo Tag<T> e il boost :: variante AnyTag comprende tipi come Tag<double>, Tag<int> e Tag<std::string>. Ogni Tag<T> ha membri di tipo T. Ora, vorrei mettere quelle varianti in un contenitore e semplicemente assegnare valori durante il funzionamento, ad esempio,Setter polimerico per Boost :: variante

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

La funzione setValue(AnyTag &tag, T &val) devono usare il tipo di runtime tag AnyTag in ordine per assegnare correttamente il tag con il valore corretto. Il mio tentativo di risolvere il problema può essere trovato di seguito e fa uso di un'altra variante che includeva solo i possibili tipi di T che potrebbero essere utilizzati in 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); 
} 

Sfortunatamente, questo approccio non è quello che vorrei, perché ad esempio durante la compilazione, non c'è problema se faccio la seguente:

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

ma durante il runtime, la libreria Boost rileva il tipo non corrispondente e si blocca il programma.

Quindi, la mia soluzione è una sorta di cancellazione di tipo che rimanda il problema.

Quello che mi piacerebbe avere è un valore setValue (AnyTag &, T & val) dove T è il tipo di runtime di AnyTag.

Capisco che questo è ciò che il visitatore della variante tenta di fare, ma in questo caso c'è un problema perché quando costruiamo il visitatore dobbiamo conoscere il tipo che useremo.

Qualche idea o idea su questo problema?

P.S .: Scusa per il post lungo ma non sono riuscito a trovare un modo per spiegare il mio processo di pensiero con meno parole.

+0

In 'setValue (a, double (1.3))', come ti aspetti che il compilatore rilevi un problema quando il problema è il tipo _run-time_ di 'a'? – Nemo

+0

Nemo, io no. Ho scritto da qualche parte che quello che sto facendo è solo rimandare il problema.Quello che mi piacerebbe avere è (nella sintassi "rilassato"): 1) il compilatore generi setValue (AnyTag :: Tag &, int & val) setValue (AnyTag :: Tag e, doppio & val) 2) e quindi durante il runtime selezionare polimoricamente il setValue corretto (...) in base al tipo di runtime di AnyTag. – CuriousNik

+0

Capisco che il dispatching non possa avvenire in questo modo in C++, quindi stavo pensando che forse qualcuno conosce un trucco (con pattern di visitatore) che potrebbe risolvere questo problema. – CuriousNik

risposta

6

Utilizzare¹ un visitatore binario.

Implementare il operator() per fare nulla, tranne per i tipi corrispondenti.

disallineamenti Maniglia a piacere (io ritorno un valore booleano che indica il successo):

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

¹ Se ho capito bene il tuo obiettivo. Mi sono concentrato sullo "Quello che vorrei avere è un valore setValue (tag AnyTag &, T & val) dove T è il tipo di runtime di AnyTag." parte della richiesta.

+0

Grazie mille, sehe. Questo è esattamente quello che volevo. Sfortunatamente, non posso votare la tua risposta perché non ho abbastanza punti reputazione. Vedo il trucco che hai fatto e non ho pensato affatto in questo modo. Inoltre, hai richiamato la mia attenzione su boost :: mpl, in modo da non ripetere queste dichiarazioni di tipo. Grazie ancora! – CuriousNik

+0

Hah! Sono contento che tu abbia notato quel piccolo dettaglio con la lista di caratteri trasformata :) Ciao – sehe

Problemi correlati