使用 std::vector<boost::variant<...>> with Google Mock 导致编译错误

Using std::vector<boost::variant<...>> with Google Mock causes a compilation error

有这个代码:

#include "gmock/gmock.h"
#include <boost/variant.hpp>

typedef boost::variant<std::vector<unsigned char>, std::vector<int>> CustomVariant;

// some overloads which don't work
std::ostream& operator<<(
  std::ostream& stream,
  const boost::variant<std::vector<unsigned char>>&)
{ return stream; }

std::ostream& operator<<(
  std::ostream& stream,
  const boost::variant<std::vector<int>>&)
{ return stream; }

std::ostream& operator<<(
  std::ostream& stream,
  const std::vector<unsigned char>&)
{ return stream; }

std::ostream& operator<<(
  std::ostream& stream,
  const std::vector<int>&)
{ return stream; }

class MyClass
{
  MOCK_METHOD1(fun, bool(std::vector<CustomVariant> v));
};

int main()
{
  MyClass a;
  return 0;
}

有两个错误:

/usr/include/boost/variant/detail/variant_io.hpp:64:14: error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream<char>’ and ‘const std::vector<unsigned char>’)
         out_ << operand;
/usr/include/boost/variant/detail/variant_io.hpp:64:14: error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’
         out_ << operand;

它抱怨没有为类型 std::basic_ostream<char>const std::vector<unsigned char> 定义 operator<<,尽管它似乎已被定义。我尝试了一些重载,但其中 none 有效。如何正确编译这段代码?

在 g++ 6.3 上编译:

g++ main.cpp -lgmock -o main -L ./googletest-release-1.8.0/googlemock -pthread

前两个打印机函数没有用,因为变体不能转换为具有其类型子集的变体。其他的不起作用,因为编译器找不到它。 ADL 仅查看定义类型的名称空间(在本例中为 std)。所以这个解决方案可行:

namespace std {

ostream& operator<<(
  ostream& stream,
  const vector<unsigned char>&)
{ return stream; }

ostream& operator<<(
  ostream& stream,
  const vector<int>&)
{ return stream; }

}

但是,这不是一个好主意。通常,您不应将任何内容放入 std 命名空间。现在你确实需要它,但是放置一个重载的 operator<< 有点太麻烦了。好的是 gtest 为它提供了一个更好的方法,而是创建一个 PrintTo() 函数:https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#teaching-googletest-how-to-print-your-values。这样,您必须为整个变体创建一个打印机,但是,这意味着您必须创建一个执行实际打印的访问者。

class Printer : public boost::static_visitor<void> {
public:
    Printer(std::ostream& stream) : stream(stream) {}

    void operator()(const std::vector<unsigned char>&) const {}
    void operator()(const std::vector<int>&) const {}
private:
    std::ostream& stream;
};

namespace boost {

void PrintTo(const CustomVariant& v, std::ostream* stream) {
    boost::apply_visitor(Printer(*stream), v);
}

}

如果各个元素的打印类似,也可以在visitor中使用模板函数:

class Printer : public boost::static_visitor<void> {
public:
    Printer(std::ostream& stream) : stream(stream) {}

    template<typename T>
    void operator()(const std::vector<T>& v) const {
        for (const T& t : v) {
            // do something
        }
    }
private:
    std::ostream& stream;
};

boost::operator<<(std::ostream&, boost::variant const&)boost/variant/detail/io.hpp 中定义,不幸的是遵从 ADL-found operator<<.

如前所述,在 std 命名空间中没有声明 operator<<(std::ostream&, std::vector<> const&),声明一个是非法的。

解决方法是将此运算符注入 boost::detail::variant 命名空间。

您不会希望在生产代码中执行此操作,因为它依赖于对 boost 内部知识的了解,但在测试中它可能是可以接受的。

这样编译:

#include "gmock/gmock.h"
#include <vector>

struct emitter
{
    emitter(std::ostream& os) : os(os) {};

    template<class T> std::ostream& operator()(T const& v) const
    {
        return os << v;
    }

    std::ostream& operator()(char c)
    {
        if (std::isprint(c))
        {
            return os << '\'' + c + '\'';
        }
        else
        {
            auto oldstate = os.flags();
            os << std::hex << "0x" << (int(c) & 0xff);
            os.flags(oldstate);
            return os;
        }
    }


    template<class T, class A>
    std::ostream &operator()(const std::vector<T, A> &v) const
    {
        const char* sep = " ";
        os << "[";
        for (auto&& x : v)
        {
            (*this)(x);
            sep = ", ";
        }
        return os << " ]";
    }


    std::ostream& os;
};

namespace boost { namespace detail { namespace variant {
    template<class T, class A>
    std::ostream &operator<<(std::ostream &os, std::vector<T, A>const &var)
    {
        auto e = emitter(os);
        return e(var);
    }

}}}

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




typedef boost::variant<std::vector<unsigned char>, std::vector<int>> CustomVariant;





class MyClass
{
    MOCK_METHOD1(fun, bool(std::vector<CustomVariant>v));
};

int main()
{
    MyClass a;
    return 0;
}