0

There is a logging system with the number of attributes of arbitrary types. The attributes are added by external program(s) using public API (function template). The types aren't known beforehand. The typical way to print out an attribute's value looks like (simplified):

void print(logging::attribute_value const& attr)
{
    auto val = logging::extract<int>(attr);
    if (val) {
        std::cout << "value: " << val.get() << std::endl;
    }
}

In the example above it's already known that the attribute value type is int. What if the expected type isn't known? Of cource, I could write it like:

typedef boost::mpl::vector<int, std::string> types; // Expected types
logging::value_ref<types> val = logging::extract<types>(attr);
[..]

however in that case I have to define all possible types and handle them separately.

Is there a way to print out (or convert to strings) all values regardless of their types, assuming the types overload streaming operator?

UPDATE

Here are some more details:

// Add new attributes with arbitrary types
template<typename T>
void addProperty(const std::string &key, T && value)
{
    auto attrs = boost::log::core::get()->get_thread_attributes();
    attrs.erase(key);
    attrs.insert(key, boost::log::attributes::make_constant(std::move(value)));
    boost::log::core::get()->set_thread_attributes(attrs);
}

and another function that should print the properties' values

void printProperties()
{
    const auto &attrs = boost::log::core::get()->get_thread_attributes();
    for (auto const &a : attrs) {
        // Print all values. Types aren't known.
    }
}
vahancho
  • 20,808
  • 3
  • 47
  • 55
  • Are you implementing your own log-sink? Usually, one does not manually extract attributes, or if one does, it's to implement e.g. custom filters which presumably looks at known attributes with known types. – sehe Feb 09 '22 at 13:34
  • If you include a self-contained example I will try to show one or more approaches – sehe Feb 09 '22 at 13:42
  • @sehe, I have updated the question with details. – vahancho Feb 09 '22 at 14:54
  • Can you make it self-contained? It is not reasonable to expect people to create a program from scratch just to check things out. I know I do it all the time, but some things (Asio, Geometry, Log, UnitTets) are very tedious to get right and 80% of the time the response is “Yeah, but i my program…. “. – sehe Feb 09 '22 at 16:15

2 Answers2

1

Is there a way to print out (or convert to strings) all values regardless of their types, assuming the types overload streaming operator?

No, Boost.Log doesn't support that, you must know the name and type of the attribute value in order to be able to extract or visit it.

What you could do is maintain your own mapping between attribute names and formatting routines, such as this:

std::map<
    logging::attribute_name,
    std::function< void(std::ostream&, logging::attribute_value const&) >
> formatters;

template< typename T >
void formatter(std::ostream& strm, logging::attribute_value const& attr)
{
    auto val = logging::extract< T >(attr);
    if (val)
        strm << val.get();
}

template<typename T>
void addProperty(const std::string &key, T && value)
{
    typedef std::remove_cv_t< std::remove_reference_t< T > > value_type;
    logging::attribute_name name(key);
    formatters[name] = &formatter< value_type >;
    boost::log::core::get()->add_thread_attribute(name,
        boost::log::attributes::make_constant(std::forward< T >(value)));
}

void printProperties()
{
    const auto &attrs = boost::log::core::get()->get_thread_attributes();
    for (auto const &a : attrs) {
        auto it = formatters.find(a.first);
        if (it != formatters.end()) {
            std::cout << "value: ";
            it->second(std::cout, a.second.get_value());
            std::cout << std::endl;
        }
    }
}
Andrey Semashev
  • 10,046
  • 1
  • 17
  • 27
  • This is, indeed, a very elegant solution - thank you. It does what I wanted (after a small adaptation). I would like also to thank you for the whole `boost::lib::*` stuff (I assume - you are the initial author of the library?). Appreciate the API, documentation and examples - all were very usefull when I started to use it this year. Well done! – vahancho Feb 10 '22 at 11:11
0

Of cource, I could write it like:

typedef boost::mpl::vector<int, std::string> types; // Expected types
logging::value_ref<types> val = logging::extract<types>(attr);
[..]

however in that case I have to define all possible types and handle them separately.

Yes, this was going to be my first suggestion: consider making the type a sum type (a bound variant, where you can visit all the bound types).

Is there a way to print out (or convert to strings) all values regardless of their types, assuming the types overload streaming operator?

The canonical alternative is to use type erasure. Mind you, it does still have overhead, but replaces static polymorphism with dynamic polymorphism under the hood¹.

Have a look e.g. at https://www.boost.org/doc/libs/1_78_0/doc/html/boost/type_erasure/ostreamable.html


¹ which compilers in very specific circumstances and with proper hinting can sometimes devirtualize back to static invocations, but I digress

sehe
  • 374,641
  • 47
  • 450
  • 633