Утилита конкатенации строк в C ++ 11

Я наконец опубликовал утилиту конкатенации, и я чувствую, что это может быть очень хорошей помощью для многих людей, но прежде чем я ее продвигаю, я хотел бы как можно больше отполировать детали. Поэтому я хотел бы попросить вас просмотреть это менее 300 строк кода.

#ifndef THEYPSILON_CONCAT
#define THEYPSILON_CONCAT

#include <sstream>
#include <tuple>

namespace theypsilon {

    template <typename CharT>
    struct separator_t {
        const CharT* sep;
        constexpr explicit separator_t(const CharT* s) noexcept: sep{s} {} 
    };

    template <typename CharT>
    constexpr separator_t<CharT> separator(const CharT* s) {
        return separator_t<CharT>(s);
    }

    namespace sep {
        constexpr char none [] = "";
        constexpr char space[] = " ";
        constexpr char endl [] = "\n";
        constexpr char coma [] = ", ";
        constexpr char plus [] = " + ";
    };

    namespace { // type helpers and traits
        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;
        };

        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;
        };

        template<typename T, typename CharT>
        constexpr bool is_writable_stream() {
            return  std::is_same<T, std::basic_ostringstream<CharT>>::value || 
                    std::is_same<T, std::basic_stringstream <CharT>>::value ||
                    std::is_same<T, std::basic_ostream<CharT>>::value;
        }

        template<typename T, typename CharT = char> 
        constexpr bool is_stringstream() {
            return  std::is_same<T, std::basic_istringstream<CharT>>::value || 
                    std::is_same<T, std::basic_ostringstream<CharT>>::value ||
                    std::is_same<T, std::basic_stringstream <CharT>>::value;
        }

        template<typename T> 
        constexpr bool is_char_type() {
            return  std::is_same<typename std::decay<T>::type, char    >::value || 
                    std::is_same<typename std::decay<T>::type, wchar_t >::value || 
                    std::is_same<typename std::decay<T>::type, char16_t>::value || 
                    std::is_same<typename std::decay<T>::type ,char32_t>::value;
        }

        template<typename T, typename CharT = char>
        constexpr bool is_c_str() { 
            return  std::is_same<typename std::decay<T>::type, CharT const *>::value ||
                    std::is_same<typename std::decay<T>::type, CharT       *>::value;
        }

        template<typename T> 
        constexpr bool is_char_sequence() {
            return  is_c_str<T,     char>() ||
                    is_c_str<T,  wchar_t>() ||
                    is_c_str<T, char16_t>() ||
                    is_c_str<T, char32_t>();
        }

        template<typename T> 
        constexpr bool is_container() {
            return (has_const_iterator<T>::value && 
                    has_begin_end<T>::beg_value  && 
                    has_begin_end<T>::end_value  &&
                    !std::is_same<T, std::string>::value &&
                    !is_stringstream<T>()) 
            || (std::is_array<T>::value && !is_char_sequence<T*>());
        }

        template <typename T>
        constexpr bool is_string() {
            return  std::is_same<T,std::string   >::value ||
                    std::is_same<T,std::wstring  >::value ||
                    std::is_same<T,std::u16string>::value ||
                    std::is_same<T,std::u32string>::value;
        }

        template <typename T>
        constexpr bool is_basic_type() {
            return std::is_scalar<T>::value || is_string<T>();
        }

        template <typename T, template <typename...> class Template>
        struct is_specialization_of : std::false_type {};

        template <template <typename...> class Template, typename... Args>
        struct is_specialization_of<Template<Args...>, Template> : std::true_type {};

        template <typename T>
        constexpr bool is_modifier() {
            return  !is_container    <T>() && !is_stringstream    <T>() && 
                    !is_char_sequence<T>() && !is_basic_type<T>() &&
                    !std::is_array<T>::value && 
                    !is_specialization_of<T, std::tuple>::value;
        }
    }

    namespace { // concat_intern

        template <typename CharT, char head, char... tail>
        std::basic_string<CharT> get_separator() { return {head, tail...}; }

        template <typename W, typename S>
        void separate(W& writter, const S* separator) { 
            if (separator) writter << separator;
        }

        template <typename W, typename S>
        void separate(W& writter, const S& separator) { 
            writter << separator;
        }

        template <typename CharT, typename W>
        std::basic_string<CharT> concat_to_string(const W& writter) {
            return writter.good() ? writter.str() : std::basic_string<CharT>();
        }

        template <typename CharT, typename W, typename S, typename T>
        void concat_intern_write(W&, const S&, bool, const T&);

        template <typename CharT, typename W, typename S, typename T>
            typename std::enable_if<is_char_sequence<T*>(),
        void>::type concat_intern_recursion(W& writter, const S& separator, const T* v) {
            if (v) writter << v;
        }

        template <typename CharT, typename W, typename S, typename T>
            typename std::enable_if<
                (!is_container<T>() && !is_stringstream<T>() && !is_char_sequence<T>()) || is_modifier<T>(),
        void>::type concat_intern_recursion(W& writter, const S& separator, const T& v) {
            writter << v;
        }

        template <typename CharT, typename W, typename S, typename T>
            typename std::enable_if<is_stringstream<T>(),
        void>::type concat_intern_recursion(W& writter, const S& separator, const T& v) {
            if (v.good()) writter << concat_to_string<CharT>(v);
            else writter.setstate(v.rdstate());
        }

        template <typename CharT, typename W, typename S, typename T>
            typename std::enable_if<is_container<T>(),
        void>::type concat_intern_recursion(W& writter, const S& separator, const T& container) {
            auto it = std::begin(container), et = std::end(container);
            while(it != et) {
                auto element = *it;
                it++;
                concat_intern_write<CharT>(writter, separator, it != et, element);
            }
        }

        template<unsigned N, unsigned Last>
        struct tuple_printer {
            template<typename CharT, typename W, typename S, typename T>
            static void print(W& writter, const S& separator, const T& v) {
                concat_intern_write<CharT>(writter, separator, true, std::get<N>(v));
                tuple_printer<N + 1, Last>::template print<CharT>(writter, separator, v);
            }
        };

        template<unsigned N>
        struct tuple_printer<N, N> {
            template<typename CharT, typename W, typename S, typename T>
            static void print(W& writter, const S& separator, const T& v) {
                concat_intern_write<CharT>(writter, separator, false, std::get<N>(v));
            }
        };

        template <typename CharT, typename W, typename S, typename... Args>
        void concat_intern_recursion(W& writter, const S& separator, const std::tuple<Args...>& v) {
            tuple_printer<0, sizeof...(Args) - 1>::template print<CharT>(writter, separator, v);
        }

        template <typename CharT, typename W, typename S, typename T, typename... Args>
        void concat_intern_recursion(W& writter, const S& separator, const T& head, const Args&... tail) {
            concat_intern_write<CharT>(writter, separator, true, head);
            concat_intern_recursion<CharT>(writter, separator, tail...);
        }

        template <typename CharT, typename W, typename S, typename T>
        inline void concat_intern_write(W& writter, const S& separator, bool b, const T& v) {
            concat_intern_recursion<CharT>(writter, separator, v);
            if (b && !is_modifier<T>()) separate(writter, separator);
        }

        template <typename CharT, typename S, typename T, typename... Args,
            typename = typename std::enable_if<is_writable_stream<T, CharT>() == true, T>::type>
        std::basic_string<CharT> concat_intern(const S& separator, T& writter, const Args&... seq) {
            concat_intern_recursion<CharT>(writter, separator, seq...);
            return concat_to_string<CharT>(writter);
        }

        template <typename CharT, typename S, typename... Args>
        std::basic_string<CharT> concat_intern(const S& separator, const Args&... seq) {
            std::basic_ostringstream<CharT> writter;
            return concat_intern<CharT>(separator, writter, seq...);
        }
    }

    template <typename CharT = char, typename... Args>
    std::basic_string<CharT> concat(const separator_t<CharT>& sep, Args&&... seq) {
        return concat_intern<CharT>(
            sep.sep, 
            std::forward<Args>(seq)...
        );
    }

    template <char head, char... tail, typename F, typename... Args,
        typename = typename std::enable_if<std::is_same<F, separator_t<char>>::value == false, F>::type>
    std::basic_string<char> concat(F&& first, Args&&... rest) {
        return concat_intern<char>(
            get_separator<char, head, tail...>(), 
            std::forward<F>(first), 
            std::forward<Args>(rest)...
        );
    }

    template <const char* sep, typename F, typename... Args,
        typename = typename std::enable_if<std::is_same<F, separator_t<char>>::value == false, F>::type>
    std::basic_string<char> concat(F&& first, Args&&... rest) {
        return concat_intern<char>(
            sep, 
            std::forward<F>(first), 
            std::forward<Args>(rest)...
        );
    }

    template <typename CharT = char, typename F, typename... Args,
        typename = typename std::enable_if<std::is_same<F, separator_t<CharT>>::value == false, F>::type>
    std::basic_string<CharT> concat(F&& first, Args&&... rest) {
        return concat_intern<CharT>(
            (const CharT*)nullptr, 
            std::forward<F>(first), 
            std::forward<Args>(rest)...
        );
    }

    template <std::ostream& sep (std::ostream&), typename CharT = char, typename F, typename... Args,
        typename = typename std::enable_if<std::is_same<F, separator_t<CharT>>::value == false, F>::type>
    std::basic_string<CharT> concat(F&& first, Args&&... rest) {
        return concat_intern<CharT>(
            sep, 
            std::forward<F>(first), 
            std::forward<Args>(rest)...
        );
    }

}

#endif
  • Этот код соответствует общим соглашениям?
  • Как вы думаете, есть ли способ улучшить эту реализацию с точки зрения эффективности, универсальности или удобства?
  • Как вы думаете, это может также выиграть от новых изменений C ++ 14?

Это будет образец кода клиента:

std::cout << concat(1,2,3,4,5) << std::endl; // prints "12345"

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

11 голосов | спросил José Manuel 27 J000000Sunday14 2014, 20:01:19

2 ответа


7

Единственный совет, который я мог бы предложить, - использовать «современные» стили стиля, которые хорошо работают со стандартной библиотекой с помощью std::integral_constant и выражение SFINAE.

Например, has_const_iterator можно переписать следующим образом:

template<typename...>
struct void_ {
    using type = void;
};

template<typename... Args>
using Void = typename void_<Args...>::type;

template<typename T, typename U = void>
struct has_const_iterator : public std::false_type {};

template<typename T>
struct has_const_iterator<T, Void<typename T::const_iterator>> : public std::true_type {};

Теперь вы получаете преимущество std::integral_constant без необходимости переопределять ::value. Он также будет работать с диспетчером тегов, который принимает std::true_type или std::false_type в качестве параметров.

Довольно необычно определять черты как функции constexpr bool, так как это запрещает использование более высоких упорядоченных мета-функций, поэтому я ' d предложите переместить их в свои собственные черты, как это было с помощью has_begin_end и has_const_iterator.

Наконец, has_begin_end можно упростить с помощью выражения SFINAE, как показано здесь:

struct has_begin_end_impl {
    template<typename T, typename B = decltype(std::declval<T&>().begin()),
                         typename E = decltype(std::declval<T&>().end())>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

template<typename T>
struct has_begin_end : public decltype(has_begin_end_impl::test<T>(0)) {};

Вы можете узнать больше о выражении SFINAE здесь . Что касается причины, что есть test(int) и test(...), это для того, чтобы сделать вызов test(0) однозначным.

ответил Rapptz 28 J000000Monday14 2014, 04:51:27
3

В целом, это хорошая работа. У меня всего несколько незначительных баллов.

Удалите официальное имя separarator для тихих предупреждений компилятора

Когда я компилирую код с g ++ с максимальными уровнями предупреждений, он жалуется, что параметр separator не используется в нескольких функциях. Отслеживая это, кажется, что ряд функций concat_intern_recursion не использует его, поэтому в моей локальной копии я удалил формальное имя параметра separator (то есть, я просто оставляю const S&) из этих вызовов и теперь не получают никаких предупреждений.

Исправить написание writer

Слово написано как writter во всем коде и документации, но правильное написание writer с одним t.

Рассмотрим, может ли поддерживаться constexpr

Я не уверен, что это можно было бы сделать, но одно, что я пытался сделать, чтобы оно не поддерживалось, было следующим:

constexpr std::string foo = concat(separator(" < "),1,3,5);

Компилятор сказал, что это связано с тем, что std::string имеет нетривиальный деструктор, поэтому, возможно, это из ваших рук, но о чем подумать.

ответил Edward 28 J000000Monday14 2014, 00:41:51

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132