Search code examples
c++sqliteboostboost-any

Use of typeid to handle different types


I am trying to use boost::any to encapsulate the sqlite return values. I then tried to write a loop to print these.

My first thought was to do something like:

for(boost::any field: row) {
  switch(field.type()) {
      case typeid(double):
          double value = any_cast<double>(field);
          ...
          break;
      case typeid(other type):
          ...
  }
}

Now for the experienced programmer it becomes obvious that this can not work since typeid returns an instance rather than a numeric id. After some research I figured I might try either typeid(...).hash_code() however this is not sufficiently constexpr qualified (Beside the danger of hash collision).

Questions

  1. Is there a better way than building an excessive if ... else ... labyrinth to handle objects based on their typeid?
  2. Is there a reason why hash_code is not a const_expr? Is this a result of the separate compilation of object files?
  3. What is the use of std::type_index? Considering that it only provides some additional operators (<, <=, >, >=) why was it not possible to integrate its functionality with std::type_info?

Solution

  • I have a feeling you are looking for boost variant and static visitations.

    Since variants weren't mentioned, this might be worth posting as an answer. Demonstruction:

    Live On Coliru

    #include <sstream>
    #include <iostream>
    
    #include <boost/variant.hpp>
    
    using namespace boost;
    
    struct Nil {};
    using blob_t = std::vector<uint8_t>;
    using field_value_t = boost::variant<Nil, double, char const*, long, blob_t/*, boost::date_time, std::vector<uint8_t>*/>;
    
    struct handler : static_visitor<std::string> {
    
        std::string operator()(double)      const { return "double"; }
        std::string operator()(char const*) const { return "C string (ew!)"; }
        std::string operator()(long)        const { return "long"; }
        std::string operator()(blob_t)      const { return "long"; }
        std::string operator()(Nil)         const { return "<NIL>"; }
    
        template<typename T>
        std::string operator()(T const&)    const { throw "Not implemented"; } // TODO proper exception
    };
    
    void handle_field(field_value_t const& value) {
        std::cout << "It's a " << apply_visitor(handler(), value) << "\n";
    }
    
    int main() {
    
        handle_field({});
        handle_field(blob_t { 1,2,3 });
        handle_field("Hello world");
        handle_field(3.14);
    
    }
    

    Prints

    It's a <NIL>
    It's a long
    It's a C string (ew!)
    It's a double