Reputation: 281
While attempting to add support for UTF-8 locales in a library, I added
the type std::wstring
to the boost::variant
that holds a value.
At that point, I start to get errors with something down inside boost::variant
:
Blockquote/opt/TWWfsw/libboost147/include/boost/variant/detail/variant_io.hpp: In member function 'void boost::detail::variant::printer<OStream>::operator()(const T&) const [with T = std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >, OStream = std::basic_ostream<char, std::char_traits<char> >]':<BR>
/opt/TWWfsw/libboost147/include/boost/variant/variant.hpp:858: instantiated from 'typename Visitor::result_type boost::detail::variant::invoke_visitor<Visitor>::internal_visit(T&, int) [with T = const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >, Visitor = boost::detail::variant::printer<std::basic_ostream<char, std::char_traits<char> > >]'<BR>
< SNIP SNIP ><BR>
Cursor.H:84: instantiated from here
/opt/TWWfsw/libboost147/include/boost/variant/detail/variant_io.hpp:64: error: no match for 'operator<<' in '((const boost::detail::variant::printer<std::basic_ostream<char, std::char_traits<char> > >*)this)->boost::detail::variant::printer<std::basic_ostream<char, std::char_traits<char> > >::out_ << operand'<BR>
/opt/TWWfsw/gcc44/include/c++/ostream:108: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>& (*)(std::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]<BR>etc.
This example is using boost-1.47 w/ g++ 4.4.
#include <string>
#include <iostream>
#include <boost/variant.hpp>
#define NO_STRING 1
int main (int argc, char** argv)
{
#if defined(NO_STRING)
boost::variant<int, std::wstring> v;
#else
boost::variant<int, std::wstring, std::string> v;
#endif
v = 3;
std::wcout << v << std::endl;
std::wstring T(L"wide char literal");
v = T;
std::wcout << v << std::endl;
return 0;
}
This program will output:
3
wide char literal
but if the #define
is removed, and both string
and wstring
are in the variant template parameters, the same error results.
My question is, can I create something that will satisfy this missing definition, like a template specialization?
Perhaps defining a variant visitor that converts the wide string to a narrow string? (not a general solution, but narrowing would work in my case)
The problem comes from using <<
with the variant when both strings are defined. Even if I only output the int through the variant, it won't compile.
Upvotes: 2
Views: 775
Reputation: 5807
Building on the answer by @John Bandela: He is actually reight. I'll just explain a bit more about the culprit.
Notice that std::cout << std::string()
and std::wcout << std::wstring()
are valid while std::wcout << std::string()
and std::wcout << std::string()
are not. This is simply due to the definition of the stream operator (see Why is it that wcout << ""; is OK but wcout << string(); is not?) It is simply not possible unless you provide an overload that handles this.
The same applies to variants: If you try to stream a variant<std::string>
into std::wcout
or a variant<std::wstring>
into std::cout
you will get the same error.
The bottom of your problem is: The operation you apply to the variant (visit, stream-operator) has to support all possibly contained types. That is due to the implementation which is like (pseudo-code):
void variant::visit(T operation){
if(contained is T1) operation(static_cast<T1>(contained));
else if(contained is T2) operation(static_cast<T2>(contained));
...
}
So a solution to your problem would be: Implement the operation yourself for all types:
#include <string>
#include <iostream>
#include <boost/variant.hpp>
std::wstring stringToWString(const std::string& v){
// Only works for single-byte strings. Do it better!
std::wstring result(v.begin(), v.end());
return result;
}
struct visitor: boost::static_visitor<void>{
template<typename T>
void operator()(const T& v) const { std::wcout << v; }
void operator()(const std::string& v) const { std::wcout << stringToWString(v); }
};
int main (int argc, char** argv)
{
boost::variant<int, std::wstring, std::string> v;
v = 3;
boost::apply_visitor(visitor(), v);
std::wcout << std::endl;
std::wstring T(L"wide char literal");
v = T;
boost::apply_visitor(visitor(), v);
std::wcout << std::endl;
v = std::string("Narrow string");
boost::apply_visitor(visitor(), v);
std::wcout << std::endl;
return 0;
}
http://coliru.stacked-crooked.com/a/35e156f38208af1e
Final Note: Do not support UTF-8 by using std::wstring
! UTF-8 can be stored in a regular std::string
. There are very(!) few places where you want to convert this to anything else. One of them is dealing with WinAPI. For that you can write wrappers, that accept a std::string
and convert right before passing it to the actual API.
So general advice: Use UTF-8 (in std::string
) everywhere in your program and only convert at border points.
Upvotes: 1
Reputation: 2436
You problem is not boost::variant
can't handle both string
and wstring
. The problem is that wcout
cannot handle std::string
. For more info about this, see Why is it that wcout << ""; is OK but wcout << string(); is not?
Upvotes: 3