Templates, and compilers with strange fancies.

Started by
19 comments, last by MaulingMonkey 17 years, 8 months ago
Just one more stab at getting an answer to my last question.
Advertisement
Quote:Original post by twix
Just one more stab at getting an answer to my last question.


value_type is declared as std::pair< const key_type , mapped_type > which is probably the problem. Try std::pair< typename Container::key_type , typename Container::value_type > instead - hopefully that's convertable (not an expert on all the things std::pair allows :S)
Quote:Original post by MaulingMonkey
Quote:Original post by twix
Just one more stab at getting an answer to my last question.


value_type is declared as std::pair< const key_type , mapped_type > which is probably the problem. Try std::pair< typename Container::key_type , typename Container::value_type > instead - hopefully that's convertable (not an expert on all the things std::pair allows :S)

Well, I'd like to be able to do this without referring to what the particular value_type in question is. In any case, it doesn't seem like that's the problem. I did some further fiddling, and I'm at a complete loss I'm afraid; I must be completely missing something here.

The full error is:
/usr/include/stlport/stl/_stream_iterator.h:143: error: no match for  ‘operator>>’ in ‘*((_STL::istream_iterator<_STL::pair<const _STL::basic_string<char, _STL::char_traits<char>, _STL::allocator<char> >, int>, char, _STL::char_traits<char>, int>*)this)->_STL::istream_iterator<_STL::pair<const _STL::basic_string<char, _STL::char_traits<char>, _STL::allocator<char> >, int>, char, _STL::char_traits<char>, int>::_M_stream >> ((_STL::istream_iterator<_STL::pair<const _STL::basic_string<char, _STL::char_traits<char>, _STL::allocator<char> >, int>, char, _STL::char_traits<char>, int>*)this)->_STL::istream_iterator<_STL::pair<const _STL::basic_string<char, _STL::char_traits<char>, _STL::allocator<char> >, int>, char, _STL::char_traits<char>, int>::_M_value’


OK... so I try to emulate the exact source that would produce this error.
	typedef 		std::istream_iterator<		std::pair<			const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, 			int			>, 		char, 		std::char_traits<char>, 		int		> iter_type;			iter_type my_iterator;	iter_type::value_type my_value;	iter_type::istream_type* my_stream = new iter_type::istream_type(std::cin.rdbuf());		*my_stream >> my_value;			delete my_stream;		std::cout << my_value << std::endl;

Unfortunately(?), this snippet compiles and works fine. What exactly did I do differently?
Quote:Original post by twix
Unfortunately(?), this snippet compiles and works fine. What exactly did I do differently?


I'm not entirely sure. That said, your code is almost certainly doing something in error if it's able to read in/change the constant .first member (or at least const-safety inappropriate). What's the code you're using to read in pairs?
Quote:Original post by MaulingMonkey
Quote:Original post by twix
Unfortunately(?), this snippet compiles and works fine. What exactly did I do differently?


I'm not entirely sure. That said, your code is almost certainly doing something in error if it's able to read in/change the constant .first member (or at least const-safety inappropriate). What's the code you're using to read in pairs?

To deal with the issue you brought up, I'm forcibly deconstifying pair members. I don't know if there's a better way to handle this, but I'll stick with this method till I can solve the bigger problem...
template< typename T1, typename T2 >std::istream& operator>>(std::istream& is, std::pair<T1, T2>& result){	typedef typename std::tr1::remove_const<T1>::type T1_nonconst;	typedef typename std::tr1::remove_const<T2>::type T2_nonconst;	 	T1_nonconst first;	is >> first;		matchString(is, ":");		T2_nonconst second;	is >> second;		const_cast<T1_nonconst&>(result.first) = first;	const_cast<T2_nonconst&>(result.second) = second;		return is;}
Well, I guess this must be a fairly nontrivial problem. It may not be worth bothering with; at this point, I can just use this workaround:
template< typename Container >typename enable_if< non_string_iterable<Container>, std::istream >::type &operator>>(std::istream& is, Container& container){	matchString(is, "collection");	matchString(is, "(");		unsigned int size;	is >> size;	matchString(is, ")");	matchString(is, "{");		typename std::insert_iterator<Container> iiter(container, container.begin());		for (unsigned int i = 0; i < size; i++)	{		typename Container::value_type value;		is >> value;				*iiter++ = value;		matchString(is, ",");	}				 	matchString(is, "}");		return is;}
Quote:Original post by twix
Quote:Original post by MaulingMonkey
Quote:Original post by twix
Unfortunately(?), this snippet compiles and works fine. What exactly did I do differently?


I'm not entirely sure. That said, your code is almost certainly doing something in error if it's able to read in/change the constant .first member (or at least const-safety inappropriate). What's the code you're using to read in pairs?

To deal with the issue you brought up, I'm forcibly deconstifying pair members. I don't know if there's a better way to handle this,


Scary. I really recommend the pair< key_type , mapped_type > in favor of this. If you really need to avoid binding only to interfaces exposing a pair or key/mapped_type, a workaround would be:

template < typename T > struct remove_pair_const {	typedef typename std::tr1::remove_const< T >::type type;};template < typename L , typename R >struct remove_pair_const< std::pair< L , R > > {	typedef std::pair< typename std::tr1::remove_const< L >::type	                 , typename std::tr1::remove_const< R >::type	                 > type;};...	std::copy_n( std::istream_iterator<typename remove_pair_const< typename Container::value_type >::type >(is)	           , size	           , std::insert_iterator<Container>(container, container.begin())	             //(should work with value_type != pair<...>	           );


Your operator>> const hack only addresses constness (in a dangerous manner) for istream reading - assignment will still fail, which *might* be part of what's botching up this situation.
Quote:Original post by MaulingMonkey
Quote:Original post by twix
Quote:Original post by MaulingMonkey
Quote:Original post by twix
Unfortunately(?), this snippet compiles and works fine. What exactly did I do differently?


I'm not entirely sure. That said, your code is almost certainly doing something in error if it's able to read in/change the constant .first member (or at least const-safety inappropriate). What's the code you're using to read in pairs?

To deal with the issue you brought up, I'm forcibly deconstifying pair members. I don't know if there's a better way to handle this,


Scary. I really recommend the pair< key_type , mapped_type > in favor of this. If you really need to avoid binding only to interfaces exposing a pair or key/mapped_type, a workaround would be:

...

Your operator>> const hack only addresses constness (in a dangerous manner) for istream reading - assignment will still fail, which *might* be part of what's botching up this situation.

That doesn't fix the lack of operator>> problem, but I've pretty much given up on that. *shrug*

I'll use your idea for const-removal, though, since it does seem to have a slightly less sinister air to it.

Anyway, thanks for your help. Props to your C++ wizardliness. [smile]
After hacking together a lot of boilerplate (including switching from the nonstandard copy_n to the standard generate_n which VS2005 supports, and rolling hand-made implementations of tr1::remove_const since I can't seem to find which header if any VS2005 has for that TR1 function), I've figured out the problem. I've figured out a hackerish solution. I've not figured out the clean solution.

#include <boost/utility.hpp>#include <iostream>#include <utility>#include <algorithm>#include <iterator>#include <vector>#include <map>#include <string>#include <cassert>void matchString( std::istream & is , const std::string & text ) {	std::vector< char > buffer( text.size() );	is.read( &(buffer[0]) , std::streamsize(text.size()) );	assert( text == std::string( buffer.begin() , buffer.end() ) );}namespace std {	template< typename T1, typename T2 >	std::istream& operator>>(std::istream& is, std::pair<T1, T2>& result)	{		is >> result.first;		// VALIDATE(is, pair_first);				matchString(is, ":");			is >> result.second;		// VALIDATE(is, pair_second);			return is;	}}template <typename T> struct non_string_iterable{ 	template <typename U>	static long  test_iterator(U*, typename U::iterator* iter = 0);	static short test_iterator( ... );		static const bool value = ( sizeof(test_iterator((T*)0)) == sizeof(long) );};template <typename T> struct non_string_iterable< std::basic_string<T> >{	static const bool value = false;};template < typename T >class istream_generator {	std::istream_iterator< T > iter;public:	istream_generator( std::istream & is ) : iter( is ) {}	T operator()() { return *(iter++); }};template < typename T > struct remove_const { typedef T type; };template < typename T > struct remove_const< const T > { typedef T type; };template < typename T > struct remove_pair_const {	typedef typename remove_const< T >::type type;};template < typename L , typename R >struct remove_pair_const< std::pair< L , R > > {	typedef std::pair< typename remove_const< L >::type	                 , typename remove_const< R >::type	                 > type;};template< typename Container >typename boost::enable_if< non_string_iterable<Container> , std::istream >::type &operator>>(std::istream& is, Container& container){	matchString(is, "collection");	matchString(is, "(");		unsigned int size;	is >> size;	// VALIDATE(is, array_size);		//std::copy_n( std::istream_iterator<typename Container::value_type>(is),    //                 size, std::insert_iterator<Container>(container, container.begin()) );    std::generate_n( std::insert_iterator< Container >( container , container.begin() ) , size                   , istream_generator< typename remove_pair_const< typename Container::value_type >::type >( is )                   );				 	matchString(is, "}");		return is;}int main() {	std::map< int , int > data;	std::cin >> data;}


Eww. Anyways - removing the namespace std { ... } around the pair operator>> causes compilation to fail in the way originally described. I deduced this to be the problem after compiler errors did not list that version under the "could be ... or ... or ..." list. Since all operands clearly reside within the std:: namespace (not a problem with the container-templated version, apparently), and operator>> is named from within the std:: namespace (within std::istream_iterator), the compiler makes no attempt to look outside the std:: namespace (since the operators there shadow those in the global namespace).

The solution, then, relies only on getting that operator>> back into the list of considerations. Adding it within the std:: namespace in the first place accomplishes this, but is likely in violation of a certain C++ rule - namely, you're not supposed/legally allowed to add to namespace std. The exception to this rule is specializing already existing items, but I was under the impression that operator>>( *intrinsicly supported types* ) was implemented in terms of a member function of std::istream, so I'm not sure that applies here.

You could probably work around this with an enable_if< is_a_pair<T>, .... > qualifier outside of the std namespace, but I'll leave that as an exercise to the reader. I'll leave elaborating on wheither this example is valid C++ to someone more standard-aware than myself.
Quote:Original post by MaulingMonkey
After hacking together a lot of boilerplate (including switching from the nonstandard copy_n to the standard generate_n which VS2005 supports, and rolling hand-made implementations of tr1::remove_const since I can't seem to find which header if any VS2005 has for that TR1 function), I've figured out the problem. I've figured out a hackerish solution. I've not figured out the clean solution.

*** Source Snippet Removed ***

Eww. Anyways - removing the namespace std { ... } around the pair operator>> causes compilation to fail in the way originally described. I deduced this to be the problem after compiler errors did not list that version under the "could be ... or ... or ..." list. Since all operands clearly reside within the std:: namespace (not a problem with the container-templated version, apparently), and operator>> is named from within the std:: namespace (within std::istream_iterator), the compiler makes no attempt to look outside the std:: namespace (since the operators there shadow those in the global namespace).

The solution, then, relies only on getting that operator>> back into the list of considerations. Adding it within the std:: namespace in the first place accomplishes this, but is likely in violation of a certain C++ rule - namely, you're not supposed/legally allowed to add to namespace std. The exception to this rule is specializing already existing items, but I was under the impression that operator>>( *intrinsicly supported types* ) was implemented in terms of a member function of std::istream, so I'm not sure that applies here.

You could probably work around this with an enable_if< is_a_pair<T>, .... > qualifier outside of the std namespace, but I'll leave that as an exercise to the reader. I'll leave elaborating on wheither this example is valid C++ to someone more standard-aware than myself.

You sir, are a magician.

This topic is closed to new replies.

Advertisement