How to return Template Value from boost::apply_visitor?

1.1k Views Asked by At

The following code correctly spits out the values 999 and "test" to the console but how do I return these values instead? Something like the commented line was my ultimate goal; return the value that I can then assign to an auto variable (since I don't know the type I will be dealing with). I plan to use boost::lexical_cast to convert this value to a std::string to insert it into a field into a database.

I have tried various variations of the visitor from other examples without success, including deriving DataMap from a base class so that I could store the variable as a member. My attempts have ultimately been unsuccessful.

Any suggestions are appreciated. Thanks.

#include "stdafx.h"
#include "boost\variant.hpp"
#include <iostream>
#include <map>

struct DataMap
{
    DataMap() {};

    typedef std::map<std::string, std::map<std::string,
        boost::variant<int, std::string>>> ArtifactMap;

    ArtifactMap::const_iterator begin() const { return _data.begin(); }
    ArtifactMap::const_iterator end() const { return _data.end(); }
    ArtifactMap _data;
};

struct DataMapVisitor : public boost::static_visitor<>
{
    template<typename T>
    void operator()(const T& t) const { std::cout << t << std::endl; }
};


int _tmain(int argc, _TCHAR* argv[])
{
    DataMap dataMap;
    std::map<std::string, boost::variant<int, std::string>> columns;
    columns.insert(std::make_pair("Col1", 999));
    columns.insert(std::make_pair("Col2", "test"));
    dataMap._data.insert(std::make_pair("Table1", columns));

    for (auto table : dataMap)
    {
        for (auto column : table.second)
        {
           boost::apply_visitor(DataMapVisitor(), column.second);
           //auto value = boost::apply_visitor(DataMapVisitor(), column.second);
        }
    }

    return 0;
}

Edit: As an update, the following code has the visitor properly returning a std::string. I'm guessing there is no way to have one visitor return multiple value types (i.e., return the string value if it's a string, return the int value if it's an int, etc.)?

class DataMapVisitor : public boost::static_visitor<std::string>
{
public:

   template<typename T>
   std::string operator()(const T& value) const
   {
      try
      {
         return boost::lexical_cast<std::string>(value);
      }
      catch (boost::bad_lexical_cast&)
      {
         return "";
      }
   }
};
1

There are 1 best solutions below

0
Aleph0 On

If the thing that you want to return is variant, you can return it to be a variant. Hopefully, that answers your question.

#include <boost\variant.hpp>
#include <iostream>
#include <map>

typedef boost::variant<int, std::string> TVariant;

struct DataMap
{
    DataMap() {};

    typedef std::map<std::string, std::map<std::string, TVariant>> ArtifactMap;

    ArtifactMap::const_iterator begin() const { return _data.begin(); }
    ArtifactMap::const_iterator end() const { return _data.end(); }
    ArtifactMap _data;
};

struct DataMapVisitor : public boost::static_visitor<TVariant>
{
    template<typename T>
    TVariant operator()(const T& t) const { 
        std::cout << t << std::endl; 
        return t;
    }
};


int main(int argc, char** args)
{
    DataMap dataMap;
    std::map<std::string, TVariant> columns;
    columns.insert(std::make_pair("Col1", 999));
    columns.insert(std::make_pair("Col2", "test"));
    dataMap._data.insert(std::make_pair("Table1", columns));

    for (auto table : dataMap)
    {
        for (auto column : table.second)
        {
            boost::apply_visitor(DataMapVisitor(), column.second);
            auto value = boost::apply_visitor(DataMapVisitor(), column.second);
        }
    }

    return 0;
}