【问题标题】:Use of typeid to handle different types使用 typeid 处理不同类型
【发布时间】:2014-12-08 22:59:23
【问题描述】:

我正在尝试使用 boost::any 来封装 sqlite 返回值。然后我尝试编写一个循环来打印这些。

我的第一个想法是做类似的事情:

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

现在对于有经验的程序员来说,很明显这是行不通的,因为 typeid 返回的是实例而不是数字 id。 经过一些研究,我想我可以尝试typeid(...).hash_code(),但这还不够constexpr 合格(除了哈希冲突的危险)。

问题

  1. 有没有比构建过多的if ... else ... 迷宫来根据对象的 typeid 处理对象更好的方法?
  2. hash_code 不是const_expr 有什么原因吗?这是单独编译目标文件的结果吗?
  3. std::type_index 有什么用?考虑到它只提供了一些额外的运算符(&lt;&lt;=&gt;&gt;=),为什么无法将其功能与std::type_info集成?

【问题讨论】:

  • 不会typeid(field) 只返回boost::any 的类型ID?
  • hash_code() 不是constexpr 因为type_info 可能是多态的。有人讨论过提出一种获取类型的编译时标识符的方法,但我认为它没有成功?
  • 使用 std::map 将 type_index 映射到函子。
  • Boost VariantBoost TypeIndex。我就把它留在这里
  • 我所指的“computed goto”技术是编译switch 的可能方式之一。它使用跳转表,控制变量用于在表中查找条目。如果生成的跳转表不够密集,编译器可能会选择使用一系列条件(完全像 if/else)来编译 switch 语句。 (Further reading)

标签: c++ sqlite boost boost-any


【解决方案1】:

我感觉您正在寻找提升变体和静态访问。

由于未提及变体,因此可能值得将其发布为答案。演示:

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

}

打印

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

【讨论】:

  • boost::varaint 相对于boost::any 的优势在哪里?标记null 值的最佳方法是什么? (在模板参数中添加void*?)
  • @ted 为什么是void*?它不再是 C 了 :) struct Nil {}; boost::variant&lt;Nil, double, long, blob_t...&gt; 你正在路上。更新了示例 Live On Coliru
  • 哦,重新Where is the advantage:优点是您将类型区分留给 Boost Variant,永远不会离开类型安全,也不会处理 typeid 周围潜在的毛茸茸的问题。跨度>
【解决方案2】:

这是一个类似于 boost::any 上的静态访问的实现,使用 C++11 lambda:

#include <iostream>
#include <type_traits>
#include <boost/any.hpp>

template <size_t, typename...>
struct select_type { };

template <size_t index, typename First, typename... Types>
struct select_type<index, First, Types...> : public select_type<index - 1, Types...> { };

template <typename First, typename... Types>
struct select_type<0, First, Types...>
{
    using type = First;
};

template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())> { };

template <typename Return, typename Class, typename... Args>
struct function_traits<Return (Class::*)(Args...) const>
{
    using result_type = Return;

    template <size_t argN>
    using argument_type = select_type<argN, Args...>;
};

template <typename... Functors>
struct any_call_impl
{
    static bool call(boost::any &, Functors const & ...)
    {
        return false;
    }

    static bool call(boost::any const &, Functors const & ...)
    {
        return false;
    }
};

template <typename FirstFunctor, typename... Functors>
struct any_call_impl<FirstFunctor, Functors...>
{
    static bool call(boost::any & v, FirstFunctor const & first, Functors const & ... rest)
    {
        using arg = typename function_traits<FirstFunctor>::template argument_type<0>::type;
        using arg_bare = typename std::remove_cv<typename std::remove_reference<arg>::type>::type;

        if (v.type() == typeid(arg_bare)) {
            first(*boost::any_cast<arg_bare>(&v));
            return true;
        }

        return any_call_impl<Functors...>::call(v, rest...);
    }

    static bool call(boost::any const & v, FirstFunctor const & first, Functors const & ... rest)
    {
        using arg = typename function_traits<FirstFunctor>::template argument_type<0>::type;
        using arg_bare = typename std::remove_cv<typename std::remove_reference<arg>::type>::type;

        if (v.type() == typeid(arg_bare)) {
            first(*boost::any_cast<arg_bare>(&v));
            return true;
        }

        return any_call_impl<Functors...>::call(v, rest...);
    }
};

template <typename... Functors>
bool any_call(boost::any & v, Functors const & ... f)
{
    return any_call_impl<Functors...>::call(v, f...);
}

template <typename... Functors>
bool any_call(boost::any const & v, Functors const & ... f)
{
    return any_call_impl<Functors...>::call(v, f...);
}

int main(void) {
    boost::any a = 1;

    any_call(a,
        [](double d) { std::cout << "double " << d << std::endl; },
        [](int i) { std::cout << "int " << i << std::endl; }
    );

    return 0;
}

(Demo)

这个想法是您将 boost::anyboost::any const 作为第一个参数传递给 any_call,然后传递多个 lambda。第一个参数类型与boost::any 中包含的对象类型匹配的lambda 将被调用,然后any_call 将返回true。如果没有 lambda 匹配,any_call 将返回 false。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-03
    • 1970-01-01
    • 1970-01-01
    • 2013-09-02
    • 2019-05-04
    • 2010-10-07
    相关资源
    最近更新 更多