【问题标题】:How to pass class template argument to boost::variant?如何将类模板参数传递给 boost::variant?
【发布时间】:2014-12-17 10:46:45
【问题描述】:

我有一个使用 boost::get of boost:variant 模块的模板方法:

typedef boost::variant<int, std::string, bool, uint8_t> Variant;

template <class T>
void write(const Variant& t) {
    size_t sizeT = boost::apply_visitor(SizeOfVisitor(), t);
    memcpy(&v[offset], &boost::get<T>(t), sizeT);
}

问题是我只在运行时才知道 Variant 的底层类型。而且我只能使用which() 方法查询它。

有什么方法可以将类型 T 传递给此方法,它是 Variant 的基础类型? 比如使用which()我知道,有什么类型,但是怎么传呢?

switch (m_var.which()) { // Returns an int index of types in the order of passed template classes
    case 0: // This is int
    case 1: // This is std::string
    ...
}
...
Writer.write<???>(m_var); // How to pass the type here?

编辑:如果您知道任何其他方法可以达到预期结果 - 实际上获取 boost::variant 内部变量的地址,然后从那里复制,请与我分享您的想法

谢谢

【问题讨论】:

  • 你不能 memcpystd::string 值。
  • 我实际上并没有记忆它。对于字符串,我有一个单独的方法来存储它的内容(mystring.c_str())。我认为这是允许的,对吧?
  • 您问题中代码的设置方式,您编写一个函数(错误地)memcpys 和 std::string,如果它收到 std::string -持有变体。

标签: c++ templates boost


【解决方案1】:

我碰巧在这里写了一个非常相似的答案:

同样,最重要的是,将memcpy 与非 POD 数据类型一起使用是完全伪造(因此您不能 em> 与std::string 一起使用。永远)。

variants 上操作只有在运行时才知道的类型的方法是使用boost::static_visitor&lt;&gt;

这是main() 的示例,显然与您想要实现的目标接近,

Live On Coliru

#include <boost/variant.hpp>
#include <boost/bind.hpp>

#include <boost/array.hpp> // just as a sample
#include <iostream>

namespace serialization {

    namespace customization {
        template<typename T, typename Out, typename R = typename boost::enable_if<boost::is_pod<T>, void>::type>
            void do_serialize(T const& x, Out& out)
            {
                static_assert(boost::is_pod<T>(), "");
                char const* rawp = reinterpret_cast<char const*>(&x);
                std::copy(rawp, rawp+sizeof(T), out);
            }

        template<typename Out>
            void do_serialize(std::string const& x, Out& out)
            {
                do_serialize(x.size(), out);
                for(auto ch : x)
                    do_serialize(ch, out);
            }
    }

    struct serialize_f : public boost::static_visitor<> {
        template<typename Out, typename... T>
            void operator()(boost::variant<T...> const& v, Out& out) const
            {
                boost::apply_visitor(boost::bind(*this, _1, boost::ref(out)), v);
            }

        template<typename T, typename Out>
            void operator()(T const& x, Out& out) const
            {
                using customization::do_serialize; // ADL dispatch
                do_serialize(x, out);
            }
    };

    template <typename T, typename Out>
        Out serialize(T const& v, Out out) {
            const static serialize_f _vis {};
            _vis(v, out);
            return out;
        }

}

namespace MyUserTypes {

    struct A {
        std::string name;
        int i;
    };

    template<typename Out> void do_serialize(A const& v, Out& out) { // ADL will find this
        serialization::serialize(v.name, out);
        serialization::serialize(v.i, out);
    }
}

int main() {
    using namespace serialization;
    std::vector<uint8_t> binary_data;
    auto out_inserter = back_inserter(binary_data);

    // variants and custom types
    typedef boost::variant<MyUserTypes::A, boost::array<char, 42> > V;
    MyUserTypes::A myA { "0123456789", 99 };
    V v = boost::array<char,42>();

    serialize(myA, out_inserter);
    serialize(v, out_inserter);
    v = myA;
    serialize(v, out_inserter);

    std::cout << "Bytes in binary_data vector: " << binary_data.size() << "\n";
}

【讨论】:

  • 您能否解释一下boost::apply_visitor(boost::bind(*this, _1, boost::ref(out)), v); 的工作原理,访问者类的operator () 使用T=concrete 类型(int、float 等)调用。我不明白 _1 替换是如何在这里工作的。 bind 函数后没有替换变量。我检查了 boost 手册,虽然这部分在文档中也不清楚。
  • @rightaway717 apply_visitor 期望 a unary functor (in this case)。它使用变体中包含的具体元素值调用该函子。现在,因为我们的函数需要两个参数,所以我们将第二个参数绑定到 ref(out),因此保留了与 apply_visitor 兼容的一元绑定函子。
【解决方案2】:
switch (m_var.which()) { // Returns an int index of types in the order of passed template classes
    case 0: // This is int
      Writer.write<int>(m_var);
    break;
    case 1: // This is std::string
      Writer.write<std::string>(m_var);
    break;
    ...
}

:)

【讨论】:

  • 哈哈 :) 这很有趣。但这不是我想写东西的地方。我正在用另一种方法写东西。
  • @rightaway717 解决方法是一样的,把开关拨到你要派送的地方就行了。在所有情况下,您都需要一个运行时调度方法,对此唯一的改进是拥有某种将值映射到类型的映射,例如 boost::fusion::map 但另一方面,我不确定那是什理论上可行,所以恐怕这是你能得到的最好的:)
  • 如果你要提倡开启魔法类型索引,为什么不同时提倡一些goto
  • @sehe 嗯,这是我想到的最好的,基于访问者的解决方案确实可能会更好,但也意味着更多的样板代码^^
  • @Drax 我很确定如果他们将使用限制在 c++14 及更高版本,这是完全可能的。事实上,您已经可以使用 c++11 定义“多态”内联访问者:参见 make_visitor in this old answer of mine。纯代码已经使用了在 lambda 中定义的本地结构 - 减少了命名空间的混乱。有一个相关的变体 here。如果结果类型不匹配,仍然需要进行一些熨烫。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-25
  • 1970-01-01
  • 1970-01-01
  • 2011-08-09
  • 2011-09-14
相关资源
最近更新 更多