Reputation: 414
There is this code:
#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;
}
There are two errors:
/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;
It complains that there is not defined operator<<
for types std::basic_ostream<char>
and const std::vector<unsigned char>
, although it seems that it's defined. I tried some overloads but none of them worked. How to properly make this code to compile?
Compiling on g++ 6.3:
g++ main.cpp -lgmock -o main -L ./googletest-release-1.8.0/googlemock -pthread
Upvotes: 1
Views: 1495
Reputation: 11730
The first two printer functions have no use, as a variant is not convertible to a variant with a subset of its types. The others don't work because the compiler can't find it. ADL only looks at the namespace where the types are defined (in this case std
). So this solution would work:
namespace std {
ostream& operator<<(
ostream& stream,
const vector<unsigned char>&)
{ return stream; }
ostream& operator<<(
ostream& stream,
const vector<int>&)
{ return stream; }
}
However, it's not a good idea to do. Normally, you shouldn't put anything into the std
namespace. Now you really need it, but putting an overloaded operator<<
is a bit too intrusive. The good thing is that gtest provides a better way for it, creating a PrintTo()
function instead: https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#teaching-googletest-how-to-print-your-values. This way, you'll have to create a printer to the whole variant, however, which means that you have to create a visitor that does the actual printing.
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);
}
}
If printing of the individual elements are similar, you can also use a template function in the 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;
};
Upvotes: 1
Reputation: 69892
boost::operator<<(std::ostream&, boost::variant const&)
is defined in boost/variant/detail/io.hpp
and unfortunately defers to an ADL-found operator<<
.
As mentioned, there is no operator<<(std::ostream&, std::vector<> const&)
declared in the std namespace and it's illegal to declare one.
A workaround is to inject this operator into the boost::detail::variant
namespace.
You wouldn't want to do this in production code because it relies on knowledge of the internals of boost, but in a test it may be acceptable.
this compiles:
#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;
}
Upvotes: 3