0

有一个具有任意类型属性数量的日志记录系统。属性由外部程序使用公共 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.
    }
}
4

2 回答 2

1

假设类型重载流操作符,有没有办法打印出(或转换为字符串)所有值,而不管它们的类型如何?

不,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;
        }
    }
}
于 2022-02-09T19:18:19.313 回答
0

当然,我可以这样写:

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

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

是的,这将是我的第一个建议:考虑将类型设为 sum 类型(绑定变体,您可以访问所有绑定类型)。

假设类型重载流操作符,有没有办法打印出(或转换为字符串)所有值,而不管它们的类型如何?

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

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


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

于 2022-02-09T13:42:27.613 回答