提取任意类型的boost日志属性值

Extract boost log attribute values of arbitrary type

有一个日志系统,具有任意类型的属性数量。这些属性由外部程序使用 public API (函数模板)添加。这些类型是事先不知道的。打印属性值的典型方式如下(简化):

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

在上面的例子中已经知道属性值类型是int。如果预期的类型未知怎么办?当然,我可以这样写:

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

但是在那种情况下,我必须定义所有可能的类型并分别处理它们。

假设类型重载流运算符,是否有办法打印出(或转换为字符串)所有值而不考虑它们的类型?

更新

以下是更多详细信息:

// 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);
}

和另一个应该打印属性值的函数

void printProperties()
{
    const auto &attrs = boost::log::core::get()->get_thread_attributes();
    for (auto const &a : attrs) {
        // Print all values. Types aren'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?

规范的替代方法是使用类型擦除。请注意,它仍然有开销,但在幕后用动态多态替换了静态多态¹。

看看在 https://www.boost.org/doc/libs/1_78_0/doc/html/boost/type_erasure/ostreamable.html


¹ 在非常特殊的情况下,哪些编译器在适当的提示下有时可以去虚拟化回静态调用,但我离题了

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

不,Boost.Log 不支持,您必须知道属性值的名称和类型才能提取或访问它。

您可以做的是维护您自己的属性名称和格式化例程之间的映射,例如:

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;
        }
    }
}