Расширение типа enable_if для исключения совпадающего типа

0

Я использую этот магический заголовок, чтобы получить возможность легко сериализовать контейнеры STL.

Тем не менее, теперь я перешел к еще более причудливым сериализаторам HTML для своих типов, и часть того, что я хотел бы сделать, - это обобщение функциональности operator << для моего нового типа ohtmlstringstream который поддерживается stringstream.

Вот моя (работающая) попытка сделать это (ohtmlstringstream::write - это общедоступный метод шаблона, который передает свой аргумент в operator<< private stringstream 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;
    }
}

Первая проблема, с которой я столкнулся, заключается в том, что в любой момент, когда std::string используется в ohtmlstringstream, она рассматривается как контейнер, чего я не хочу; Я хотел бы рассматривать строки как строки, а не как контейнеры. Конечно, что касается pretty_print, то std :: string - это, безусловно, контейнер символов.

Это выдержка из 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>

Проблема здесь в том, что мне не ясно, как я могу использовать SFINAE и enable_if а остальная часть этого материала для создания еще одного предиката, который вычисляет true для всех контейнеров, кроме std::string.

Теперь это только первая проблема. Вторая проблема - это строка в моем первом списке кода, которая идет os.write("<div class='container'>"); , Обратите внимание, насколько это досадно неспецифично. Мне действительно хотелось бы, чтобы процедура сериализации контейнера сообщала фактический тип контейнера (будь то std::map или std::forward-list или std::vector).

Я хотел бы знать, существует ли какой-то (разумно разумный) метод для достижения этого с помощью шаблонов или я просто должен использовать макросы для явного определения серии шаблонов, по одному для каждого типа контейнера STL: таким образом я могу легко создать точный вид HTML, который я хочу для любого данного контейнера.

Это правда, что перечисление всех контейнеров STL с шаблонами решит обе проблемы. Думаю, я начну это делать. Но мне все же хотелось бы узнать ответ на первоначальный первый вопрос. Как я могу исключить определенный тип, используя enable_if?

  • 0
    I'd like to treat strings as just strings, not as containers Что
  • 0
    ::pretty_print::is_container<std::string>::value == true . У него есть const_iterator , у него есть начало и конец. Однако я бы тоже хотел узнать, что на самом деле делает has_begin_end и почему он работает.
Показать ещё 2 комментария
Теги:
c++11
stl
sfinae

1 ответ

0
Лучший ответ

Добавить &&!std::is_same<Foo, std::string>::value в ваш тест enable_if.

Синтаксис enable_if< condition , необязательного типа >::type может помочь - условием является любое время компиляции bool. Вы можете просто бросить больше вещей там!

Если вам нужен один признак для повторного использования, просто создайте тот, который наследуется от std::integral_constant<bool, логика здесь >{}; ,

Если у вас есть полная поддержка С++ 11, попробуйте написать функцию constexpr которая принимает тип T и возвращает true вместо класса признаков. В С++ 1y это может быть полезно при появлении понятий lite.

  • 0
    Какие-нибудь хитрости для решения второй проблемы? Я думаю, я мог бы написать более частичные шаблоны специализаций или что-то?
  • 0
    @stevenlu просто напишите класс быстрых признаков, который имеет статический метод, который возвращает имя cotainer. Либо оставьте неосуществленную черту неспециализированной черты, чтобы получить ошибки, либо дайте ей общее имя. Это ортогонально к остальной части кода ...
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню