Home > Software engineering >  output map object where the value can be any data type
output map object where the value can be any data type

Time:11-25

Trying to output a map object where the value can be any data type. Tried the following:

#include <iostream>
#include <unordered_map>
#include <any>

std::unordered_map<std::string, std::any> example = {
    {"first", 'A'},
    {"second", 2},
    {"third", 'C'}
};

std::ostream &operator<<(std::ostream &os,
                    const std::any &m) {
 for (auto &t : example) {
    os << "{" << t.first << ": " << t.second << "}\n";
}
return os;
}

 int main()
{std::cout << example;
 return 0;
}

But,getting infinite loop of values.

CodePudding user response:

There isn't a good way of printing the contents of an arbitrary std::unordered_map<std::string, std::any>. It might have contents that aren't printable, and you've discarded the information about what type the contents actually are. You need to keep that information somewhere.

#include <string>
#include <iostream>
#include <unordered_map>
#include <any>
#include <utility>

template <typename T>
std::ostream & print_any(std::ostream & os, const std::any & any) {
    return os << std::any_cast<const std::decay_t<T> &>(any);
}

class any_printable {
    using print_t = std::ostream & (*)(std::ostream &, const std::any &);
    std::any value;
    print_t print;
public:
    template <typename T>
    any_printable(T&& t) : value(std::forward<T>(t)), print(print_any<T>) {}
    friend std::ostream & operator<<(std::ostream & os, const any_printable & ap) {
        return ap.print(os, ap.value);
    }
};

std::ostream &operator<<(std::ostream &os, const std::unordered_map<std::string, any_printable> &map) {
    for (auto & [key, value] : map) {
        os << "{" << key << ": " << value << "}\n";
    }
    return os;
}

int main() {
    std::unordered_map<std::string, any_printable> example = {
        {"first", 'A'},
        {"second", 2},
        {"third", 'C'}
    };
    std::cout << example;
}

See it on coliru

CodePudding user response:

Another alternative is to register the printing functions corresponding to each type into the hash table and use type_index(any::type) as the key to access the corresponding printing function at runtime.

#include <any>
#include <iostream>
#include <unordered_map>
#include <typeindex>

template<class... Ts>
auto gen_any_printer = std::unordered_map{ 
  std::pair{
    std::type_index(typeid(Ts)),
     [](std::ostream& os, const std::any& a) -> auto&
    { return os << std::any_cast<Ts const&>(a); }
  }...
};

std::ostream& operator<<(std::ostream& os, const std::any& a) {
  const static auto any_printer = gen_any_printer<char, int, const char*>;
  return any_printer.at(std::type_index(a.type()))(os, a);
}

std::ostream& operator<<(
  std::ostream& os, const std::unordered_map<std::string, std::any>& m) {
  for (const auto& [key, value] : m)
    os << "{" << key << ": " << value << "}\n";
  return os;
}

int main() {
  std::unordered_map<std::string, std::any> example = {
    {"first", 'A'}, {"second", 2}, {"third", "hai"}
  };
  std::cout << example;
}

Demo.

  • Related