Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serializing a variables_map

How do I serialize/deserialize a boost::program_options::variables_map? I can't find an already implemented serialize function, and I don't know what functions in variables_map I can use to extract and reassemble the map.

like image 226
Jayen Avatar asked Nov 06 '10 05:11

Jayen


People also ask

What is boost:: program_ options?

Boost. ProgramOptions is a library that makes it easy to parse command-line options, for example, for console applications. If you develop applications with a graphical user interface, command-line options are usually not important. To parse command-line options with Boost.

What is Boost serialization?

The library Boost. Serialization makes it possible to convert objects in a C++ program to a sequence of bytes that can be saved and loaded to restore the objects. There are different data formats available to define the rules for generating sequences of bytes. All of the formats supported by Boost.

Is Boost Program Options header only?

Some boost libraries are header-only, some are not, and for various reasons etc.


1 Answers

It looks like you found out that boost::program_options::variables_map derives from std::map so you can use its serialization (but see the warning later on this). If the only remaining problem is serializing the boost::any values it contains then you're almost there.

You can't serialize an arbitrary boost::any because it doesn't really know how to manipulate what it holds. However, if you know and can enumerate the types used by your application, then serialization is possible. For example, if you know that the boost::any value is always a string or an int, then something like this should work.

To serialize (value is a boost::any):

if (value.type() == typeid(int)) {
   ar << std::string("int");
   ar << boost::any_cast<int>(value);
}
else if (value.type() == typeid(std::string)) {
   ar << std::string("string");
   ar << boost::any_cast<std::string>(value);
}

To deserialize (value is a boost::any):

std::string type;
ar >> type;
if (type == "int") {
   int x;
   ar >> x;
   value = x;
}
else if (type == "string") {
   std::string x;
   ar >> x;
   value = x;
}

Obviously you can use more efficient type tags than "int" and "string" in your serialization stream, but this gives you the basic idea.

Edit: boost::archive is picky about const references so what I wrote above doesn't quite compile. This does, and it worked for a very simple test:

enum {
   TYPE_int,
   TYPE_string,
};

namespace boost {
   namespace serialization {

      template<class Archive>
      void save(Archive& ar, const boost::program_options::variable_value& value, unsigned int version) {
         const boost::any& anyValue = value.value();
         if (anyValue.type() == typeid(int)) {
            int type = static_cast<int>(TYPE_int);
            int typedValue = boost::any_cast<int>(anyValue);
            ar << type << typedValue;
         }
         else if (anyValue.type() == typeid(std::string)) {
            int type = static_cast<int>(TYPE_string);
            std::string typedValue = boost::any_cast<std::string>(anyValue);
            ar << type << typedValue;
         }
      }

      template<class Archive>
      void load(Archive& ar, boost::program_options::variable_value& value, unsigned int version) {
         boost::any anyValue;
         int type;
         ar >> type;
         if (type == TYPE_int) {
            int x;
            ar >> x;
            anyValue = x;
         }
         else if (type == TYPE_string) {
            std::string x;
            ar >> x;
            anyValue = x;
         }

         value = boost::program_options::variable_value(anyValue, false);
      }

      template<class Archive>
      void serialize(Archive& ar, boost::program_options::variables_map& value, unsigned int version) {
         // Probably works but is sloppy and dangerous.  Would be better to
         // deserialize into a temporary std::map and build a variables_map
         // properly.  Left as an exercise.
         ar & static_cast<std::map<std::string, boost::program_options::variable_value>&>(value);
      }
   }
}

BOOST_SERIALIZATION_SPLIT_FREE(boost::program_options::variable_value);

There are a couple possible issues with this code. The first is in load() for variable_value - the last statement makes a variable_value from a boost::any and I wasn't quite sure what that bool argument did (you may need to serialize whatever that bool represents). The second is that you may or may not get a consistent variables_map by just casting to a std::map reference and deserializing. It would be safer to deserialize into a real std::map and then build the variables_map from the std::map contents.

like image 69
rhashimoto Avatar answered Nov 15 '22 18:11

rhashimoto