প্রিটি-প্রিন্ট সি ++ এসটিএল পাত্রে


389

এই পোস্টের শেষে আপডেটগুলি নোট করুন।

আপডেট: আমি এই লাইব্রেরির জন্য গিটহাবে একটি সর্বজনীন প্রকল্প তৈরি করেছি !


আমি একটি একক টেমপ্লেট রাখতে চাই যা একবার এবং সমস্ত ক্ষেত্রে সমস্ত এসটিএল কনটেইনারগুলির মাধ্যমে প্রিন্ট-প্রিন্টিংয়ের যত্ন নেয় operator<<। সিউডো কোডে, আমি এই জাতীয় কিছু খুঁজছি:

template<container C, class T, String delim = ", ", String open = "[", String close = "]">
std::ostream & operator<<(std::ostream & o, const C<T> & x)
{
    o << open;
    // for (typename C::const_iterator i = x.begin(); i != x.end(); i++) /* Old-school */
    for (auto i = x.begin(); i != x.end(); i++)
    {
        if (i != x.begin()) o << delim;
        o << *i;
    }
    o << close;
    return o;
}

এখন আমি এখানে প্রচুর টেমপ্লেট যাদু দেখেছি যা আমি কখনই সম্ভব বলে ভাবিনি, তাই আমি ভাবছি যে কেউ যদি এমন কিছু পরামর্শ দিতে পারে যা সমস্ত পাত্রে মেলে C. ?

অনেক ধন্যবাদ!


আপডেট (এবং সমাধান)

এই সমস্যাটি উত্থাপনের পরে আবার চ্যানেল 9 এ , আমি সোভেন গ্রোটের কাছ থেকে একটি চমত্কার উত্তর পেয়েছি, যা কিছুটা SFINAE টাইপ ট্রেইটিংয়ের সাথে মিলিত হয়ে সমস্যাটি সম্পূর্ণ সাধারণ এবং নীড়সুলভ ফ্যাশনে সমাধান করার জন্য উপস্থিত হয়। ডিলিমিটারগুলি স্বতন্ত্রভাবে বিশেষীকরণযোগ্য হতে পারে, স্ট্যান্ড :: সেট এর একটি বিশেষ বিশেষায়নের অন্তর্ভুক্ত রয়েছে, পাশাপাশি কাস্টম ডিলিমিটারগুলি ব্যবহারের উদাহরণ রয়েছে।

সহায়ক "র‌্যাপ_আরে ()" কাঁচা সি অ্যারেগুলি মুদ্রণ করতে ব্যবহার করা যেতে পারে। আপডেট: জোড় এবং টিপলগুলি মুদ্রণের জন্য উপলব্ধ; ডিফল্ট ডিলিমিটারগুলি হ'ল বৃত্তাকার বন্ধনী।

সক্ষম-if ধরণের বৈশিষ্ট্যের জন্য C ++ 0x প্রয়োজন, তবে কিছু সংশোধন করে এটির একটি C ++ 98 সংস্করণ করা সম্ভব হবে। টিপলগুলিকে বৈচিত্র্যযুক্ত টেম্পলেটগুলির প্রয়োজন হয়, সুতরাং C ++ 0x।

আমি সুইভেনকে এখানে সমাধান পোস্ট করতে বলেছি যাতে আমি এটি গ্রহণ করতে পারি তবে এর মধ্যে আমি কোডটি নিজেই রেফারেন্সের জন্য পোস্ট করতে চাই। ( আপডেট: সোভেন এখন নীচে তার কোড পোস্ট করেছে, যা আমি স্বীকৃত উত্তর দিয়েছি My আমার নিজের কোডটি ধারক ধরণের বৈশিষ্ট্য ব্যবহার করে, যা আমার পক্ষে কাজ করে তবে কনটেইনার ক্লাসগুলির সাথে অপ্রত্যাশিত আচরণের কারণ হতে পারে যা পুনরুক্তি সরবরাহ করে provide)

শিরোনাম (প্রশংসাপত্র। H):

#ifndef H_PRETTY_PRINT
#define H_PRETTY_PRINT


#include <type_traits>
#include <iostream>
#include <utility>
#include <tuple>


namespace std
{
    // Pre-declarations of container types so we don't actually have to include the relevant headers if not needed, speeding up compilation time.
    template<typename T, typename TTraits, typename TAllocator> class set;
}

namespace pretty_print
{

    // SFINAE type trait to detect a container based on whether T::const_iterator exists.
    // (Improvement idea: check also if begin()/end() exist.)

    template<typename T>
    struct is_container_helper
    {
    private:
        template<typename C> static char test(typename C::const_iterator*);
        template<typename C> static int  test(...);
    public:
        static const bool value = sizeof(test<T>(0)) == sizeof(char);
    };


    // 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, is_container_helper<T>::value> { };


    // Holds the delimiter values for a specific character type

    template<typename TChar>
    struct delimiters_values
    {
        typedef TChar char_type;
        const TChar * prefix;
        const TChar * delimiter;
        const TChar * postfix;
    };


    // Defines the delimiter values for a specific container and character type

    template<typename T, typename TChar>
    struct delimiters
    {
        typedef delimiters_values<TChar> type;
        static const type values; 
    };


    // Default delimiters

    template<typename T> struct delimiters<T, char> { static const delimiters_values<char> values; };
    template<typename T> const delimiters_values<char> delimiters<T, char>::values = { "[", ", ", "]" };
    template<typename T> struct delimiters<T, wchar_t> { static const delimiters_values<wchar_t> values; };
    template<typename T> const delimiters_values<wchar_t> delimiters<T, wchar_t>::values = { L"[", L", ", L"]" };


    // Delimiters for set

    template<typename T, typename TTraits, typename TAllocator> struct delimiters< ::std::set<T, TTraits, TAllocator>, char> { static const delimiters_values<char> values; };
    template<typename T, typename TTraits, typename TAllocator> const delimiters_values<char> delimiters< ::std::set<T, TTraits, TAllocator>, char>::values = { "{", ", ", "}" };
    template<typename T, typename TTraits, typename TAllocator> struct delimiters< ::std::set<T, TTraits, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
    template<typename T, typename TTraits, typename TAllocator> const delimiters_values<wchar_t> delimiters< ::std::set<T, TTraits, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };


    // Delimiters for pair (reused for tuple, see below)

    template<typename T1, typename T2> struct delimiters< ::std::pair<T1, T2>, char> { static const delimiters_values<char> values; };
    template<typename T1, typename T2> const delimiters_values<char> delimiters< ::std::pair<T1, T2>, char>::values = { "(", ", ", ")" };
    template<typename T1, typename T2> struct delimiters< ::std::pair<T1, T2>, wchar_t> { static const delimiters_values<wchar_t> values; };
    template<typename T1, typename T2> const delimiters_values<wchar_t> delimiters< ::std::pair<T1, T2>, wchar_t>::values = { L"(", L", ", L")" };


    // Functor to print containers. You can use this directly if you want to specificy a non-default delimiters type.

    template<typename T, typename TChar = char, typename TCharTraits = ::std::char_traits<TChar>, typename TDelimiters = delimiters<T, TChar>>
    struct print_container_helper
    {
        typedef TChar char_type;
        typedef TDelimiters delimiters_type;
        typedef std::basic_ostream<TChar, TCharTraits> & ostream_type;

        print_container_helper(const T & container)
        : _container(container)
        {
        }

        inline void operator()(ostream_type & stream) const
        {
            if (delimiters_type::values.prefix != NULL)
                stream << delimiters_type::values.prefix;

            for (typename T::const_iterator beg = _container.begin(), end = _container.end(), it = beg; it != end; ++it)
            {
                if (it != beg && delimiters_type::values.delimiter != NULL)
                    stream << delimiters_type::values.delimiter;

                stream << *it;
            }

            if (delimiters_type::values.postfix != NULL)
                stream << delimiters_type::values.postfix;
        }

    private:
        const T & _container;
    };


    // Type-erasing helper class for easy use of custom delimiters.
    // Requires TCharTraits = std::char_traits<TChar> and TChar = char or wchar_t, and MyDelims needs to be defined for TChar.
    // Usage: "cout << pretty_print::custom_delims<MyDelims>(x)".

    struct custom_delims_base
    {
        virtual ~custom_delims_base() { }
        virtual ::std::ostream & stream(::std::ostream &) = 0;
        virtual ::std::wostream & stream(::std::wostream &) = 0;
    };

    template <typename T, typename Delims>
    struct custom_delims_wrapper : public custom_delims_base
    {
        custom_delims_wrapper(const T & t) : t(t) { }

        ::std::ostream & stream(::std::ostream & stream)
        {
          return stream << ::pretty_print::print_container_helper<T, char, ::std::char_traits<char>, Delims>(t);
        }
        ::std::wostream & stream(::std::wostream & stream)
        {
          return stream << ::pretty_print::print_container_helper<T, wchar_t, ::std::char_traits<wchar_t>, Delims>(t);
        }

    private:
        const T & t;
    };

    template <typename Delims>
    struct custom_delims
    {
        template <typename Container> custom_delims(const Container & c) : base(new custom_delims_wrapper<Container, Delims>(c)) { }
        ~custom_delims() { delete base; }
        custom_delims_base * base;
    };

} // namespace pretty_print


template <typename TChar, typename TCharTraits, typename Delims>
inline std::basic_ostream<TChar, TCharTraits> & operator<<(std::basic_ostream<TChar, TCharTraits> & stream, const pretty_print::custom_delims<Delims> & p)
{
    return p.base->stream(stream);
}


// Template aliases for char and wchar_t delimiters
// Enable these if you have compiler support
//
// Implement as "template<T, C, A> const sdelims::type sdelims<std::set<T,C,A>>::values = { ... }."

//template<typename T> using pp_sdelims = pretty_print::delimiters<T, char>;
//template<typename T> using pp_wsdelims = pretty_print::delimiters<T, wchar_t>;


namespace std
{
    // Prints a print_container_helper to the specified stream.

    template<typename T, typename TChar, typename TCharTraits, typename TDelimiters>
    inline basic_ostream<TChar, TCharTraits> & operator<<(basic_ostream<TChar, TCharTraits> & stream,
                                                          const ::pretty_print::print_container_helper<T, TChar, TCharTraits, TDelimiters> & helper)
    {
        helper(stream);
        return stream;
    }

    // Prints a container to the stream using default delimiters

    template<typename T, typename TChar, typename TCharTraits>
    inline typename enable_if< ::pretty_print::is_container<T>::value, basic_ostream<TChar, TCharTraits>&>::type
    operator<<(basic_ostream<TChar, TCharTraits> & stream, const T & container)
    {
        return stream << ::pretty_print::print_container_helper<T, TChar, TCharTraits>(container);
    }

    // Prints a pair to the stream using delimiters from delimiters<std::pair<T1, T2>>.
    template<typename T1, typename T2, typename TChar, typename TCharTraits>
    inline basic_ostream<TChar, TCharTraits> & operator<<(basic_ostream<TChar, TCharTraits> & stream, const pair<T1, T2> & value)
    {
        if (::pretty_print::delimiters<pair<T1, T2>, TChar>::values.prefix != NULL)
            stream << ::pretty_print::delimiters<pair<T1, T2>, TChar>::values.prefix;

        stream << value.first;

        if (::pretty_print::delimiters<pair<T1, T2>, TChar>::values.delimiter != NULL)
            stream << ::pretty_print::delimiters<pair<T1, T2>, TChar>::values.delimiter;

        stream << value.second;

        if (::pretty_print::delimiters<pair<T1, T2>, TChar>::values.postfix != NULL)
            stream << ::pretty_print::delimiters<pair<T1, T2>, TChar>::values.postfix;

        return stream;
    }
} // namespace std

// Prints a tuple to the stream using delimiters from delimiters<std::pair<tuple_dummy_t, tuple_dummy_t>>.

namespace pretty_print
{
    struct tuple_dummy_t { }; // Just if you want special delimiters for tuples.

    typedef std::pair<tuple_dummy_t, tuple_dummy_t> tuple_dummy_pair;

    template<typename Tuple, size_t N, typename TChar, typename TCharTraits>
    struct pretty_tuple_helper
    {
        static inline void print(::std::basic_ostream<TChar, TCharTraits> & stream, const Tuple & value)
        {
            pretty_tuple_helper<Tuple, N - 1, TChar, TCharTraits>::print(stream, value);

            if (delimiters<tuple_dummy_pair, TChar>::values.delimiter != NULL)
                stream << delimiters<tuple_dummy_pair, TChar>::values.delimiter;

            stream << std::get<N - 1>(value);
        }
    };

    template<typename Tuple, typename TChar, typename TCharTraits>
    struct pretty_tuple_helper<Tuple, 1, TChar, TCharTraits>
    {
        static inline void print(::std::basic_ostream<TChar, TCharTraits> & stream, const Tuple & value) { stream << ::std::get<0>(value); }
    };
} // namespace pretty_print


namespace std
{
    template<typename TChar, typename TCharTraits, typename ...Args>
    inline basic_ostream<TChar, TCharTraits> & operator<<(basic_ostream<TChar, TCharTraits> & stream, const tuple<Args...> & value)
    {
        if (::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.prefix != NULL)
            stream << ::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.prefix;

        ::pretty_print::pretty_tuple_helper<const tuple<Args...> &, sizeof...(Args), TChar, TCharTraits>::print(stream, value);

        if (::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.postfix != NULL)
            stream << ::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.postfix;

        return stream;
    }
} // namespace std


// A wrapper for raw C-style arrays. Usage: int arr[] = { 1, 2, 4, 8, 16 };  std::cout << wrap_array(arr) << ...

namespace pretty_print
{
    template <typename T, size_t N>
    struct array_wrapper
    {
        typedef const T * const_iterator;
        typedef T value_type;

        array_wrapper(const T (& a)[N]) : _array(a) { }
        inline const_iterator begin() const { return _array; }
        inline const_iterator end() const { return _array + N; }

    private:
        const T * const _array;
    };
} // namespace pretty_print

template <typename T, size_t N>
inline pretty_print::array_wrapper<T, N> pretty_print_array(const T (& a)[N])
{
    return pretty_print::array_wrapper<T, N>(a);
}


#endif

ব্যবহারের উদাহরণ:

#include <iostream>
#include <vector>
#include <unordered_map>
#include <map>
#include <set>
#include <array>
#include <tuple>
#include <utility>
#include <string>

#include "prettyprint.h"

// Specialization for a particular container
template<> const pretty_print::delimiters_values<char> pretty_print::delimiters<std::vector<double>, char>::values = { "|| ", " : ", " ||" };

// Custom delimiters for one-off use
struct MyDel { static const delimiters_values<char> values; };
const delimiters_values<char> MyDel::values = { "<", "; ", ">" };

int main(int argc, char * argv[])
{
  std::string cs;
  std::unordered_map<int, std::string> um;
  std::map<int, std::string> om;
  std::set<std::string> ss;
  std::vector<std::string> v;
  std::vector<std::vector<std::string>> vv;
  std::vector<std::pair<int, std::string>> vp;
  std::vector<double> vd;
  v.reserve(argc - 1);
  vv.reserve(argc - 1);
  vp.reserve(argc - 1);
  vd.reserve(argc - 1);

  std::cout << "Printing pairs." << std::endl;

  while (--argc)
  {
    std::string s(argv[argc]);
    std::pair<int, std::string> p(argc, s);

    um[argc] = s;
    om[argc] = s;
    v.push_back(s);
    vv.push_back(v);
    vp.push_back(p);
    vd.push_back(1./double(i));
    ss.insert(s);
    cs += s;

    std::cout << "  " << p << std::endl;
  }

  std::array<char, 5> a{{ 'h', 'e', 'l', 'l', 'o' }};

  std::cout << "Vector: " << v << std::endl
            << "Incremental vector: " << vv << std::endl
            << "Another vector: " << vd << std::endl
            << "Pairs: " << vp << std::endl
            << "Set: " << ss << std::endl
            << "OMap: " << om << std::endl
            << "UMap: " << um << std::endl
            << "String: " << cs << std::endl
            << "Array: " << a << std::endl
  ;

  // Using custom delimiters manually:
  std::cout << pretty_print::print_container_helper<std::vector<std::string>, char, std::char_traits<char>, MyDel>(v) << std::endl;

  // Using custom delimiters with the type-erasing helper class
  std::cout << pretty_print::custom_delims<MyDel>(v) << std::endl;

  // Pairs and tuples and arrays:
  auto a1 = std::make_pair(std::string("Jello"), 9);
  auto a2 = std::make_tuple(1729);
  auto a3 = std::make_tuple("Qrgh", a1, 11);
  auto a4 = std::make_tuple(1729, 2875, std::pair<double, std::string>(1.5, "meow"));
  int arr[] = { 1, 4, 9, 16 };

  std::cout << "C array: " << wrap_array(arr) << std::endl
            << "Pair: " << a1 << std::endl
            << "1-tuple: " << a2 << std::endl
            << "n-tuple: " << a3 << std::endl
            << "n-tuple: " << a4 << std::endl
  ;
}

উন্নতির জন্য আরও ধারণা:

  • std::tuple<...>একইভাবে জন্য আউটপুট বাস্তবায়ন আমাদের এটি আছে std::pair<S,T> আপডেট: এটি এখন একটি পৃথক প্রশ্ন ! আপআপডেট: এটি এখন বাস্তবায়িত হয়েছে, জিওকে ধন্যবাদ!
  • নেমস্পেসগুলি যুক্ত করুন যাতে সাহায্যকারী শ্রেণিগুলি বিশ্বব্যাপী নেমস্পেসে রক্তপাত না করে। সম্পন্ন
  • কাস্টম ডিলিমিটার ক্লাস তৈরি করার সুবিধার্থে টেমপ্লেট অ্যালিয়াস (বা এর মতো কিছু) যোগ করুন, বা প্রিপ্রসেসর ম্যাক্রোস?

সাম্প্রতিক হাল নাগাদ:

  • আমি মুদ্রণ ফাংশনে লুপের জন্য সরল পক্ষে কাস্টম আউটপুট পুনরায় মুছে ফেললাম।
  • সমস্ত বাস্তবায়ন বিশদ এখন pretty_printনামস্থানে রয়েছে। কেবলমাত্র বৈশ্বিক স্ট্রিম অপারেটর এবং pretty_print_arrayর‌্যাপার বিশ্বব্যাপী নেমস্পেসে রয়েছে।
  • নাম স্পেসিং ঠিক করা হয়েছে যাতে operator<<এখন সঠিকভাবে std

মন্তব্য:

  • আউটপুট পুনরায় মুছে ফেলার অর্থ হ'ল std::copy()সুন্দর-মুদ্রণ পেতে কোনও উপায় নেই । আমি পছন্দসই পুনরুদ্ধার করতে পারি যদি এটি কোনও কাঙ্ক্ষিত বৈশিষ্ট্য হয় তবে নীচে সোভেনের কোডটির প্রয়োগ রয়েছে।
  • প্রবহমানকারীদের অবজেক্ট কনস্ট্যান্টের চেয়ে কমপাইল-টাইম কনস্ট্যান্ট তৈরি করা সচেতন নকশার সিদ্ধান্ত ছিল। তার অর্থ আপনি রানটাইমের সময় ডিলিমিটার সরবরাহ করতে পারবেন না, তবে এর অর্থ হ'ল কোনও অপ্রয়োজনীয় ওভারহেড নেই। ডেনিস জিকিফুজ নীচে সোভেনের কোডটিতে একটি মন্তব্যে একটি অবজেক্ট ভিত্তিক ডিলিমিটার কনফিগারেশন প্রস্তাব করেছেন। যদি ইচ্ছা হয় তবে এটি বিকল্প বৈশিষ্ট্য হিসাবে প্রয়োগ করা যেতে পারে।
  • নেস্টেড কনটেইনার ডিলিমিটারগুলি কীভাবে কাস্টমাইজ করা যায় তা বর্তমানে স্পষ্ট নয়।
  • মনে রাখবেন যে এই গ্রন্থাগারের উদ্দেশ্য হ'ল দ্রুত ধারক মুদ্রণ সুবিধার জন্য যা আপনার পক্ষে শূন্য কোডিং প্রয়োজন । এটি কোনও উদ্দেশ্যমূলক বিন্যাসকরণ গ্রন্থাগার নয়, বরং ধারক পরিদর্শনের জন্য বয়লার-প্লেট কোড লেখার প্রয়োজনীয়তা হ্রাস করার জন্য একটি বিকাশকারী সরঞ্জাম।

যারা অবদান রেখেছেন তাদের সবাইকে ধন্যবাদ!


দ্রষ্টব্য: আপনি যদি কাস্টম ডিলিমিটর মোতায়েনের জন্য একটি দ্রুত উপায় খুঁজছেন, তবে ক্ষয়ের ক্ষয়টি ব্যবহারের একটি উপায় এখানে। আমরা ধরে নিই যে আপনি ইতিমধ্যে একটি ডিলিমিটার ক্লাস তৈরি করেছেন MyDel, যেমন বলুন:

struct MyDel { static const pretty_print::delimiters_values<char> values; };
const pretty_print::delimiters_values<char> MyDel::values = { "<", "; ", ">" };

এখন আমরা সেইসব ডিলিমিটার ব্যবহার করে std::cout << MyPrinter(v) << std::endl;কিছু ধারক লিখতে সক্ষম হতে চাই vMyPrinterএকটি ধরণের মুছে ফেলা ক্লাস হবে, যেমন:

struct wrapper_base
{
  virtual ~wrapper_base() { }
  virtual std::ostream & stream(std::ostream & o) = 0;
};

template <typename T, typename Delims>
struct wrapper : public wrapper_base
{
  wrapper(const T & t) : t(t) { }
  std::ostream & stream(std::ostream & o)
  {
    return o << pretty_print::print_container_helper<T, char, std::char_traits<char>, Delims>(t);
  }
private:
  const T & t;
};

template <typename Delims>
struct MyPrinter
{
  template <typename Container> MyPrinter(const Container & c) : base(new wrapper<Container, Delims>(c)) { }
  ~MyPrinter() { delete base; }
  wrapper_base * base;
};

template <typename Delims>
std::ostream & operator<<(std::ostream & o, const MyPrinter<Delims> & p) { return p.base->stream(o); }

আপনার কোড কাজ করবে না। কনটেইনার সি এর মতো কোনও কীওয়ার্ড নেই
the_drow

31
@ থ্রেডো: দেখে মনে হচ্ছে ওপি ইতিমধ্যে এটি জানে। তারা কেবল তারা কী সন্ধান করছে তা ইঙ্গিত দিচ্ছে।
মার্সেলো ক্যান্টোস

আসলে, আমি কেবল একটি "নৈতিক" সিউডো-কোড উদাহরণ দিয়েছি gave (আমি রিটার্নের ধরণটিও রেখেছিলাম, আমি লক্ষ করছি।) নিশ্চিতভাবেই, ডিলিমিটরকে পরিবর্তনযোগ্য করে তোলা কীভাবে সেরা তা আমি জানি না।
কেরেক এসবি

1
অপর একটি বিকল্প pretty_printহ'ল অপারেটরদের নামের জায়গাতে রাখবে এবং মুদ্রণের সময় ব্যবহারকারীর জন্য একটি মোড়ক সরবরাহ করা হবে। ব্যবহারকারীর দৃষ্টিকোণ থেকে: std::cout << pretty_print(v);(সম্ভবত অন্য কোনও নাম সহ)। তারপরে আপনি মোড়কের মতো একই নামস্থলে অপারেটরটি সরবরাহ করতে পারেন এবং এটি আপনার পছন্দসই প্রিন্টিংয়ে প্রসারিত করতে পারে। আপনি প্রতিটি কলের মধ্যে
বিভাজনকারীকে

1
আপনার "আপডেট" উত্তরগুলি হুংকারযুক্ত প্রশ্নের পরিবর্তে প্রকৃত উত্তরে তৈরি করুন।
einpoklum

উত্তর:


82

এই সমাধানটি কয়েকটি পরিবর্তন সহ মার্সেলোর সমাধান দ্বারা অনুপ্রাণিত হয়েছিল:

#include <iostream>
#include <iterator>
#include <type_traits>
#include <vector>
#include <algorithm>

// This works similar to ostream_iterator, but doesn't print a delimiter after the final item
template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar> >
class pretty_ostream_iterator : public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
    typedef TChar char_type;
    typedef TCharTraits traits_type;
    typedef std::basic_ostream<TChar, TCharTraits> ostream_type;

    pretty_ostream_iterator(ostream_type &stream, const char_type *delim = NULL)
        : _stream(&stream), _delim(delim), _insertDelim(false)
    {
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator=(const T &value)
    {
        if( _delim != NULL )
        {
            // Don't insert a delimiter if this is the first time the function is called
            if( _insertDelim )
                (*_stream) << _delim;
            else
                _insertDelim = true;
        }
        (*_stream) << value;
        return *this;
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator*()
    {
        return *this;
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator++()
    {
        return *this;
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator++(int)
    {
        return *this;
    }
private:
    ostream_type *_stream;
    const char_type *_delim;
    bool _insertDelim;
};

#if _MSC_VER >= 1400

// Declare pretty_ostream_iterator as checked
template<typename T, typename TChar, typename TCharTraits>
struct std::_Is_checked_helper<pretty_ostream_iterator<T, TChar, TCharTraits> > : public std::tr1::true_type
{
};

#endif // _MSC_VER >= 1400

namespace std
{
    // Pre-declarations of container types so we don't actually have to include the relevant headers if not needed, speeding up compilation time.
    // These aren't necessary if you do actually include the headers.
    template<typename T, typename TAllocator> class vector;
    template<typename T, typename TAllocator> class list;
    template<typename T, typename TTraits, typename TAllocator> class set;
    template<typename TKey, typename TValue, typename TTraits, typename TAllocator> class map;
}

// Basic is_container template; specialize to derive from std::true_type for all desired container types
template<typename T> struct is_container : public std::false_type { };

// Mark vector as a container
template<typename T, typename TAllocator> struct is_container<std::vector<T, TAllocator> > : public std::true_type { };

// Mark list as a container
template<typename T, typename TAllocator> struct is_container<std::list<T, TAllocator> > : public std::true_type { };

// Mark set as a container
template<typename T, typename TTraits, typename TAllocator> struct is_container<std::set<T, TTraits, TAllocator> > : public std::true_type { };

// Mark map as a container
template<typename TKey, typename TValue, typename TTraits, typename TAllocator> struct is_container<std::map<TKey, TValue, TTraits, TAllocator> > : public std::true_type { };

// Holds the delimiter values for a specific character type
template<typename TChar>
struct delimiters_values
{
    typedef TChar char_type;
    const TChar *prefix;
    const TChar *delimiter;
    const TChar *postfix;
};

// Defines the delimiter values for a specific container and character type
template<typename T, typename TChar>
struct delimiters
{
    static const delimiters_values<TChar> values; 
};

// Default delimiters
template<typename T> struct delimiters<T, char> { static const delimiters_values<char> values; };
template<typename T> const delimiters_values<char> delimiters<T, char>::values = { "{ ", ", ", " }" };
template<typename T> struct delimiters<T, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T> const delimiters_values<wchar_t> delimiters<T, wchar_t>::values = { L"{ ", L", ", L" }" };

// Delimiters for set
template<typename T, typename TTraits, typename TAllocator> struct delimiters<std::set<T, TTraits, TAllocator>, char> { static const delimiters_values<char> values; };
template<typename T, typename TTraits, typename TAllocator> const delimiters_values<char> delimiters<std::set<T, TTraits, TAllocator>, char>::values = { "[ ", ", ", " ]" };
template<typename T, typename TTraits, typename TAllocator> struct delimiters<std::set<T, TTraits, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T, typename TTraits, typename TAllocator> const delimiters_values<wchar_t> delimiters<std::set<T, TTraits, TAllocator>, wchar_t>::values = { L"[ ", L", ", L" ]" };

// Delimiters for pair
template<typename T1, typename T2> struct delimiters<std::pair<T1, T2>, char> { static const delimiters_values<char> values; };
template<typename T1, typename T2> const delimiters_values<char> delimiters<std::pair<T1, T2>, char>::values = { "(", ", ", ")" };
template<typename T1, typename T2> struct delimiters<std::pair<T1, T2>, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T1, typename T2> const delimiters_values<wchar_t> delimiters<std::pair<T1, T2>, wchar_t>::values = { L"(", L", ", L")" };

// Functor to print containers. You can use this directly if you want to specificy a non-default delimiters type.
template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar>, typename TDelimiters = delimiters<T, TChar> >
struct print_container_helper
{
    typedef TChar char_type;
    typedef TDelimiters delimiters_type;
    typedef std::basic_ostream<TChar, TCharTraits>& ostream_type;

    print_container_helper(const T &container)
        : _container(&container)
    {
    }

    void operator()(ostream_type &stream) const
    {
        if( delimiters_type::values.prefix != NULL )
            stream << delimiters_type::values.prefix;
        std::copy(_container->begin(), _container->end(), pretty_ostream_iterator<typename T::value_type, TChar, TCharTraits>(stream, delimiters_type::values.delimiter));
        if( delimiters_type::values.postfix != NULL )
            stream << delimiters_type::values.postfix;
    }
private:
    const T *_container;
};

// Prints a print_container_helper to the specified stream.
template<typename T, typename TChar, typename TCharTraits, typename TDelimiters>
std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const print_container_helper<T, TChar, TDelimiters> &helper)
{
    helper(stream);
    return stream;
}

// Prints a container to the stream using default delimiters
template<typename T, typename TChar, typename TCharTraits>
typename std::enable_if<is_container<T>::value, std::basic_ostream<TChar, TCharTraits>&>::type
    operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const T &container)
{
    stream << print_container_helper<T, TChar, TCharTraits>(container);
    return stream;
}

// Prints a pair to the stream using delimiters from delimiters<std::pair<T1, T2>>.
template<typename T1, typename T2, typename TChar, typename TCharTraits>
std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const std::pair<T1, T2> &value)
{
    if( delimiters<std::pair<T1, T2>, TChar>::values.prefix != NULL )
        stream << delimiters<std::pair<T1, T2>, TChar>::values.prefix;

    stream << value.first;

    if( delimiters<std::pair<T1, T2>, TChar>::values.delimiter != NULL )
        stream << delimiters<std::pair<T1, T2>, TChar>::values.delimiter;

    stream << value.second;

    if( delimiters<std::pair<T1, T2>, TChar>::values.postfix != NULL )
        stream << delimiters<std::pair<T1, T2>, TChar>::values.postfix;
    return stream;    
}

// Used by the sample below to generate some values
struct fibonacci
{
    fibonacci() : f1(0), f2(1) { }
    int operator()()
    {
        int r = f1 + f2;
        f1 = f2;
        f2 = r;
        return f1;
    }
private:
    int f1;
    int f2;
};

int main()
{
    std::vector<int> v;
    std::generate_n(std::back_inserter(v), 10, fibonacci());

    std::cout << v << std::endl;

    // Example of using pretty_ostream_iterator directly
    std::generate_n(pretty_ostream_iterator<int>(std::cout, ";"), 20, fibonacci());
    std::cout << std::endl;
}

মার্সেলোর সংস্করণটির মতো এটিতেও একটি কনটেনার ধরণের বৈশিষ্ট্য ব্যবহার করা হয়েছে যা অবশ্যই সমর্থিত হওয়া সমস্ত ধারকগুলির জন্য বিশেষীকরণ করা উচিত। এটা তোলে জন্য চেক করতে একটি বৈশিষ্ট্যের ব্যবহার করা সম্ভব হতে পারে value_type, const_iterator, begin()/ end(), কিন্তু আমি নিশ্চিত আমি যে কথা বলতে চাই করছি যেহেতু এটি যে ঐ মানদন্ড অনুসারে কিন্তু আসলে পাত্রে নয়, মত মেলে পারে std::basic_string। মার্সেলোর সংস্করণটির মতো এটিও টেমপ্লেট ব্যবহার করে যা ব্যবহার করার জন্য ডিলিমিটর নির্দিষ্ট করতে বিশেষীকরণ করা যায়।

প্রধান পার্থক্য হ'ল আমি আমার সংস্করণটি এ এর ​​চারপাশে তৈরি করেছি pretty_ostream_iteratorযা একইরকম কাজ করে std::ostream_iteratorতবে শেষ আইটেমটির পরে কোনও ডিলিমিটার মুদ্রণ করে না। কনটেইনারগুলি print_container_helperফর্ম্যাট করা কাজটিই সম্পন্ন হয় , যা is_container বৈশিষ্ট্য ছাড়াই পাত্রে মুদ্রণ করতে সরাসরি ব্যবহার করা যেতে পারে, বা একটি ভিন্ন ভিন্ন সীমানার প্রকার নির্দিষ্ট করতে।

আমি is_container এবং ডিলিমিটারগুলিও সংজ্ঞায়িত করেছি তাই এটি অ-মানক পূর্বাভাস বা বরাদ্দকারীদের এবং এবং চর এবং wchar_t উভয়ের জন্যই কাজ করবে। অপারেটর << ফাংশন নিজেই চর এবং wchar_t উভয় স্ট্রিমের সাথে কাজ করতে সংজ্ঞায়িত।

অবশেষে, আমি ব্যবহার করেছি std::enable_if, যা সি ++ 0x এর অংশ হিসাবে উপলব্ধ এবং ভিজ্যুয়াল সি ++ 2010 এবং জি ++ 4.3 ((-std = c ++ 0x পতাকা প্রয়োজন) এবং এর পরে কাজ করে। এইভাবে বুস্টের উপর কোনও নির্ভরতা নেই।


যদি আমি এই অধিকারটি পড়ছি, একটি <i, j>ফাংশনের মতো [i j]এবং অন্যটির মতো একটি জুটি মুদ্রণের জন্য , আপনাকে এই ধরণেরটি পাস করতে কয়েক মুখ্য স্থির সদস্যের সাথে একটি সম্পূর্ণ নতুন প্রকারের সংজ্ঞা দিতে হবে print_container_helper? এটি অত্যধিক জটিল বলে মনে হচ্ছে। আপনি কেন ক্ষেত্রের ভিত্তিতে কেস ভিত্তিতে কেস সেট করতে পারেন এমন ক্ষেত্রগুলি এবং বিশেষায়িতকরণগুলি কেবল ভিন্ন ডিফল্ট মান সরবরাহ করে কেন একটি আসল অবজেক্টের সাথে যান না?
ডেনিস জিকিফুজ

এটি এইভাবে দেখুন: আপনি ব্যক্তিগতভাবে পছন্দ করেন এমন কিছু ডিলিমিটর যদি থাকে তবে আপনি স্থির সদস্যদের সাথে একসাথে বেশ কয়েকটি ক্লাস তৈরি করতে পারেন এবং তারপরে কেবল সেগুলি ব্যবহার করতে পারেন। অবশ্যই আপনি ঠিক বলেছেন যে print_container_helperব্যবহারটি ঠিক তেমন মার্জিত নয় operator<<। আপনি অবশ্যই উত্সটি সর্বদা পরিবর্তন করতে পারেন বা কেবল আপনার পছন্দসই ধারকটির জন্য সুস্পষ্ট বিশেষত্ব যুক্ত করতে পারেন, যেমন pair<int, int>এবং এর জন্য pair<double, string>। শেষ পর্যন্ত এটি সুবিধার বিরুদ্ধে শক্তি ওজন করার বিষয়। উন্নতির জন্য পরামর্শ স্বাগত!
কেরেক এসবি

... এবং এটি অনুসরণ করতে যদি আপনার যদি ইতিমধ্যে একই উপাত্তের বিভিন্ন ধরণের বিন্যাসে প্রাসঙ্গিক প্রিন্টিংয়ের প্রয়োজন হয় তবে আপনাকে সম্ভবত কমপক্ষে একটি ছোট মোড়ক লিখতে হবে। এটি একটি উচ্চ-কনফিগারযোগ্য ফর্ম্যাটিং লাইব্রেরি নয়, বরং শূন্য-প্রয়াসের বোধগম্য-ডিফল্ট লাইব্রেরি যা আপনাকে যাদুকরীভাবে চিন্তা না করে পাত্রে মুদ্রণ করতে দেয় ... (তবে আপনি যদি আরও বিশ্বব্যাপী নমনীয়তা চান তবে আমরা সম্ভবত কিছু # ম্যাক্রোগুলি যুক্ত করতে পারি ডিফল্টগুলি
সামাল দেওয়া

আসল সমস্যাটি হ'ল, যদিও আমি কাস্টম ডিলিমিটরগুলির জন্য প্যারামিটারগুলি ব্যবহার করতে সহজেই মুদ্রণ_কন্টেইনার_হেল্পারকে সংশোধন করতে পারি, তবুও ডিলিমিটারগুলির টেম্পলেট বিশেষায়িত করা ছাড়া অভ্যন্তরীণ পাত্রে (বা জোড়) ডিলিমিটার নির্দিষ্ট করার কোনও উপায় নেই। এটি অর্জন খুব জটিল হবে।
সোভেন

আমি প্রায় টাইপ ইরেজ ব্যবহার করে একটি সুবিধাজনক কাস্টম ডিলিমিটার সমাধান অর্জনের জন্য পরিচালনা করছি। আপনার যদি ইতিমধ্যে একটি সীমানার শ্রেণি থাকে MyDelsতবে আমি বলতে পারি std::cout << CustomPrinter<MyDels>(x);। এই মুহুর্তে আমি যা করতে পারি না তা বলা হয় std::cout << CustomDelims<"{", ":", "}">(x);, কারণ আপনার const char *টেমপ্লেট আর্গুমেন্ট থাকতে পারে না । ডিলিমিটরদের সংকলন-সময়কে ধ্রুবক করার সিদ্ধান্তটি সেখানে ব্যবহারের স্বাচ্ছন্দ্যে কিছুটা বিধিনিষেধ তৈরি করে, তবে আমি মনে করি এটির পক্ষে এটি যথার্থ।
কেরেক এসবি

22

এটি কয়েকবার সম্পাদনা করা হয়েছে, এবং আমরা সিদ্ধান্ত নিয়েছি যে মূল শ্রেণিটি একটি সংগ্রহ রেঞ্জপ্রিন্টারে আবৃত করে

আপনি একবারে ওয়ান-টাইম অপারেটর << ওভারলোড লেখার পরে কোনও সংগ্রহের সাথে এটি স্বয়ংক্রিয়ভাবে কাজ করা উচিত, আপনি জোড়টি মুদ্রণের জন্য মানচিত্রের জন্য একটি বিশেষের প্রয়োজন হবে এবং ডিলিমিটারটি সেখানে কাস্টমাইজ করতে চাইতে পারেন।

আপনার সরাসরি আইটেমটি সরাসরি আউটপুট না করে আইটেমটি ব্যবহার করার জন্য একটি বিশেষ "মুদ্রণ" ফাংশনও থাকতে পারে। এসটিএল অ্যালগরিদমের মতো কিছুটা আপনাকে কাস্টম পূর্বাভাসেও যেতে দেয়। মানচিত্রের সাহায্যে আপনি স্ট্যান্ড :: জোড়ের জন্য একটি কাস্টম প্রিন্টার ব্যবহার করবেন this

আপনার "ডিফল্ট" প্রিন্টারটি কেবল এটিকে স্ট্রিমে আউটপুট দেবে।

ঠিক আছে, আসুন একটি কাস্টম প্রিন্টারে কাজ করি। আমি আমার বাইরের ক্লাসটি রেঞ্জপ্রিন্টারে পরিবর্তন করব। সুতরাং আমাদের কাছে 2 টি আইট্রেটর এবং কিছু ডিলিমিটার রয়েছে তবে প্রকৃত আইটেমগুলি কীভাবে প্রিন্ট করা যায় তা কাস্টমাইজ করা হয়নি।

struct DefaultPrinter
{
   template< typename T >
   std::ostream & operator()( std::ostream& os, const T& t ) const
   {
     return os << t;
   }

   // overload for std::pair
   template< typename K, typename V >
   std::ostream & operator()( std::ostream & os, std::pair<K,V> const& p)
   {
      return os << p.first << '=' << p.second;
   }
};

// some prototypes
template< typename FwdIter, typename Printer > class RangePrinter;

template< typename FwdIter, typename Printer > 
  std::ostream & operator<<( std::ostream &, 
        RangePrinter<FwdIter, Printer> const& );

template< typename FwdIter, typename Printer=DefaultPrinter >
class RangePrinter
{
    FwdIter begin;
    FwdIter end;
    std::string delim;
    std::string open;
    std::string close;
    Printer printer;

    friend std::ostream& operator<< <>( std::ostream&, 
         RangePrinter<FwdIter,Printer> const& );

public:
    RangePrinter( FwdIter b, FwdIter e, Printer p,
         std::string const& d, std::string const & o, std::string const& c )
      : begin( b ), end( e ), printer( p ), open( o ), close( c )
    {
    } 

     // with no "printer" variable
    RangePrinter( FwdIter b, FwdIter e,
         std::string const& d, std::string const & o, std::string const& c )
      : begin( b ), end( e ), open( o ), close( c )
    {
    } 

};


template<typename FwdIter, typename Printer>
std::ostream& operator<<( std::ostream& os, 
          RangePrinter<FwdIter, Printer> const& range )
{
    const Printer & printer = range.printer;

    os << range.open;
    FwdIter begin = range.begin, end = range.end;

    // print the first item
    if (begin == end) 
    { 
      return os << range.close; 
    }

    printer( os, *begin );

    // print the rest with delim as a prefix
    for( ++begin; begin != end; ++begin )
    {
       os << range.delim;
       printer( os, *begin );
    }
    return os << range.close;
}

এখন ডিফল্টরূপে এটি মানচিত্রের জন্য ততক্ষণ কাজ করবে যতক্ষণ না কী এবং মান ধরণের উভয়ই মুদ্রণযোগ্য হয় এবং যখন না হয় তখন আপনি নিজের বিশেষ আইটেম প্রিন্টারে রাখতে পারেন (অন্য কোনও ধরণের সাথে আপনি যেমন করতে পারেন), বা না চাইলে ডিলিমিটার হিসাবে

এগুলি এখনই তৈরি করতে ফ্রি-ফাংশনটি সরিয়ে নিচ্ছি:

একটি ফ্রি-ফাংশন (পুনরুক্তি সংস্করণ) এর মতো দেখতে কিছুটা হবে এবং আপনার ডিফল্টও থাকতে পারে:

template<typename Collection>
RangePrinter<typename Collection::const_iterator> rangePrinter
    ( const Collection& coll, const char * delim=",", 
       const char * open="[", const char * close="]")
{
   return RangePrinter< typename Collection::const_iterator >
     ( coll.begin(), coll.end(), delim, open, close );
}

তারপরে আপনি এটি স্টাডি :: জন্য সেট করে ব্যবহার করতে পারেন

 std::cout << outputFormatter( mySet );

আপনি ফ্রি-ফাংশন সংস্করণও লিখতে পারেন যা একটি কাস্টম প্রিন্টারে নেয় এবং যা দুটি পুনরাবৃত্তকারী লাগে। যে কোনও ক্ষেত্রে তারা আপনার জন্য টেম্পলেট প্যারামিটারগুলি সমাধান করবে এবং আপনি এগুলি অস্থায়ী হিসাবে পাস করতে সক্ষম হবেন।


আমি দেখি. এটি মার্সেলো ক্যান্টোসের ধারণার অনুরূপ, তাই না? আমি এটিকে একটি কার্যকর উদাহরণে পরিণত করার চেষ্টা করব, ধন্যবাদ!
কেরেক এসবি

আমি এই সমাধানটি মার্সেলোর চেয়ে অনেক ক্লিনার পেয়েছি এবং এটি একই নমনীয়তার প্রস্তাব দেয়। আমি যে দিকটি স্পষ্টভাবে আউটপুটটিকে একটি ফাংশন কলে আবৃত করতে চাই তা পছন্দ করি। সত্যিই শীতল হতে, আপনি সরাসরি পুনরাবৃত্তির একটি পরিসর আউটপুট দেওয়ার জন্য সমর্থন যোগ করতে পারেন, যাতে আমি করতে পারি std::cout << outputFormatter(beginOfRange, endOfRange);
বিজার্ন পোলেক্স

1
@ ক্যাশকো: এই সমাধানটিতে একটি সমস্যা রয়েছে, এটি পুনরাবৃত্ত সংগ্রহগুলি (অর্থাত্ সংগ্রহের সংগ্রহ) নিয়ে কাজ করে বলে মনে হয় না। std::pair"অভ্যন্তরীণ সংগ্রহ" এর সর্বাধিক প্রাথমিক উদাহরণ।
ম্যাথিউ মিঃ

আমি এই উত্তরটি খুব পছন্দ করছি, যেহেতু এর কোনও নির্ভরতা নেই এবং এটি যে পাত্রে সমর্থন করে তা সম্পর্কে জানতে হবে না। এটি std::mapসহজেই পরিচালনা করতে পারে কিনা তা আমরা কী খুঁজে বের করতে পারি এবং এটি সংগ্রহের সংগ্রহের জন্য যদি কাজ করে? যদিও আমি এটিকে উত্তর হিসাবে গ্রহণ করতে প্রলোভিত হই। আমি আশা করি মার্সেলো কিছু মনে করবেন না, তার সমাধানও কাজ করে।
কেরেক এসবি

@ ম্যাথিউ এম। এটি নির্ভর করে আপনি কীভাবে অভ্যন্তরীণ সংগ্রহটি মুদ্রণ করবেন। যদি আপনি কেবল ওএস << ওপেন << * ইটার << বন্ধ ব্যবহার করেন তবে এতে আপনার সমস্যা হবে তবে আমি আপনাকে পরামর্শ হিসাবে আপনার ব্যবহারকারীকে একটি কাস্টম প্রিন্টারে পাস করার অনুমতি দিলে আপনি নিজের পছন্দ মতো কিছু মুদ্রণ করতে পারেন।
ক্যাশকু

14

এখানে একটি ওয়ার্কিং লাইব্রেরি রয়েছে, একটি সম্পূর্ণ কার্যকারী প্রোগ্রাম হিসাবে উপস্থাপিত, যা আমি স্রেফ একসাথে হ্যাক করেছি:

#include <set>
#include <vector>
#include <iostream>

#include <boost/utility/enable_if.hpp>

// Default delimiters
template <class C> struct Delims { static const char *delim[3]; };
template <class C> const char *Delims<C>::delim[3]={"[", ", ", "]"};
// Special delimiters for sets.                                                                                                             
template <typename T> struct Delims< std::set<T> > { static const char *delim[3]; };
template <typename T> const char *Delims< std::set<T> >::delim[3]={"{", ", ", "}"};

template <class C> struct IsContainer { enum { value = false }; };
template <typename T> struct IsContainer< std::vector<T> > { enum { value = true }; };
template <typename T> struct IsContainer< std::set<T>    > { enum { value = true }; };

template <class C>
typename boost::enable_if<IsContainer<C>, std::ostream&>::type
operator<<(std::ostream & o, const C & x)
{
  o << Delims<C>::delim[0];
  for (typename C::const_iterator i = x.begin(); i != x.end(); ++i)
    {
      if (i != x.begin()) o << Delims<C>::delim[1];
      o << *i;
    }
  o << Delims<C>::delim[2];
  return o;
}

template <typename T> struct IsChar { enum { value = false }; };
template <> struct IsChar<char> { enum { value = true }; };

template <typename T, int N>
typename boost::disable_if<IsChar<T>, std::ostream&>::type
operator<<(std::ostream & o, const T (&x)[N])
{
  o << "[";
  for (int i = 0; i != N; ++i)
    {
      if (i) o << ",";
      o << x[i];
    }
  o << "]";
  return o;
}

int main()
{
  std::vector<int> i;
  i.push_back(23);
  i.push_back(34);

  std::set<std::string> j;
  j.insert("hello");
  j.insert("world");

  double k[] = { 1.1, 2.2, M_PI, -1.0/123.0 };

  std::cout << i << "\n" << j << "\n" << k << "\n";
}

এটি বর্তমানে কেবলমাত্র vectorএবং এর setসাথে কাজ করে তবে বেশিরভাগ ধারকগুলির সাথে কাজ করতে তৈরি করা যেতে পারে কেবল IsContainerবিশেষায়িতকরণগুলিতে প্রসারিত করে । এই কোডটি ন্যূনতম কিনা তা নিয়ে আমি খুব বেশি ভাবিনি, তবে আমি অবিলম্বে কিছু অপ্রয়োজনীয় হিসাবে ফেলে দিতে পারি তা ভাবতে পারি না।

সম্পাদনা: কেবল কিক্সের জন্য, আমি এমন একটি সংস্করণ অন্তর্ভুক্ত করেছি যা অ্যারে পরিচালনা করে। আরও অস্পষ্টতা এড়াতে আমাকে চর অ্যারে বাদ দিতে হয়েছিল; এটি এখনও সমস্যা হতে পারে wchar_t[]


2
@ নাওয়াজ: যেমনটি আমি বলেছিলাম, এটি একটি সমাধানের শুরু মাত্র। আপনি std::map<>অপারেটর বিশেষীকরণ দ্বারা, বা একটি operator<<জন্য সংজ্ঞায়িত দ্বারা সমর্থন করতে পারে std::pair<>
মার্সেলো ক্যান্টোস

তবে Delimsক্লাস টেম্পলেট ব্যবহারের জন্য +1 !
নওয়াজ

@ এমসি: ওহ ভাল। এটি খুব আশাব্যঞ্জক দেখাচ্ছে! (যাইহোক, আপনার "std :: ostream &" টাইপের রিটার্ন দরকার, আমি প্রথম দিকে তা ভুলে গিয়েছিলাম))
কেরেক এসবি

হুম, এসটিডি :: ভেক্টর <int> এবং স্টাড :: সেট <
স্ট্যান্ড

হ্যাঁ, আমি বর্তমানে অস্পষ্টতাগুলি কীভাবে রোধ করতে পারি তা operator<<টেম্পলেটটি যে কোনও কিছুর সাথে মেলে এই কারণে ঘটে ।
মার্সেলো ক্যান্টোস

10

আপনি contain fmt} লাইব্রেরি ব্যবহার করে ধারকগুলির পাশাপাশি রেঞ্জ এবং টিপলগুলি ফর্ম্যাট করতে পারেন । উদাহরণ স্বরূপ:

#include <vector>
#include <fmt/ranges.h>

int main() {
  auto v = std::vector<int>{1, 2, 3};
  fmt::print("{}", v);
}

কপি করে প্রিন্ট

{1, 2, 3}

to stdout

দাবি অস্বীকার: আমি {fmt of এর লেখক}


8

কোডটি এখন বেশ কয়েকটি অনুষ্ঠানে কার্যকর বলে প্রমাণিত হয়েছে এবং ব্যবহারটি যথেষ্ট কম হওয়ায় কাস্টমাইজেশনে যাওয়ার ব্যয় অনুভব করছি। সুতরাং, আমি এটি এমআইটি লাইসেন্সের অধীনে ছেড়ে দেওয়ার এবং একটি গিটহাব সংগ্রহস্থল সরবরাহ করার সিদ্ধান্ত নিয়েছি যেখানে শিরোনাম এবং একটি ছোট উদাহরণ ফাইল ডাউনলোড করা যেতে পারে।

http://djmuw.github.io/prettycc

0. ভূমিকা এবং শব্দ

এই উত্তরের পরিপ্রেক্ষিতে একটি 'সজ্জা' হ'ল উপসর্গ-স্ট্রিং, ডিলিমিটার-স্ট্রিং এবং একটি পোস্টফিক্স-স্ট্রিংয়ের সেট। যেখানে প্রিফিক্স স্ট্রিংটি আগে একটি স্ট্রিমের মধ্যে সন্নিবেশ করা হয় এবং একটি ধারকটির মানগুলির পরে পোস্টফিক্স স্ট্রিং (দেখুন 2. লক্ষ্য পাত্রে দেখুন)। ডিলিমিটার স্ট্রিংটি সংশ্লিষ্ট ধারকটির মানগুলির মধ্যে সন্নিবেশ করা হয়।

দ্রষ্টব্য: প্রকৃতপক্ষে, এই উত্তরটি প্রশ্নটিকে 100% তে সম্বোধন করে না যেহেতু সজ্জাটি কঠোরভাবে সময় নিয়মিত সংকলিত হয় না কারণ বর্তমান প্রবাহে একটি কাস্টম সজ্জা প্রয়োগ করা হয়েছে কিনা তা যাচাই করার জন্য রানটাইম চেক করা দরকার। তবুও, আমি মনে করি এটির কিছু শালীন বৈশিষ্ট্য রয়েছে।

দ্রষ্টব্য 2: এটি এখনও ভাল পরীক্ষা না করায় ছোট ছোট বাগ থাকতে পারে।

সাধারণ ধারণা / ব্যবহার

জিরো অতিরিক্ত কোড ব্যবহারের জন্য প্রয়োজনীয়

এটি হিসাবে হিসাবে সহজ রাখা হয়

#include <vector>
#include "pretty.h"

int main()
{
  std::cout << std::vector<int>{1,2,3,4,5}; // prints 1, 2, 3, 4, 5
  return 0;
}

সহজ কাস্টমাইজেশন ...

... নির্দিষ্ট স্ট্রিম অবজেক্টের প্রতি শ্রদ্ধা সহ

#include <vector>
#include "pretty.h"

int main()
{
  // set decoration for std::vector<int> for cout object
  std::cout << pretty::decoration<std::vector<int>>("(", ",", ")");
  std::cout << std::vector<int>{1,2,3,4,5}; // prints (1,2,3,4,5)
  return 0;
}

বা সমস্ত স্ট্রিমের প্রতি শ্রদ্ধা সহ:

#include <vector>
#include "pretty.h"

// set decoration for std::vector<int> for all ostream objects
PRETTY_DEFAULT_DECORATION(std::vector<int>, "{", ", ", "}")

int main()
{
  std::cout << std::vector<int>{1,2,3,4,5}; // prints {1, 2, 3, 4, 5}
  std::cout << pretty::decoration<std::vector<int>>("(", ",", ")");
  std::cout << std::vector<int>{1,2,3,4,5}; // prints (1,2,3,4,5)
  return 0;
}

রুক্ষ বর্ণনা

  • কোডটিতে কোনও শ্রেণীর টেম্পলেট রয়েছে যা কোনও ধরণের জন্য একটি ডিফল্ট সজ্জা সরবরাহ করে
  • যা (ক) নির্দিষ্ট ধরণের (গুলি) এর জন্য ডিফল্ট সজ্জা পরিবর্তন করতে বিশেষীকরণ করা যেতে পারে এবং এটি
  • দ্বারা উপলব্ধ ব্যক্তিগত সংগ্রহস্থল ব্যবহার করার ios_baseব্যবহার xalloc/ pwordঅর্ডার করার জন্য একটি পয়েন্টার সংরক্ষণ করার জন্য pretty::decorবস্তু বিশেষভাবে একটি নির্দিষ্ট প্রবাহে একটি নির্দিষ্ট ধরনের শোভাকর।

যদি pretty::decor<T>এই স্ট্রিমের জন্য কোনও অবজেক্ট স্পষ্টভাবে সেট আপ না করা pretty::defaulted<T, charT, chartraitT>::decoration()থাকে তবে প্রদত্ত ধরণের জন্য ডিফল্ট সজ্জাটি পেতে বলা হয়। ক্লাসটি pretty::defaultedডিফল্ট সজ্জা কাস্টমাইজ করার জন্য বিশেষীকরণ করতে হবে।

2. লক্ষ্য বস্তু / পাত্রে

উদ্দিষ্ট বস্তু objজন্য 'চমত্কার প্রসাধন' এই কোড বস্তু পারেন ভুগেন

  • অতিরিক্ত লোড std::beginএবংstd::end সংজ্ঞায়িত (সি স্টাইল অ্যারে অন্তর্ভুক্ত),
  • ADL এর মাধ্যমে থাকা begin(obj)এবং end(obj)উপলভ্য,
  • টাইপ হয় std::tuple
  • বা টাইপ std::pair

কোডটিতে পরিসীমা বৈশিষ্ট্য ( begin/ end) সহ শ্রেণি সনাক্তকরণের জন্য একটি বৈশিষ্ট্য অন্তর্ভুক্ত রয়েছে । (কোনও begin(obj) == end(obj)বৈধ এক্সপ্রেশন কিনা তা কোনও চেক অন্তর্ভুক্ত নেই ))

কোডটি operator<<বিশ্বব্যাপী নেমস্পেসে সরবরাহ করে যা কেবলমাত্র ক্লাসে প্রযোজ্য যা আরও বেশি বিশেষায়িত সংস্করণ operator<<উপলব্ধ নেই। সুতরাং, উদাহরণস্বরূপ std::stringবৈধ begin/ endজোড়া থাকা সত্ত্বেও এই কোডটিতে অপারেটরটি ব্যবহার করে মুদ্রণ করা হয় না ।

3. ব্যবহার এবং অনুকূলিতকরণ

সজ্জা প্রতিটি ধরণের (আলাদা আলাদা ব্যতীত tuple) এবং স্ট্রিমের জন্য আলাদাভাবে চাপানো যেতে পারে (স্ট্রিমের ধরণের নয়!)। (মানে কstd::vector<int> বিভিন্ন স্ট্রিমের জন্য আলাদা আলাদা সাজসজ্জা থাকতে পারে))

ক) ডিফল্ট সজ্জা

ডিফল্ট উপসর্গটি ""ডিফল্ট পোস্টফিক্সের মতো (কিছুই নয়), যখন ডিফল্ট ডিলিমিটারটি ", "(কমা + স্পেস) হয়।

খ) pretty::defaultedক্লাস টেম্পলেট বিশেষায়িত করে কোনও প্রকারের কাস্টমাইজড ডিফল্ট সজ্জা

struct defaultedএকটি স্ট্যাটিক সদস্য ফাংশন আছে decoration()একটি ফিরতি decorবস্তুর দেওয়া টাইপ জন্য ডিফল্ট মান অন্তর্ভুক্ত করা হয়েছে।

অ্যারে ব্যবহার করে উদাহরণ:

ডিফল্ট অ্যারে মুদ্রণ কাস্টমাইজ করুন:

namespace pretty
{
  template<class T, std::size_t N>
  struct defaulted<T[N]>
  {
    static decor<T[N]> decoration()
    {
      return{ { "(" }, { ":" }, { ")" } };
    }
  };
}

একটি অ্যারের অ্যারে মুদ্রণ করুন:

float e[5] = { 3.4f, 4.3f, 5.2f, 1.1f, 22.2f };
std::cout << e << '\n'; // prints (3.4:4.3:5.2:1.1:22.2)

ব্যবহার PRETTY_DEFAULT_DECORATION(TYPE, PREFIX, DELIM, POSTFIX, ...)জন্য ম্যাক্রো charস্ট্রিম

ম্যাক্রো প্রসারিত হয়

namespace pretty { 
  template< __VA_ARGS__ >
  struct defaulted< TYPE > {
    static decor< TYPE > decoration() {
      return { PREFIX, DELIM, POSTFIX };
    } 
  }; 
} 

উপরের আংশিক বিশেষজ্ঞকে পুনরায় লেখার জন্য সক্ষম করা

PRETTY_DEFAULT_DECORATION(T[N], "", ";", "", class T, std::size_t N)

বা একটি সম্পূর্ণ বিশেষায়নের মত সন্নিবেশ করা

PRETTY_DEFAULT_DECORATION(std::vector<int>, "(", ", ", ")")

আরেকটি ম্যাক্রো wchar_tস্ট্রিম অন্তর্ভুক্ত করা হয়: PRETTY_DEFAULT_WDECORATION

গ) স্রোতে সজ্জা চাপুন

ফাংশনটি pretty::decorationএকটি নির্দিষ্ট স্রোতে একটি সজ্জা চাপানোর জন্য ব্যবহৃত হয়। ওভারলোডগুলি নেওয়া হয় - একটি স্ট্রিং আর্গুমেন্ট হ'ল ডিলিমিটার (ডিফল্ট শ্রেণীর উপসর্গ এবং পোস্টফিক্স গ্রহণ করা) - বা তিনটি স্ট্রিং আর্গুমেন্ট সম্পূর্ণ সজ্জায় একত্রিত করে

প্রদত্ত প্রকার এবং স্ট্রিমের জন্য সম্পূর্ণ সজ্জা

float e[3] = { 3.4f, 4.3f, 5.2f };
std::stringstream u;
// add { ; } decoration to u
u << pretty::decoration<float[3]>("{", "; ", "}");

// use { ; } decoration
u << e << '\n'; // prints {3.4; 4.3; 5.2}

// uses decoration returned by defaulted<float[3]>::decoration()
std::cout << e; // prints 3.4, 4.3, 5.2

প্রদত্ত স্ট্রিমের জন্য ডিলিমিটারের কাস্টমাইজেশন

PRETTY_DEFAULT_DECORATION(float[3], "{{{", ",", "}}}")

std::stringstream v;
v << e; // prints {{{3.4,4.3,5.2}}}

v << pretty::decoration<float[3]>(":");
v << e; // prints {{{3.4:4.3:5.2}}}

v << pretty::decoration<float[3]>("((", "=", "))");
v << e; // prints ((3.4=4.3=5.2))

4. বিশেষ পরিচালনা std::tuple

প্রতিটি সম্ভাব্য টিউপল ধরণের জন্য কোনও বিশেষায়নের অনুমতি দেওয়ার পরিবর্তে, এই কোডটি std::tuple<void*>সমস্ত ধরণের জন্য উপলব্ধ যে কোনও প্রসাধন প্রয়োগ করে std::tuple<...>

স্ট্রিম থেকে কাস্টম সজ্জা সরান

কোনও নির্দিষ্ট ধরণের pretty::clearস্রোতে ডিফল্ট সজ্জাতে ফিরে যেতে ফাংশন টেম্পলেটটি ব্যবহার করুন s

s << pretty::clear<std::vector<int>>();

5. আরও উদাহরণ

নিউলাইন ডিলিমিটার সহ "ম্যাট্রিক্সের মতো" মুদ্রণ করা হচ্ছে

std::vector<std::vector<int>> m{ {1,2,3}, {4,5,6}, {7,8,9} };
std::cout << pretty::decoration<std::vector<std::vector<int>>>("\n");
std::cout << m;

ছাপে

1, 2, 3
4, 5, 6
7, 8, 9

এটি আদর্শ / কে কেইউজেড-এ দেখুন

6. কোড

#ifndef pretty_print_0x57547_sa4884X_0_1_h_guard_
#define pretty_print_0x57547_sa4884X_0_1_h_guard_

#include <string>
#include <iostream>
#include <type_traits>
#include <iterator>
#include <utility>

#define PRETTY_DEFAULT_DECORATION(TYPE, PREFIX, DELIM, POSTFIX, ...) \
    namespace pretty { template< __VA_ARGS__ >\
    struct defaulted< TYPE > {\
    static decor< TYPE > decoration(){\
      return { PREFIX, DELIM, POSTFIX };\
    } /*decoration*/ }; /*defaulted*/} /*pretty*/

#define PRETTY_DEFAULT_WDECORATION(TYPE, PREFIX, DELIM, POSTFIX, ...) \
    namespace pretty { template< __VA_ARGS__ >\
    struct defaulted< TYPE, wchar_t, std::char_traits<wchar_t> > {\
    static decor< TYPE, wchar_t, std::char_traits<wchar_t> > decoration(){\
      return { PREFIX, DELIM, POSTFIX };\
    } /*decoration*/ }; /*defaulted*/} /*pretty*/

namespace pretty
{

  namespace detail
  {
    // drag in begin and end overloads
    using std::begin;
    using std::end;
    // helper template
    template <int I> using _ol = std::integral_constant<int, I>*;
    // SFINAE check whether T is a range with begin/end
    template<class T>
    class is_range
    {
      // helper function declarations using expression sfinae
      template <class U, _ol<0> = nullptr>
      static std::false_type b(...);
      template <class U, _ol<1> = nullptr>
      static auto b(U &v) -> decltype(begin(v), std::true_type());
      template <class U, _ol<0> = nullptr>
      static std::false_type e(...);
      template <class U, _ol<1> = nullptr>
      static auto e(U &v) -> decltype(end(v), std::true_type());
      // return types
      using b_return = decltype(b<T>(std::declval<T&>()));
      using e_return = decltype(e<T>(std::declval<T&>()));
    public:
      static const bool value = b_return::value && e_return::value;
    };
  }

  // holder class for data
  template<class T, class CharT = char, class TraitT = std::char_traits<CharT>>
  struct decor
  {
    static const int xindex;
    std::basic_string<CharT, TraitT> prefix, delimiter, postfix;
    decor(std::basic_string<CharT, TraitT> const & pre = "",
      std::basic_string<CharT, TraitT> const & delim = "",
      std::basic_string<CharT, TraitT> const & post = "")
      : prefix(pre), delimiter(delim), postfix(post) {}
  };

  template<class T, class charT, class traits>
  int const decor<T, charT, traits>::xindex = std::ios_base::xalloc();

  namespace detail
  {

    template<class T, class CharT, class TraitT>
    void manage_decor(std::ios_base::event evt, std::ios_base &s, int const idx)
    {
      using deco_type = decor<T, CharT, TraitT>;
      if (evt == std::ios_base::erase_event)
      { // erase deco
        void const * const p = s.pword(idx);
        if (p)
        {
          delete static_cast<deco_type const * const>(p);
          s.pword(idx) = nullptr;
        }
      }
      else if (evt == std::ios_base::copyfmt_event)
      { // copy deco
        void const * const p = s.pword(idx);
        if (p)
        {
          auto np = new deco_type{ *static_cast<deco_type const * const>(p) };
          s.pword(idx) = static_cast<void*>(np);
        }
      }
    }

    template<class T> struct clearer {};

    template<class T, class CharT, class TraitT>
    std::basic_ostream<CharT, TraitT>& operator<< (
      std::basic_ostream<CharT, TraitT> &s, clearer<T> const &)
    {
      using deco_type = decor<T, CharT, TraitT>;
      void const * const p = s.pword(deco_type::xindex);
      if (p)
      { // delete if set
        delete static_cast<deco_type const *>(p);
        s.pword(deco_type::xindex) = nullptr;
      }
      return s;
    }

    template <class CharT> 
    struct default_data { static const CharT * decor[3]; };
    template <> 
    const char * default_data<char>::decor[3] = { "", ", ", "" };
    template <> 
    const wchar_t * default_data<wchar_t>::decor[3] = { L"", L", ", L"" };

  }

  // Clear decoration for T
  template<class T>
  detail::clearer<T> clear() { return{}; }
  template<class T, class CharT, class TraitT>
  void clear(std::basic_ostream<CharT, TraitT> &s) { s << detail::clearer<T>{}; }

  // impose decoration on ostream
  template<class T, class CharT, class TraitT>
  std::basic_ostream<CharT, TraitT>& operator<<(
    std::basic_ostream<CharT, TraitT> &s, decor<T, CharT, TraitT> && h)
  {
    using deco_type = decor<T, CharT, TraitT>;
    void const * const p = s.pword(deco_type::xindex);
    // delete if already set
    if (p) delete static_cast<deco_type const *>(p);
    s.pword(deco_type::xindex) = static_cast<void *>(new deco_type{ std::move(h) });
    // check whether we alread have a callback registered
    if (s.iword(deco_type::xindex) == 0)
    { // if this is not the case register callback and set iword
      s.register_callback(detail::manage_decor<T, CharT, TraitT>, deco_type::xindex);
      s.iword(deco_type::xindex) = 1;
    }
    return s;
  }

  template<class T, class CharT = char, class TraitT = std::char_traits<CharT>>
  struct defaulted
  {
    static inline decor<T, CharT, TraitT> decoration()
    {
      return{ detail::default_data<CharT>::decor[0],
        detail::default_data<CharT>::decor[1],
        detail::default_data<CharT>::decor[2] };
    }
  };

  template<class T, class CharT = char, class TraitT = std::char_traits<CharT>>
  decor<T, CharT, TraitT> decoration(
    std::basic_string<CharT, TraitT> const & prefix,
    std::basic_string<CharT, TraitT> const & delimiter,
    std::basic_string<CharT, TraitT> const & postfix)
  {
    return{ prefix, delimiter, postfix };
  }

  template<class T, class CharT = char,
  class TraitT = std::char_traits < CharT >>
    decor<T, CharT, TraitT> decoration(
      std::basic_string<CharT, TraitT> const & delimiter)
  {
    using str_type = std::basic_string<CharT, TraitT>;
    return{ defaulted<T, CharT, TraitT>::decoration().prefix,
      delimiter, defaulted<T, CharT, TraitT>::decoration().postfix };
  }

  template<class T, class CharT = char,
  class TraitT = std::char_traits < CharT >>
    decor<T, CharT, TraitT> decoration(CharT const * const prefix,
      CharT const * const delimiter, CharT const * const postfix)
  {
    using str_type = std::basic_string<CharT, TraitT>;
    return{ str_type{ prefix }, str_type{ delimiter }, str_type{ postfix } };
  }

  template<class T, class CharT = char,
  class TraitT = std::char_traits < CharT >>
    decor<T, CharT, TraitT> decoration(CharT const * const delimiter)
  {
    using str_type = std::basic_string<CharT, TraitT>;
    return{ defaulted<T, CharT, TraitT>::decoration().prefix,
      str_type{ delimiter }, defaulted<T, CharT, TraitT>::decoration().postfix };
  }

  template<typename T, std::size_t N, std::size_t L>
  struct tuple
  {
    template<class CharT, class TraitT>
    static void print(std::basic_ostream<CharT, TraitT>& s, T const & value,
      std::basic_string<CharT, TraitT> const &delimiter)
    {
      s << std::get<N>(value) << delimiter;
      tuple<T, N + 1, L>::print(s, value, delimiter);
    }
  };

  template<typename T, std::size_t N>
  struct tuple<T, N, N>
  {
    template<class CharT, class TraitT>
    static void print(std::basic_ostream<CharT, TraitT>& s, T const & value,
      std::basic_string<CharT, TraitT> const &) {
      s << std::get<N>(value);
    }
  };

}

template<class CharT, class TraitT>
std::basic_ostream<CharT, TraitT> & operator<< (
  std::basic_ostream<CharT, TraitT> &s, std::tuple<> const & v)
{
  using deco_type = pretty::decor<std::tuple<void*>, CharT, TraitT>;
  using defaulted_type = pretty::defaulted<std::tuple<void*>, CharT, TraitT>;
  void const * const p = s.pword(deco_type::xindex);
  auto const d = static_cast<deco_type const * const>(p);
  s << (d ? d->prefix : defaulted_type::decoration().prefix);
  s << (d ? d->postfix : defaulted_type::decoration().postfix);
  return s;
}

template<class CharT, class TraitT, class ... T>
std::basic_ostream<CharT, TraitT> & operator<< (
  std::basic_ostream<CharT, TraitT> &s, std::tuple<T...> const & v)
{
  using deco_type = pretty::decor<std::tuple<void*>, CharT, TraitT>;
  using defaulted_type = pretty::defaulted<std::tuple<void*>, CharT, TraitT>;
  using pretty_tuple = pretty::tuple<std::tuple<T...>, 0U, sizeof...(T)-1U>;
  void const * const p = s.pword(deco_type::xindex);
  auto const d = static_cast<deco_type const * const>(p);
  s << (d ? d->prefix : defaulted_type::decoration().prefix);
  pretty_tuple::print(s, v, d ? d->delimiter : 
    defaulted_type::decoration().delimiter);
  s << (d ? d->postfix : defaulted_type::decoration().postfix);
  return s;
}

template<class T, class U, class CharT, class TraitT>
std::basic_ostream<CharT, TraitT> & operator<< (
  std::basic_ostream<CharT, TraitT> &s, std::pair<T, U> const & v)
{
  using deco_type = pretty::decor<std::pair<T, U>, CharT, TraitT>;
  using defaulted_type = pretty::defaulted<std::pair<T, U>, CharT, TraitT>;
  void const * const p = s.pword(deco_type::xindex);
  auto const d = static_cast<deco_type const * const>(p);
  s << (d ? d->prefix : defaulted_type::decoration().prefix);
  s << v.first;
  s << (d ? d->delimiter : defaulted_type::decoration().delimiter);
  s << v.second;
  s << (d ? d->postfix : defaulted_type::decoration().postfix);
  return s;
}


template<class T, class CharT = char,
class TraitT = std::char_traits < CharT >>
  typename std::enable_if < pretty::detail::is_range<T>::value,
  std::basic_ostream < CharT, TraitT >> ::type & operator<< (
    std::basic_ostream<CharT, TraitT> &s, T const & v)
{
  bool first(true);
  using deco_type = pretty::decor<T, CharT, TraitT>;
  using default_type = pretty::defaulted<T, CharT, TraitT>;
  void const * const p = s.pword(deco_type::xindex);
  auto d = static_cast<pretty::decor<T, CharT, TraitT> const * const>(p);
  s << (d ? d->prefix : default_type::decoration().prefix);
  for (auto const & e : v)
  { // v is range thus range based for works
    if (!first) s << (d ? d->delimiter : default_type::decoration().delimiter);
    s << e;
    first = false;
  }
  s << (d ? d->postfix : default_type::decoration().postfix);
  return s;
}

#endif // pretty_print_0x57547_sa4884X_0_1_h_guard_

4

আমি এখানে আরও একটি উত্তর যুক্ত করতে যাচ্ছি, কারণ আমি আমার আগেরটির সাথে একটি ভিন্ন পদ্ধতির সাথে হাজির হয়েছি, এবং তা হল স্থানীয় অবস্থানগুলি ব্যবহার করা।

বুনিয়াদি এখানে

মূলত আপনি যা করেন তা হ'ল:

  1. থেকে উত্পন্ন একটি বর্গ তৈরি করুন std::locale::facet । সামান্য খারাপ দিক এটি আপনার আইডি ধরে রাখতে কোথাও একটি সংকলন ইউনিট প্রয়োজন। আসুন একে মাইপ্রেটিভেক্টরপ্রিন্টার বলি। আপনি সম্ভবত এটির একটি আরও ভাল নাম দিতে এবং জোড় এবং মানচিত্রের জন্য একটি তৈরি করতে চাই।
  2. আপনার স্ট্রিম ফাংশনে, আপনি পরীক্ষা করে দেখুন std::has_facet< MyPrettyVectorPrinter >
  3. যদি এটি সত্য হয় তবে এটি দিয়ে বের করুন std::use_facet< MyPrettyVectorPrinter >( os.getloc() )
  4. আপনার মুখের অবজেক্টগুলির ডিলিমিটারগুলির জন্য মান থাকবে এবং আপনি সেগুলি পড়তে পারেন। যদি চেহারাটি না পাওয়া যায় তবে আপনার মুদ্রণ ফাংশন ( operator<<) ডিফল্টরূপে সরবরাহ করে। নোট আপনি ভেক্টর পড়ার জন্য একই জিনিস করতে পারেন।

আমি এই পদ্ধতিটি পছন্দ করি কারণ আপনি এখনও একটি কাস্টম ওভাররাইড ব্যবহার করতে সক্ষম হওয়া সত্ত্বেও একটি ডিফল্ট মুদ্রণ ব্যবহার করতে পারেন।

ডাউনসাইডগুলি আপনার ফ্যাক্টের জন্য একটি লাইব্রেরি প্রয়োজন যদি একাধিক প্রকল্পে ব্যবহৃত হয় (সুতরাং কেবলমাত্র শিরোনামই হতে পারে না) এবং একটি নতুন লোকেল অবজেক্ট তৈরি করার ব্যয় সম্পর্কে আপনাকে সচেতন হওয়া দরকার।

আমি এটিকে আমার অন্যটি পরিবর্তনের পরিবর্তে একটি নতুন সমাধান হিসাবে লিখেছি কারণ আমি বিশ্বাস করি যে উভয় পদ্ধতিরই সঠিক হতে পারে এবং আপনি নিজের বাছাই করেন।


আমাকে এটি সোজা করে দেওয়া যাক: এই পদ্ধতির সাথে, আমি কীভাবে ব্যবহার করতে চাই তার প্রতিটি ধারক প্রকারের সক্রিয়ভাবে শ্বেতলিস্ট করা দরকার?
কেরেক এসবি

সত্যিই কারও নিজস্ব প্রকারের ব্যতীত অন্য কোনও স্ট্যান্ডকে প্রসারিত করা উচিত নয়, তবে আপনি অপারেটরের একটি ওভারলোড লিখুন << প্রতিটি ধারক প্রকারের জন্য (ভেক্টর, মানচিত্র, তালিকা, ডীক) আরও জোড় যা আপনি মুদ্রণ করতে সক্ষম হতে চান। অবশ্যই কিছু একটি অংশ ভাগ করতে পারে (যেমন আপনি তালিকা মুদ্রণ করতে ইচ্ছুক হতে পারে, ভেক্টর এবং একই সব deque)। আপনি একটি "ডিফল্ট" মুদ্রণ পদ্ধতি সরবরাহ করেন তবে ব্যবহারকারীদের একটি রূপ এবং লোকেল তৈরি করতে এবং মুদ্রণের আগে ইমবুউ করতে দেয়। কিছুটা বাড়ানোর মতো তার তারিখের সময় প্রিন্ট করে। ডিফল্টরূপে মুদ্রণের জন্য কেউ গ্লোবাল লোকালে তাদের চেহারাটি লোড করতে পারে।
ক্যাশকো

2

এখানে লক্ষ্যটি হ'ল আমরা কীভাবে প্রিন্ট করি তার কাস্টমাইজেশন করতে ADL ব্যবহার করা।

আপনি একটি ফর্ম্যাটর ট্যাগে পাস করেন এবং ট্যাগের নেমস্পেসে 4 টি ফাংশন (আগে, পরে, এর মধ্যে এবং নেমে) ওভাররাইড করে। কনটেইনারগুলির মাধ্যমে পুনরাবৃত্ত হওয়ার সময় ফর্ম্যাটরটি কীভাবে 'অলংকরণগুলি' প্রিন্ট করে তা পরিবর্তিত করে।

একটি ডিফল্ট বিন্যাস যা {(a->b),(c->d)}মানচিত্রের (a,b,c)জন্য, tupleoids "hello"জন্য, স্ট্রিংগুলির [x,y,z]জন্য , এবং অন্তর্ভুক্ত সমস্ত কিছুর জন্য করে।

এটি তৃতীয় পক্ষের পুনরাবৃত্তিযোগ্য প্রকারের সাথে "কেবলমাত্র কাজ করা" উচিত (এবং তাদের "অন্যান্য কিছুর" মতো আচরণ করুন)।

আপনি যদি আপনার তৃতীয় পক্ষের পুনরাবৃত্তদের জন্য কাস্টম অলঙ্কারগুলি চান, কেবল নিজের ট্যাগ তৈরি করুন। মানচিত্রের উত্স হ্যান্ডেল করতে কিছুটা সময় লাগবে (আপনার ওভারলোডের প্রয়োজনpretty_print_descend( your_tag ফিরে যাওয়ার জন্যpretty_print::decorator::map_magic_tag<your_tag> )। এটি করার একটি ক্লিনার উপায় সম্ভবত আছে, নিশ্চিত নয়।

পুনরুক্তি এবং টুপল-নেস সনাক্ত করার জন্য একটি ছোট গ্রন্থাগার:

namespace details {
  using std::begin; using std::end;
  template<class T, class=void>
  struct is_iterable_test:std::false_type{};
  template<class T>
  struct is_iterable_test<T,
    decltype((void)(
      (void)(begin(std::declval<T>())==end(std::declval<T>()))
      , ((void)(std::next(begin(std::declval<T>()))))
      , ((void)(*begin(std::declval<T>())))
      , 1
    ))
  >:std::true_type{};
  template<class T>struct is_tupleoid:std::false_type{};
  template<class...Ts>struct is_tupleoid<std::tuple<Ts...>>:std::true_type{};
  template<class...Ts>struct is_tupleoid<std::pair<Ts...>>:std::true_type{};
  // template<class T, size_t N>struct is_tupleoid<std::array<T,N>>:std::true_type{}; // complete, but problematic
}
template<class T>struct is_iterable:details::is_iterable_test<std::decay_t<T>>{};
template<class T, std::size_t N>struct is_iterable<T(&)[N]>:std::true_type{}; // bypass decay
template<class T>struct is_tupleoid:details::is_tupleoid<std::decay_t<T>>{};

template<class T>struct is_visitable:std::integral_constant<bool, is_iterable<T>{}||is_tupleoid<T>{}> {};

একটি লাইব্রেরি যা আমাদের পুনরাবৃত্ত বা টিপল ধরণের সামগ্রীর সামগ্রী দেখতে দেয়:

template<class C, class F>
std::enable_if_t<is_iterable<C>{}> visit_first(C&& c, F&& f) {
  using std::begin; using std::end;
  auto&& b = begin(c);
  auto&& e = end(c);
  if (b==e)
      return;
  std::forward<F>(f)(*b);
}
template<class C, class F>
std::enable_if_t<is_iterable<C>{}> visit_all_but_first(C&& c, F&& f) {
  using std::begin; using std::end;
  auto it = begin(c);
  auto&& e = end(c);
  if (it==e)
      return;
  it = std::next(it);
  for( ; it!=e; it = std::next(it) ) {
    f(*it);
  }
}

namespace details {
  template<class Tup, class F>
  void visit_first( std::index_sequence<>, Tup&&, F&& ) {}
  template<size_t... Is, class Tup, class F>
  void visit_first( std::index_sequence<0,Is...>, Tup&& tup, F&& f ) {
    std::forward<F>(f)( std::get<0>( std::forward<Tup>(tup) ) );
  }
  template<class Tup, class F>
  void visit_all_but_first( std::index_sequence<>, Tup&&, F&& ) {}
  template<size_t... Is,class Tup, class F>
  void visit_all_but_first( std::index_sequence<0,Is...>, Tup&& tup, F&& f ) {
    int unused[] = {0,((void)(
      f( std::get<Is>(std::forward<Tup>(tup)) )
    ),0)...};
    (void)(unused);
  }
}
template<class Tup, class F>
std::enable_if_t<is_tupleoid<Tup>{}> visit_first(Tup&& tup, F&& f) {
  details::visit_first( std::make_index_sequence< std::tuple_size<std::decay_t<Tup>>{} >{}, std::forward<Tup>(tup), std::forward<F>(f) );
}
template<class Tup, class F>
std::enable_if_t<is_tupleoid<Tup>{}> visit_all_but_first(Tup&& tup, F&& f) {
  details::visit_all_but_first( std::make_index_sequence< std::tuple_size<std::decay_t<Tup>>{} >{}, std::forward<Tup>(tup), std::forward<F>(f) );
}

একটি সুন্দর মুদ্রণ গ্রন্থাগার:

namespace pretty_print {
  namespace decorator {
    struct default_tag {};
    template<class Old>
    struct map_magic_tag:Old {}; // magic for maps

    // Maps get {}s. Write trait `is_associative` to generalize:
    template<class CharT, class Traits, class...Xs >
    void pretty_print_before( default_tag, std::basic_ostream<CharT, Traits>& s, std::map<Xs...> const& ) {
      s << CharT('{');
    }

    template<class CharT, class Traits, class...Xs >
    void pretty_print_after( default_tag, std::basic_ostream<CharT, Traits>& s, std::map<Xs...> const& ) {
      s << CharT('}');
    }

    // tuples and pairs get ():
    template<class CharT, class Traits, class Tup >
    std::enable_if_t<is_tupleoid<Tup>{}> pretty_print_before( default_tag, std::basic_ostream<CharT, Traits>& s, Tup const& ) {
      s << CharT('(');
    }

    template<class CharT, class Traits, class Tup >
    std::enable_if_t<is_tupleoid<Tup>{}> pretty_print_after( default_tag, std::basic_ostream<CharT, Traits>& s, Tup const& ) {
      s << CharT(')');
    }

    // strings with the same character type get ""s:
    template<class CharT, class Traits, class...Xs >
    void pretty_print_before( default_tag, std::basic_ostream<CharT, Traits>& s, std::basic_string<CharT, Xs...> const& ) {
      s << CharT('"');
    }
    template<class CharT, class Traits, class...Xs >
    void pretty_print_after( default_tag, std::basic_ostream<CharT, Traits>& s, std::basic_string<CharT, Xs...> const& ) {
      s << CharT('"');
    }
    // and pack the characters together:
    template<class CharT, class Traits, class...Xs >
    void pretty_print_between( default_tag, std::basic_ostream<CharT, Traits>&, std::basic_string<CharT, Xs...> const& ) {}

    // map magic. When iterating over the contents of a map, use the map_magic_tag:
    template<class...Xs>
    map_magic_tag<default_tag> pretty_print_descend( default_tag, std::map<Xs...> const& ) {
      return {};
    }
    template<class old_tag, class C>
    old_tag pretty_print_descend( map_magic_tag<old_tag>, C const& ) {
      return {};
    }

    // When printing a pair immediately within a map, use -> as a separator:
    template<class old_tag, class CharT, class Traits, class...Xs >
    void pretty_print_between( map_magic_tag<old_tag>, std::basic_ostream<CharT, Traits>& s, std::pair<Xs...> const& ) {
      s << CharT('-') << CharT('>');
    }
  }

  // default behavior:
  template<class CharT, class Traits, class Tag, class Container >
  void pretty_print_before( Tag const&, std::basic_ostream<CharT, Traits>& s, Container const& ) {
    s << CharT('[');
  }
  template<class CharT, class Traits, class Tag, class Container >
  void pretty_print_after( Tag const&, std::basic_ostream<CharT, Traits>& s, Container const& ) {
    s << CharT(']');
  }
  template<class CharT, class Traits, class Tag, class Container >
  void pretty_print_between( Tag const&, std::basic_ostream<CharT, Traits>& s, Container const& ) {
    s << CharT(',');
  }
  template<class Tag, class Container>
  Tag&& pretty_print_descend( Tag&& tag, Container const& ) {
    return std::forward<Tag>(tag);
  }

  // print things by default by using <<:
  template<class Tag=decorator::default_tag, class Scalar, class CharT, class Traits>
  std::enable_if_t<!is_visitable<Scalar>{}> print( std::basic_ostream<CharT, Traits>& os, Scalar&& scalar, Tag&&=Tag{} ) {
    os << std::forward<Scalar>(scalar);
  }
  // for anything visitable (see above), use the pretty print algorithm:
  template<class Tag=decorator::default_tag, class C, class CharT, class Traits>
  std::enable_if_t<is_visitable<C>{}> print( std::basic_ostream<CharT, Traits>& os, C&& c, Tag&& tag=Tag{} ) {
    pretty_print_before( std::forward<Tag>(tag), os, std::forward<C>(c) );
    visit_first( c, [&](auto&& elem) {
      print( os, std::forward<decltype(elem)>(elem), pretty_print_descend( std::forward<Tag>(tag), std::forward<C>(c) ) );
    });
    visit_all_but_first( c, [&](auto&& elem) {
      pretty_print_between( std::forward<Tag>(tag), os, std::forward<C>(c) );
      print( os, std::forward<decltype(elem)>(elem), pretty_print_descend( std::forward<Tag>(tag), std::forward<C>(c) ) );
    });
    pretty_print_after( std::forward<Tag>(tag), os, std::forward<C>(c) );
  }
}

পরীক্ষার কোড:

int main() {
  std::vector<int> x = {1,2,3};

  pretty_print::print( std::cout, x );
  std::cout << "\n";

  std::map< std::string, int > m;
  m["hello"] = 3;
  m["world"] = 42;

  pretty_print::print( std::cout, m );
  std::cout << "\n";
}

সরাসরি উদাহরণ

এটি সি ++ ১৪ টি বৈশিষ্ট্য (কিছু উপকরণ _tএবং auto&&ল্যাম্বডাস) ব্যবহার করে তবে কোনওটিই প্রয়োজনীয় নয়।


@ কেরেকএসবি কার্যকারী সংস্করণ, কিছু পরিবর্তন রয়েছে। কোডটির বেশিরভাগ অংশই এই মুহুর্তে সাধারণ "ভিজিট টুপলস / পুনরাবৃত্তি" এবং অভিনব ফর্ম্যাটিং ( এর গুলি এর ->মধ্যে অন্তর্ভুক্ত )। সুন্দর প্রিন্ট লাইব্রেরির মূলটি খুব সুন্দর এবং ছোট, যা দুর্দান্ত। আমি এটিকে সহজেই প্রসারিত করার চেষ্টা করেছি, নিশ্চিত হয়েছি যে আমি সফল হই কিনা। pairmap
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

1

আমার সমাধান simple.h যার অংশ, SCC প্যাকেজ। সমস্ত স্ট্যান্ডের ধারক, মানচিত্র, সেট, সি-অ্যারে মুদ্রণযোগ্য।


মজাদার. আমি ধারকগুলির জন্য টেমপ্লেট-অফ-টেম্পলেট পদ্ধতির পছন্দ করি, তবে এটি কি কাস্টম পাত্রে এবং এসটিএল কনটেইনারগুলির সাথে অ-মানক পূর্বাভাস বা বরাদ্দকারীদের সাথে কাজ করে? (আমি বৈকল্পিক টেম্পলেটগুলি ব্যবহার করে সি ++ 0x এ একটি বিম্যাপ প্রয়োগের প্রয়াসের জন্য অনুরূপ কিছু করেছি )) এছাড়াও, আপনার মুদ্রণের রুটিনগুলির জন্য আপনি সাধারণভাবে পুনরাবৃত্তিকে ব্যবহার করবেন বলে মনে হয় না; কাউন্টারটির স্পষ্ট ব্যবহার কেন i?
কেরেক এসবি

অ-মানক পূর্বাভাস সহ ধারকটি কী? স্বাক্ষর মেলে যা কাস্টম ধারক মুদ্রণ করা হবে। অ-মানক বরাদ্দগুলি এখনই সমর্থিত নয়, তবে এটি ঠিক করা সহজ। আমার এই মুহুর্তের দরকার নেই।
লিওনিড ভলনিটস্কি

পুনরাবৃত্তির পরিবর্তে সূচক ব্যবহার করার উপযুক্ত কারণ নেই। .তিহাসিক কারণ। আমার সময় পেলে এটি ঠিক করে দেবে।
লিওনিড ভলনিটস্কি

"নন-স্ট্যান্ডার্ড পূর্বাভাসযুক্ত ধারক দ্বারা" বলতে আমি std::setকাস্টম তুলনাকারীর মতো কিছু বা কাস্টম সমতার সাথে আনর্ডারড_ম্যাপের অর্থ। এই নির্মাণগুলিকে সমর্থন করা খুব গুরুত্বপূর্ণ হবে।
কেরেক এসবি

1

প্রথম বুস্টকন (বর্তমানে সিপ্পকন নামে পরিচিত) এর মধ্যে একটি থেকে বেরিয়ে এসে আমি এবং আরও দু'জন একটি লাইব্রেরিতে কাজ করার জন্য কাজ করেছি। মূল স্টিকিং পয়েন্টটির নামস্থান স্ট্যান্ডের প্রসারিত হওয়া দরকার ছিল। এটি একটি বুস্ট লাইব্রেরির জন্য যেতে হবে না পরিণত হয়েছিল।

দুর্ভাগ্যক্রমে কোডটির লিঙ্কগুলি আর কাজ করে না, তবে আপনি আলোচনায় কিছু আকর্ষণীয় সংবাদ পেয়ে যাবেন (কমপক্ষে যারা নাম রাখবেন সে সম্পর্কে কথা বলছেন না!)

http://boost.2283326.n4.nabble.com/explore-Library-Proposal-Container-Streaming-td2619544.html


0

2016 সালে বাস্তবায়নের আমার সংস্করণটি এখানে

একটি শিরোনামে সমস্ত কিছু, তাই https://github.com/skident/eos/blob/master/incolve/eos/io/print.hpp ব্যবহার করা সহজ

/*! \file       print.hpp
 *  \brief      Useful functions for work with STL containers. 
 *          
 *  Now it supports generic print for STL containers like: [elem1, elem2, elem3]
 *  Supported STL conrainers: vector, deque, list, set multiset, unordered_set,
 *  map, multimap, unordered_map, array
 *
 *  \author     Skident
 *  \date       02.09.2016
 *  \copyright  Skident Inc.
 */

#pragma once

// check is the C++11 or greater available (special hack for MSVC)
#if (defined(_MSC_VER) && __cplusplus >= 199711L) || __cplusplus >= 201103L
    #define MODERN_CPP_AVAILABLE 1
#endif


#include <iostream>
#include <sstream>
#include <vector>
#include <deque>
#include <set>
#include <list>
#include <map>
#include <cctype>

#ifdef MODERN_CPP_AVAILABLE
    #include <array>
    #include <unordered_set>
    #include <unordered_map>
    #include <forward_list>
#endif


#define dump(value) std::cout << (#value) << ": " << (value) << std::endl

#define BUILD_CONTENT                                                       \
        std::stringstream ss;                                               \
        for (; it != collection.end(); ++it)                                \
        {                                                                   \
            ss << *it << elem_separator;                                    \
        }                                                                   \


#define BUILD_MAP_CONTENT                                                   \
        std::stringstream ss;                                               \
        for (; it != collection.end(); ++it)                                \
        {                                                                   \
            ss  << it->first                                                \
                << keyval_separator                                         \
                << it->second                                               \
                << elem_separator;                                          \
        }                                                                   \


#define COMPILE_CONTENT                                                     \
        std::string data = ss.str();                                        \
        if (!data.empty() && !elem_separator.empty())                       \
            data = data.substr(0, data.rfind(elem_separator));              \
        std::string result = first_bracket + data + last_bracket;           \
        os << result;                                                       \
        if (needEndl)                                                       \
            os << std::endl;                                                \



////
///
///
/// Template definitions
///
///

//generic template for classes: deque, list, forward_list, vector
#define VECTOR_AND_CO_TEMPLATE                                          \
    template<                                                           \
        template<class T,                                               \
                 class Alloc = std::allocator<T> >                      \
        class Container, class Type, class Alloc>                       \

#define SET_TEMPLATE                                                    \
    template<                                                           \
        template<class T,                                               \
                 class Compare = std::less<T>,                          \
                 class Alloc = std::allocator<T> >                      \
            class Container, class T, class Compare, class Alloc>       \

#define USET_TEMPLATE                                                   \
    template<                                                           \
template < class Key,                                                   \
           class Hash = std::hash<Key>,                                 \
           class Pred = std::equal_to<Key>,                             \
           class Alloc = std::allocator<Key>                            \
           >                                                            \
    class Container, class Key, class Hash, class Pred, class Alloc     \
    >                                                                   \


#define MAP_TEMPLATE                                                    \
    template<                                                           \
        template<class Key,                                             \
                class T,                                                \
                class Compare = std::less<Key>,                         \
                class Alloc = std::allocator<std::pair<const Key,T> >   \
                >                                                       \
        class Container, class Key,                                     \
        class Value/*, class Compare, class Alloc*/>                    \


#define UMAP_TEMPLATE                                                   \
    template<                                                           \
        template<class Key,                                             \
                   class T,                                             \
                   class Hash = std::hash<Key>,                         \
                   class Pred = std::equal_to<Key>,                     \
                   class Alloc = std::allocator<std::pair<const Key,T> >\
                 >                                                      \
        class Container, class Key, class Value,                        \
        class Hash, class Pred, class Alloc                             \
                >                                                       \


#define ARRAY_TEMPLATE                                                  \
    template<                                                           \
        template<class T, std::size_t N>                                \
        class Array, class Type, std::size_t Size>                      \



namespace eos
{
    static const std::string default_elem_separator     = ", ";
    static const std::string default_keyval_separator   = " => ";
    static const std::string default_first_bracket      = "[";
    static const std::string default_last_bracket       = "]";


    //! Prints template Container<T> as in Python
    //! Supported containers: vector, deque, list, set, unordered_set(C++11), forward_list(C++11)
    //! \param collection which should be printed
    //! \param elem_separator the separator which will be inserted between elements of collection
    //! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
    //! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
    template<class Container>
    void print( const Container& collection
              , const std::string& elem_separator   = default_elem_separator
              , const std::string& first_bracket    = default_first_bracket
              , const std::string& last_bracket     = default_last_bracket
              , std::ostream& os = std::cout
              , bool needEndl = true
            )
    {
        typename Container::const_iterator it = collection.begin();
        BUILD_CONTENT
        COMPILE_CONTENT
    }


    //! Prints collections with one template argument and allocator as in Python.
    //! Supported standard collections: vector, deque, list, forward_list
    //! \param collection which should be printed
    //! \param elem_separator the separator which will be inserted between elements of collection
    //! \param keyval_separator separator between key and value of map. For default it is the '=>'
    //! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
    //! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
    VECTOR_AND_CO_TEMPLATE
    void print( const Container<Type>& collection
              , const std::string& elem_separator   = default_elem_separator
              , const std::string& first_bracket    = default_first_bracket
              , const std::string& last_bracket     = default_last_bracket
              , std::ostream& os = std::cout
              , bool needEndl = true
            )
    {
        typename Container<Type>::const_iterator it = collection.begin();
        BUILD_CONTENT
        COMPILE_CONTENT
    }


    //! Prints collections like std:set<T, Compare, Alloc> as in Python
    //! \param collection which should be printed
    //! \param elem_separator the separator which will be inserted between elements of collection
    //! \param keyval_separator separator between key and value of map. For default it is the '=>'
    //! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
    //! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
    SET_TEMPLATE
    void print( const Container<T, Compare, Alloc>& collection
              , const std::string& elem_separator   = default_elem_separator
              , const std::string& first_bracket    = default_first_bracket
              , const std::string& last_bracket     = default_last_bracket
              , std::ostream& os = std::cout
              , bool needEndl = true
            )
    {
        typename Container<T, Compare, Alloc>::const_iterator it = collection.begin();
        BUILD_CONTENT
        COMPILE_CONTENT
    }


    //! Prints collections like std:unordered_set<Key, Hash, Pred, Alloc> as in Python
    //! \param collection which should be printed
    //! \param elem_separator the separator which will be inserted between elements of collection
    //! \param keyval_separator separator between key and value of map. For default it is the '=>'
    //! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
    //! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
    USET_TEMPLATE
    void print( const Container<Key, Hash, Pred, Alloc>& collection
              , const std::string& elem_separator   = default_elem_separator
              , const std::string& first_bracket    = default_first_bracket
              , const std::string& last_bracket     = default_last_bracket
              , std::ostream& os = std::cout
              , bool needEndl = true
            )
    {
        typename Container<Key, Hash, Pred, Alloc>::const_iterator it = collection.begin();
        BUILD_CONTENT
        COMPILE_CONTENT
    }

    //! Prints collections like std:map<T, U> as in Python
    //! supports generic objects of std: map, multimap
    //! \param collection which should be printed
    //! \param elem_separator the separator which will be inserted between elements of collection
    //! \param keyval_separator separator between key and value of map. For default it is the '=>'
    //! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
    //! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
    MAP_TEMPLATE
    void print(   const Container<Key, Value>& collection
                , const std::string& elem_separator   = default_elem_separator
                , const std::string& keyval_separator = default_keyval_separator
                , const std::string& first_bracket    = default_first_bracket
                , const std::string& last_bracket     = default_last_bracket
                , std::ostream& os = std::cout
                , bool needEndl = true
        )
    {
        typename Container<Key, Value>::const_iterator it = collection.begin();
        BUILD_MAP_CONTENT
        COMPILE_CONTENT
    }

    //! Prints classes like std:unordered_map as in Python
    //! \param collection which should be printed
    //! \param elem_separator the separator which will be inserted between elements of collection
    //! \param keyval_separator separator between key and value of map. For default it is the '=>'
    //! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
    //! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
    UMAP_TEMPLATE
    void print(   const Container<Key, Value, Hash, Pred, Alloc>& collection
                , const std::string& elem_separator   = default_elem_separator
                , const std::string& keyval_separator = default_keyval_separator
                , const std::string& first_bracket    = default_first_bracket
                , const std::string& last_bracket     = default_last_bracket
                , std::ostream& os = std::cout
                , bool needEndl = true
        )
    {
        typename Container<Key, Value, Hash, Pred, Alloc>::const_iterator it = collection.begin();
        BUILD_MAP_CONTENT
        COMPILE_CONTENT
    }

    //! Prints collections like std:array<T, Size> as in Python
    //! \param collection which should be printed
    //! \param elem_separator the separator which will be inserted between elements of collection
    //! \param keyval_separator separator between key and value of map. For default it is the '=>'
    //! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
    //! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
    ARRAY_TEMPLATE
    void print(   const Array<Type, Size>& collection
                , const std::string& elem_separator   = default_elem_separator
                , const std::string& first_bracket    = default_first_bracket
                , const std::string& last_bracket     = default_last_bracket
                , std::ostream& os = std::cout
                , bool needEndl = true
            )
    {
        typename Array<Type, Size>::const_iterator it = collection.begin();
        BUILD_CONTENT
        COMPILE_CONTENT
    }

    //! Removes all whitespaces before data in string.
    //! \param str string with data
    //! \return string without whitespaces in left part
    std::string ltrim(const std::string& str);

    //! Removes all whitespaces after data in string
    //! \param str string with data
    //! \return string without whitespaces in right part
    std::string rtrim(const std::string& str);

    //! Removes all whitespaces before and after data in string
    //! \param str string with data
    //! \return string without whitespaces before and after data in string
    std::string trim(const std::string& str);



    ////////////////////////////////////////////////////////////
    ////////////////////////ostream logic//////////////////////
    /// Should be specified for concrete containers
    /// because of another types can be suitable
    /// for templates, for example templates break
    /// the code like this "cout << string("hello") << endl;"
    ////////////////////////////////////////////////////////////



#define PROCESS_VALUE_COLLECTION(os, collection)                            \
    print(  collection,                                                     \
            default_elem_separator,                                         \
            default_first_bracket,                                          \
            default_last_bracket,                                           \
            os,                                                             \
            false                                                           \
    );                                                                      \

#define PROCESS_KEY_VALUE_COLLECTION(os, collection)                        \
    print(  collection,                                                     \
            default_elem_separator,                                         \
            default_keyval_separator,                                       \
            default_first_bracket,                                          \
            default_last_bracket,                                           \
            os,                                                             \
            false                                                           \
    );                                                                      \

    ///< specialization for vector
    template<class T>
    std::ostream& operator<<(std::ostream& os, const std::vector<T>& collection)
    {
        PROCESS_VALUE_COLLECTION(os, collection)
        return os;
    }

    ///< specialization for deque
    template<class T>
    std::ostream& operator<<(std::ostream& os, const std::deque<T>& collection)
    {
        PROCESS_VALUE_COLLECTION(os, collection)
        return os;
    }

    ///< specialization for list
    template<class T>
    std::ostream& operator<<(std::ostream& os, const std::list<T>& collection)
    {
        PROCESS_VALUE_COLLECTION(os, collection)
        return os;
    }

    ///< specialization for set
    template<class T>
    std::ostream& operator<<(std::ostream& os, const std::set<T>& collection)
    {
        PROCESS_VALUE_COLLECTION(os, collection)
        return os;
    }

    ///< specialization for multiset
    template<class T>
    std::ostream& operator<<(std::ostream& os, const std::multiset<T>& collection)
    {
        PROCESS_VALUE_COLLECTION(os, collection)
        return os;
    }

#ifdef MODERN_CPP_AVAILABLE
    ///< specialization for unordered_map
    template<class T>
    std::ostream& operator<<(std::ostream& os, const std::unordered_set<T>& collection)
    {
        PROCESS_VALUE_COLLECTION(os, collection)
        return os;
    }

    ///< specialization for forward_list
    template<class T>
    std::ostream& operator<<(std::ostream& os, const std::forward_list<T>& collection)
    {
        PROCESS_VALUE_COLLECTION(os, collection)
        return os;
    }

    ///< specialization for array
    template<class T, std::size_t N>
    std::ostream& operator<<(std::ostream& os, const std::array<T, N>& collection)
    {
        PROCESS_VALUE_COLLECTION(os, collection)
        return os;
    }
#endif

    ///< specialization for map, multimap
    MAP_TEMPLATE
    std::ostream& operator<<(std::ostream& os, const Container<Key, Value>& collection)
    {
        PROCESS_KEY_VALUE_COLLECTION(os, collection)
        return os;
    }

    ///< specialization for unordered_map
    UMAP_TEMPLATE
    std::ostream& operator<<(std::ostream& os, const Container<Key, Value, Hash, Pred, Alloc>& collection)
    {
        PROCESS_KEY_VALUE_COLLECTION(os, collection)
        return os;
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.