使用 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;
}
有这个代码:
#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;
}