I have template overloads for operator>>()
where I need to distinguish between containers that can be resized, e.g., vector
, and containers that cannot, e.g., array
. I am currently just using the presence of an allocator_type
trait (see code, below)--and it works just fine--but was wondering if there is a more explicit way of testing this.
template <class T>
struct is_resizable {
typedef uint8_t yes;
typedef uint16_t no;
template <class U>
static yes test(class U::allocator_type *);
template <class U>
static no test(...);
static const bool value = sizeof test<T>(0) == sizeof yes;
};
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value && is_resizable<C>::value,
istream &
>::type
operator>>(istream &ibs, C &c)
{
c.resize(ibs.repeat() == 0 ? c.size() : ibs.repeat());
for (typename C::iterator it = c.begin(); it != c.end(); ++it)
{
C::value_type v;
ibs >> v;
*it = v;
}
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value && !is_resizable<C>::value,
istream &
>::type
operator>>(istream &ibs, C &c)
{
for (typename C::iterator it = c.begin(); it != c.end(); ++it)
ibs >> *it;
return ibs;
}
Thanks to help from @Jarod42 on a separate question, I have a solution that works with C++98, C++03, and C++11; g++ and VS2015. Also, for the problem child, std::vector<bool>
.
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature) \
template <typename U> \
class traitsName \
{ \
private: \
typedef boost::uint8_t yes; typedef boost::uint16_t no; \
template<typename T, T> struct helper; \
template<typename T> static yes check(helper<signature, &funcName>*);\
template<typename T> static no check(...); \
public: \
static const bool value = sizeof check<U>(0) == sizeof(yes); \
}
DEFINE_HAS_SIGNATURE(has_resize_1, T::resize, void (T::*)(typename T::size_type));
DEFINE_HAS_SIGNATURE(has_resize_2, T::resize, void (T::*)(typename T::size_type, \
typename T::value_type));
This is how it's used, below. Notice that both the has_resize_1
and has_resize_2
member-function signatures for resize()
are checked. That's because before C++11, resize()
had a single signature with two parameters, the last with a default value; as of C++11, it has two signatures--one with one parameter and the other with two parameters. Moreover, VS2015 apparently has three signatures--all of the above. The solution is just to check for both signatures all the time.
There is probably a way to combine the two checks into a single type trait, such as has_resize<C>::value
. Tell me if you know.
template <typename T>
typename boost::enable_if_c<
!boost::spirit::traits::is_container<T>::value,
xstream &>::type
operator>>(xstream &ibs, T &b)
{
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value &&
(has_resize_1<C>::value || has_resize_2<C>::value),
xstream &
>::type
operator>>(xstream &ibs, C &c)
{
typename C::value_type v;
ibs >> v;
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value &&
!(has_resize_1<C>::value || has_resize_2<C>::value),
xstream &
>::type
operator>>(xstream &ibs, C &c)
{
typename C::value_type v;
ibs >> v;
return ibs;
}