Question

I am using this magical header to gain the ability to easily serialize STL containers.

However, I have now moved on to even more fancy HTML serializers for my types, and part of what I would like to do is generalize the operator << functionality to my new type ohtmlstringstream which is backed by a stringstream.

Here is my (functioning) attempt to do this (ohtmlstringstream::write is a public template method that passes its arg on to the private member stringstream's operator<<):

namespace std {
    template<typename T>
    inline typename enable_if< ::pretty_print::is_container<T>::value, ohtmlstringstream&>::type
    operator<<(ohtmlstringstream& os, const T& container) {
        auto it = std::begin(container);
        const auto the_end = end(container);
        os.write("<div class='container'>");
        while(it != the_end) {
            os << *it;
            it++;
        }
        os.write("</div>");
        return os;
    }
}

The first problem that I run into is that any time a std::string is used on ohtmlstringstream, it is treated as a container, which is something that I do not want; I'd like to treat strings as just strings, not as containers. Of course, as far as pretty_print is concerned, the std::string is most certainly a container of chars.

This is an excerpt from prettyprint.hpp:

namespace pretty_print
{

    // SFINAE type trait to detect whether T::const_iterator exists.

    template<typename T>
    struct has_const_iterator
    {
    private:
        typedef char                      yes;
        typedef struct { char array[2]; } no;

        template <typename C> static yes test(typename C::const_iterator*);
        template <typename C> static no  test(...);
    public:
        static const bool value = sizeof(test<T>(0)) == sizeof(yes);
        typedef T type;
    };

    // SFINAE type trait to detect whether "T::const_iterator T::begin/end() const" exist.

    template <typename T>
    struct has_begin_end_OLD
    {
        struct Dummy { typedef void const_iterator; };
        typedef typename std::conditional<has_const_iterator<T>::value, T, Dummy>::type TType;
        typedef typename TType::const_iterator iter;

        struct Fallback { iter begin() const; iter end() const; };
        struct Derived : TType, Fallback { };

        template<typename C, C> struct ChT;

        template<typename C> static char (&f(ChT<iter (Fallback::*)() const, &C::begin>*))[1];
        template<typename C> static char (&f(...))[2];
        template<typename C> static char (&g(ChT<iter (Fallback::*)() const, &C::end>*))[1];
        template<typename C> static char (&g(...))[2];

        static bool const beg_value = sizeof(f<Derived>(0)) == 2;
        static bool const end_value = sizeof(g<Derived>(0)) == 2;
    };

    template <typename T>
    struct has_begin_end
    {
        template<typename C> static char (&f(typename std::enable_if<
                                             std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::begin)),
                                             typename C::const_iterator(C::*)() const>::value, void>::type*))[1];

        template<typename C> static char (&f(...))[2];

        template<typename C> static char (&g(typename std::enable_if<
                                             std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::end)),
                                             typename C::const_iterator(C::*)() const>::value, void>::type*))[1];

        template<typename C> static char (&g(...))[2];

        static bool const beg_value = sizeof(f<T>(0)) == 1;
        static bool const end_value = sizeof(g<T>(0)) == 1;
    };

    // Basic is_container template; specialize to derive from std::true_type for all desired container types

    template<typename T> struct is_container : public ::std::integral_constant<bool,
    has_const_iterator<T>::value && has_begin_end<T>::beg_value && has_begin_end<T>::end_value> { };

    template<typename T, std::size_t N> struct is_container<T[N]> : public ::std::true_type { };

    template<std::size_t N> struct is_container<char[N]> : public ::std::false_type { };

    template <typename T> struct is_container< ::std::valarray<T>> : public ::std::true_type { }; 

...<snip>

The problem here is that it's not clear to me how I could use SFINAE and enable_if and the rest of this stuff to build yet another predicate that evalutes to true for all containers except std::string.

Now, that's only the first problem. The second problem is the line in my first code listing that goes os.write("<div class='container'>");. Note how annoyingly unspecific this is. I really would like the serialization routine of the container to report the actual type of the container (be it std::map or std::forward-list or std::vector).

What I'd like to know is whether there exists some (reasonably sane) method to achieve this with templates, or whether I really should just use macros to explicitly define a series of templates, one for each STL container type: That way I can easily build the exact kind of HTML I want for any given container.

It is true that enumerating all of the STL containers with templates would solve both problems. I think I'll start doing this. But I'd still like to know the answer to the original first question. How can I exclude a particular type using enable_if?

Was it helpful?

Solution

Add &&!std::is_same<Foo, std::string>::value to your enable_if test.

The syntax of enable_if< condition , optional type >::type may help -- the condition is any compile time bool. You can just toss more stuff in there!

If you want one trait for reuse, just create one that inherits from std::integral_constant<bool, logic here >{};.

If you have full C++11 support, try writing a constexpr function takes the type T and returns true instead of a traits class. In C++1y this may be useful when concepts lite arrives.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top