Boost logo

Boost Users :

From: hirsch_at_[hidden]
Date: 2007-09-13 11:08:22


Hi,

I have a recursive variant type like that:
--------------------------
typedef boost::make_recursive_variant<bool,
                int,
                double,
                std::string,
                std::vector< boost::recursive_variant_ >,
                std::map<std::string, boost::recursive_variant_>
>::type TVType;

typedef std::vector<TVType> TVector;
typedef std::map<std::string, TVType> TMap;
--------------------------

And I implemented an accessor class to extract elements from the variant in
a path-linke manner:
--------------------------
TMap map;
map["longValue"] = 23;
map["stringValue"] = "abcde";

TVector subvec;
subvec.push_back(42);
map["subVector"] = subvec;

TVType tv = map;

Access acc(tv);
std::string sValue = acc.get<std::string>("stringValue"); // "abcde"
long nSubValue = acc.get<long>("subVector/0"); // 42
----------------------------

It is implemented by using a boost::static_visitor. It works fine for now,
but I also want to change values which are inside my variant hierarchy. So
I have to return a reference-type from my Access helper rather than a value
type, but I can't figure out how to do this.
Here's the implementation of the accessor as it is now:

----------------------------
class Access: public boost::static_visitor<TVType>
{
    typedef std::list< std::string > TStrList;

  public:
    template<typename T>
    T get(const std::string& sPath, const TVType& tvt)
    {
      m_aSplits.clear();

      // split the path-parts, neighboring splitters will be
      // automatically compressed to one.
      boost::split( m_aSplits, sPath, std::bind2nd(
std::equal_to<char>(), '/'),
                      boost::token_compress_on);

      // remove empty entries
      for(TStrList::iterator it = m_aSplits.begin(); it!= m_aSplits.end();
++it)
      {
        if(it->empty())
          it = m_aSplits.erase(it);
      }
      // in case of an exception, transform the boost::bad_get exception to
our CIS::Exception
      try
      {
        return boost::get<T>(boost::apply_visitor(*this, tvt));
      }
      catch(boost::bad_get& ex)
      {
        throw CIS::Exception(ex.what(), EX_POS);
      }
    }

    TVType operator()(const TVector& v)
    {
       // if the splits-list is empty we have found our element, so the
TVector itself was requested
       if(m_aSplits.empty())
         return v;

       const uint32_t nIdx =
boost::lexical_cast<uint32_t>(m_aSplits.front());

       if(nIdx >= v.size())
           throw Exception(boost::format("Invalid index: '%1%' from '%2%' in
access path")
                                        % nIdx % v.size(), EX_POS);
       m_aSplits.pop_front();
       return boost::apply_visitor(*this, v[nIdx]);
    }

    TVType operator()(const TMap& m);
    {
        // if the splits-list is empty we have found our element, so the
TMap itself was requested
        if(m_aSplits.empty())
          return m;

        TMap::const_iterator it = m.find(m_aSplits.front());
        if(it == m.end())
           throw Exception(boost::format("Invalid key: '%1%' in access
path")
                                  % m_aSplits.front(), EX_POS);

        m_aSplits.pop_front();
        return boost::apply_visitor(*this, it->second);
    }

    template<typename T>
    TVType operator()(const T& t)
    {
      return t;
    }

  private:
    TStrList m_aSplits; ///< splitted parts of the path.
};
------------------------

I tried to remove the consts and return a reference, but the compiler claims
it can't
convert a TVector& to a TVType&. I also tried with pointers but got the same
error.
Does anybody have an ide how I can implement this functionality?

Thanks,
         Falco


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net