এই পোস্টের শেষে আপডেটগুলি নোট করুন।
আপডেট: আমি এই লাইব্রেরির জন্য গিটহাবে একটি সর্বজনীন প্রকল্প তৈরি করেছি !
আমি একটি একক টেমপ্লেট রাখতে চাই যা একবার এবং সমস্ত ক্ষেত্রে সমস্ত এসটিএল কনটেইনারগুলির মাধ্যমে প্রিন্ট-প্রিন্টিংয়ের যত্ন নেয় 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;
কিছু ধারক লিখতে সক্ষম হতে চাই v
। MyPrinter
একটি ধরণের মুছে ফেলা ক্লাস হবে, যেমন:
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); }
pretty_print
হ'ল অপারেটরদের নামের জায়গাতে রাখবে এবং মুদ্রণের সময় ব্যবহারকারীর জন্য একটি মোড়ক সরবরাহ করা হবে। ব্যবহারকারীর দৃষ্টিকোণ থেকে: std::cout << pretty_print(v);
(সম্ভবত অন্য কোনও নাম সহ)। তারপরে আপনি মোড়কের মতো একই নামস্থলে অপারেটরটি সরবরাহ করতে পারেন এবং এটি আপনার পছন্দসই প্রিন্টিংয়ে প্রসারিত করতে পারে। আপনি প্রতিটি কলের মধ্যে