您使用哪种C ++标准库包装函数?
今天早上提出的这个问题使我想知道C ++标准库中缺less哪些function,以及如何用封装function填补空白。 例如,我自己的实用程序库有这个向量附加function:
template <class T> std::vector<T> & operator += ( std::vector<T> & v1, const std::vector <T> & v2 ) { v1.insert( v1.end(), v2.begin(), v2.end() ); return v1; }
这个清除(或多或less)任何types – 特别是像std :: stack:
template <class C> void Clear( C & c ) { c = C(); }
我还有几个,但是我对你使用哪一个感兴趣? 请限制封装函数的答案 – 即不超过几行代码。
boost :: array
包含 (容器,val)(很简单,但方便)。
template<typename C, typename T> bool contains(const C& container, const T& val) { return std::find(std::begin(container), std::end(container), val) != std::end(container); }
remove_unstable(开始,结束,值)
std :: remove的更快版本,除了不保留其余对象的顺序。
template <typename T> T remove_unstable(T start, T stop, const typename T::value_type& val){ while(start != stop) { if (*start == val) { --stop; ::std::iter_swap(start, stop); } else { ++start; } } return stop; }
(在podtypes(int,float等)的向量的情况下,几乎所有对象都被删除,std :: remove可能会更快)。
很多时候,我会使用向量作为一套项目没有特定的顺序(显然,当我不需要快速是这个元素在这里设置的检查)。 在这些情况下,调用erase()会浪费时间,因为它会重新sorting元素,我不关心顺序。 这就是当下面的O(1)函数派上用场时 – 只要把最后一个元素移动到你想要删除的那个位置:
template<typename T> void erase_unordered(std::vector<T>& v, size_t index) { v[index] = v.back(); v.pop_back(); }
template < class T > class temp_value { public : temp_value(T& var) : _var(var), _original(var) {} ~temp_value() { _var = _original; } private : T& _var; T _original; temp_value(const temp_value&); temp_value& operator=(const temp_value&); };
好的,因为这似乎并不像我想的那样直截了当,下面是一个解释:
在其构造函数temp_value
存储对variables的引用和variables原始值的副本。 在其析构函数中,它将被引用的variables恢复到它的原始值。 所以,无论你对构造和销毁之间的variables做了什么,当temp_value
对象超出范围时,它将被重置。
像这样使用它:
void f(some_type& var) { temp_value<some_type> restorer(var); // remembers var's value // change var as you like g(var); // upon destruction restorer will restore var to its original value }
这是另一种使用示波器技巧的方法:
namespace detail { // use scope-guard trick class restorer_base { public: // call to flag the value shouldn't // be restored at destruction void dismiss(void) const { mDismissed = true; } protected: // creation restorer_base(void) : mDismissed(false) {} restorer_base(const restorer_base& pOther) : mDismissed(pOther.is_dismissed()) { // take "ownership" pOther.dismiss(); } ~restorer_base(void) {} // non-virtual // query bool is_dismissed(void) const { return mDismissed; } private: // not copy-assignable, copy-constructibility is ok restorer_base& operator=(const restorer_base&); mutable bool mDismissed; }; // generic single-value restorer, could be made // variadic to store and restore several variables template <typename T> class restorer_holder : public restorer_base { public: restorer_holder(T& pX) : mX(pX), mValue(pX) {} ~restorer_holder(void) { if (!is_dismissed()) mX = mValue; } private: // not copy-assignable, copy-constructibility is ok restorer_holder& operator=(const restorer_holder&); T& mX; T mValue; }; } // store references to generated holders typedef const detail::restorer_base& restorer; // generator (could also be made variadic) template <typename T> detail::restorer_holder<T> store(T& pX) { return detail::restorer_holder<T>(pX); }
这只是一个更多的锅炉代码,但允许更清洁的使用:
#include <iostream> template <typename T> void print(const T& pX) { std::cout << pX << std::endl; } void foo(void) { double d = 10.0; double e = 12.0; print(d); print(e); { restorer f = store(d); restorer g = store(e); d = -5.0; e = 3.1337; print(d); print(e); g.dismiss(); } print(d); print(e); } int main(void) { foo(); int i = 5; print(i); { restorer r = store(i); i *= 123; print(i); } print(i); }
尽pipe如此,它却消除了在课堂上使用的能力。
这是获得相同效果的第三种方法(不会遇到可能抛出析构函数的问题):
执行:
//none -- it is built into the language
用法:
#include <iostream> template <typename T> void print(const T& pX) { std::cout << pX << std::endl; } void foo(void) { double d = 10.0; double e = 12.0; print(d); print(e); { double f(d); double g(e); f = -5.0; g = 3.1337; print(f); print(g); e = std::move(g); } print(d); print(e); } int main(void) { foo(); int i = 5; print(i); { int r(i); r *= 123; print(r); } print(i); }
不是一个真正的包装,但臭名昭着的缺lesscopy_if
。 从这里
template<typename In, typename Out, typename Pred> Out copy_if(In first, In last, Out res, Pred Pr) { while (first != last) { if (Pr(*first)) { *res++ = *first; } ++first; } return res; }
template< typename T, std::size_t sz > inline T* begin(T (&array)[sz]) {return array;} template< typename T, std::size_t sz > inline T* end (T (&array)[sz]) {return array + sz;}
有时我觉得我在begin()
和end()
地狱。 我想有一些function,如:
template<typename T> void sort(T& x) { std::sort(x.begin(), x.end()); }
和其他类似的为std::find
, std::for_each
,基本上所有的STLalgorithm。
我觉得sort(x)
比sort(x.begin(), x.end())
更容易阅读/理解。
我几乎不再使用这个,但它曾经是一个主要的东西:
template<typename T> std::string make_string(const T& data) { std::ostringstream stream; stream << data; return stream.str(); }
将更新更多,因为我记得他们。 :P
每个工具箱中的效用函数当然是copy_if
。 不是真的包装。
我通常使用的另一个助手是deleter
,我使用std::for_each
来删除容器中的所有指针。
挖掘我的“sth.h”我也发现vector<wstring> StringSplit(wstring const&, wchar_t);
我有一个头,把下面的“util”命名空间:
// does a string contain another string inline bool contains(const std::string &s1, const std::string &s2) { return s1.find(s2) != std::string::npos; } // remove trailing whitespace inline std::string &rtrim(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end()); return s; } // remove leading whitespace inline std::string <rim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace)))); return s; } // remove whitespace from both ends inline std::string &trim(std::string &s) { return ltrim(rtrim(s)); } // split a string based on a delimeter and return the result (you pass an existing vector for the results) inline std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) { std::stringstream ss(s); std::string item; while(std::getline(ss, item, delim)) { elems.push_back(item); } return elems; } // same as above, but returns a vector for you inline std::vector<std::string> split(const std::string &s, char delim) { std::vector<std::string> elems; return split(s, delim, elems); } // does a string end with another string inline bool endswith(const std::string &s, const std::string &ending) { return ending.length() <= s.length() && s.substr(s.length() - ending.length()) == ending; } // does a string begin with another string inline bool beginswith(const std::string &s, const std::string &start) { return s.compare(0, start.length(), start) == 0; }
臭名昭着的丢失algorithm:
template < class Container, class Value > void erase(Container& ioContainer, Value const& iValue) { ioContainer.erase( std::remove(ioContainer.begin(), ioContainer.end(), iValue), ioContainer.end()); } // erase template < class Container, class Pred > void erase_if(Container& ioContainer, Pred iPred) { ioContainer.erase( std::remove_if(ioContainer.begin(), ioContainer.end(), iPred), ioContainer.end()); } // erase_if
包装sprintf
string example = function("<li value='%d'>Buffer at: 0x%08X</li>", 42, &some_obj); // 'function' is one of the functions below: Format or stringf
我们的目标是从输出中解耦格式,而不会受到sprintf及其类似的麻烦。 这不是很漂亮,但它是非常有用的,特别是如果你的编码准则禁止iostreams。
这里是一个版本,根据需要分配,从尼尔·巴特沃斯。 [查看迈克版本的修订历史logging,我将其作为其余两个版本的子集删除。 它和Neil类似,除了后者是使用vector而不是delete []的exception安全的:string的ctor会抛出分配失败。 迈克也使用了稍后显示的相同技术来确定前面的大小。 -RP]
string Format( const char * fmt, ... ) { const int BUFSIZE = 1024; int size = BUFSIZE, rv = -1; vector <char> buf; do { buf.resize( size ); va_list valist; va_start( valist, fmt ); // if _vsnprintf() returns < 0, the buffer wasn't big enough // so increase buffer size and try again // NOTE: MSFT's _vsnprintf is different from C99's vsnprintf, // which returns non-negative on truncation // http://msdn.microsoft.com/en-us/library/1kt27hek.aspx rv = _vsnprintf( &buf[0], size, fmt, valist ); va_end( valist ); size *= 2; } while( rv < 0 ); return string( &buf[0] ); }
从罗杰·帕特 ( Roger Pate) ,这是一个预先确定需要的尺寸的版本。 这需要可写的std :: strings,这是由stream行的实现提供的,但是被C ++ 0x明确要求。 [查看Marcus版本的修订历史,我删除了它,因为它略有不同,但基本上是下面的一个子集。 -RP]
履行
void vinsertf(std::string& s, std::string::iterator it, char const* fmt, int const chars_needed, va_list args ) { using namespace std; int err; // local error code if (chars_needed < 0) err = errno; else { string::size_type const off = it - s.begin(); // save iterator offset if (it == s.end()) { // append to the end s.resize(s.size() + chars_needed + 1); // resize, allow snprintf's null it = s.begin() + off; // iterator was invalidated err = vsnprintf(&*it, chars_needed + 1, fmt, args); s.resize(s.size() - 1); // remove snprintf's null } else { char saved = *it; // save char overwritten by snprintf's null s.insert(it, chars_needed, '\0'); // insert needed space it = s.begin() + off; // iterator was invalidated err = vsnprintf(&*it, chars_needed + 1, fmt, args); *(it + chars_needed) = saved; // restore saved char } if (err >= 0) { // success return; } err = errno; it = s.begin() + off; // above resize might have invalidated 'it' // (invalidation is unlikely, but allowed) s.erase(it, it + chars_needed); } string what = stringf("vsnprintf: [%d] ", err); what += strerror(err); throw runtime_error(what); }
公共接口
std::string stringf(char const* fmt, ...) { using namespace std; string s; va_list args; va_start(args, fmt); int chars_needed = vsnprintf(0, 0, fmt, args); va_end(args); va_start(args, fmt); try { vinsertf(s, s.end(), fmt, chars_needed, args); } catch (...) { va_end(args); throw; } va_end(args); return s; } // these have nearly identical implementations to stringf above: std::string& appendf(std::string& s, char const* fmt, ...); std::string& insertf(std::string& s, std::string::iterator it, char const* fmt, ...);
is_sorted
实用程序,在应用algorithm(如include
期望sorting条目的algorithm)之前testing容器:
template < class FwdIt > bool is_sorted(FwdIt iBegin, FwdIt iEnd) { typedef typename std::iterator_traits<FwdIt>::value_type value_type; return adjacent_find(iBegin, iEnd, std::greater<value_type>()) == iEnd; } // is_sorted template < class FwdIt, class Pred > bool is_sorted_if(FwdIt iBegin, FwdIt iEnd, Pred iPred) { if (iBegin == iEnd) return true; FwdIt aIt = iBegin; for (++aIt; aIt != iEnd; ++iBegin, ++aIt) { if (!iPred(*iBegin, *aIt)) return false; } return true; } // is_sorted_if
是的,我知道,否定谓词并使用adjacent_find
的谓词版本会更好:)
绝对boost :: addressof
//! \brief Fills reverse_map from map, so that all keys of map // become values of reverse_map and all values become keys. //! \note This presumes that there is a one-to-one mapping in map! template< typename T1, typename T2, class TP1, class TA1, class TP2, class TA2 > inline void build_reverse_map( const std::map<T1,T2,TP1,TA1>& map , std::map<T2,T1,TP2,TA2>& reverse_map) { typedef std::map<T1,T2,TP1,TA1> map_type; typedef std::map<T2,T1,TP2,TA2> r_map_type; typedef typename r_map_type::value_type r_value_type; for( typename map_type::const_iterator it=map.begin(), end=map.end(); it!=end; ++it ) { const r_value_type v(it->second,it->first); const bool was_new = reverse_map.insert(v).second; assert(was_new); } }
看看我的stl_util.h
,许多经典(deleter函数, copy_if
),也是这个(可能也很常见,但我没有看到它给出的答复迄今)通过地图search并返回find的值或默认值,ala get
Python的dict
:
template<typename K, typename V> inline V search_map(const std::map<K, V>& mapping, const K& key, const V& null_result = V()) { typename std::map<K, V>::const_iterator i = mapping.find(key); if(i == mapping.end()) return null_result; return i->second; }
使用缺省构造的V
的缺省null_result
与std::map
的operator[]
的行为非常operator[]
,但是当映射为const(对我来说是通用的)或者默认构造的V是不是正确的使用。
这里是我的一套extra-utils,build立在一个boost.range'ish std-algo包装器上,你可能需要一些function。 (这是微不足道的写,这是有趣的东西)
#pragma once /** @file @brief Defines various utility classes/functions for handling ranges/function objects in addition to bsRange (which is a ranged version of the \<algorithm\> header) Items here uses a STL/boost-style naming due to their 'templatised' nature. If template variable is R, anything matching range_concept can be used. If template variable is C, it must be a container object (supporting C::erase()) */ #include <boost/range/begin.hpp> #include <boost/range/end.hpp> #include <boost/smart_ptr.hpp> namespace boost { struct use_default; template<class T> class iterator_range; #pragma warning(disable: 4348) // redeclaration of template default parameters (this clashes with fwd-decl in boost/transform_iterator.hpp) template < class UnaryFunction , class Iterator , class Reference = use_default , class Value = use_default > class transform_iterator; template < class Iterator , class Value = use_default , class Category = use_default , class Reference = use_default , class difference = use_default > class indirect_iterator; template<class T> struct range_iterator; template < class Incrementable , class CategoryOrTraversal = use_default , class difference = use_default > class counting_iterator; template <class Predicate, class Iterator> class filter_iterator; } namespace orz { /// determines if any value that compares equal exists in container template<class R, class T> inline bool contains(const R& r, const T& v) { return std::find(boost::begin(r), boost::end(r), v) != boost::end(r); } /// determines if predicate evaluates to true for any value in container template<class R, class F> inline bool contains_if(const R& r, const F& f) { return std::find_if(boost::begin(r), boost::end(r), f) != boost::end(r); } /// insert elements in range r at end of container c template<class R, class C> inline void insert(C& c, const R& r) { c.insert(c.end(), boost::begin(r), boost::end(r)); } /// copy elements that match predicate template<class I, class O, class P> inline void copy_if(I i, I end, O& o, const P& p) { for (; i != end; ++i) { if (p(*i)) { *o = *i; ++o; } } } /// copy elements that match predicate template<class R, class O, class P> inline void copy_if(R& r, O& o, const P& p) { copy_if(boost::begin(r), boost::end(r), o, p); } /// erases first element that compare equal template<class C, class T> inline bool erase_first(C& c, const T& v) { typename C::iterator end = boost::end(c); typename C::iterator i = std::find(boost::begin(c), end, v); return i != c.end() ? c.erase(i), true : false; } /// erases first elements that match predicate template<class C, class F> inline bool erase_first_if(C& c, const F& f) { typename C::iterator end = boost::end(c); typename C::iterator i = std::find_if(boost::begin(c), end, f); return i != end ? c.erase(i), true : false; } /// erase all elements (doesn't deallocate memory for std::vector) template<class C> inline void erase_all(C& c) { c.erase(c.begin(), c.end()); } /// erase all elements that compare equal template<typename C, typename T> int erase(C& c, const T& value) { int n = 0; for (boost::range_iterator<C>::type i = boost::begin(c); i != boost::end(c);) { if (*i == value) { i = c.erase(i); ++n; } else { ++i; } } return n; } /// erase all elements that match predicate template<typename C, typename F> int erase_if(C& c, const F& f) { int n = 0; for (boost::range_iterator<C>::type i = boost::begin(c); i != boost::end(c);) { if (f(*i)) { i = c.erase(i); ++n; } else { ++i; } } return n; } /// erases all consecutive duplicates from container (sort container first to get all) template<class C> inline int erase_duplicates(C& c) { boost::range_iterator<C>::type i = std::unique(c.begin(), c.end()); typename C::size_type n = std::distance(i, c.end()); c.erase(i, c.end()); return n; } /// erases all consecutive duplicates, according to predicate, from container (sort container first to get all) template<class C, class F> inline int erase_duplicates_if(C& c, const F& f) { boost::range_iterator<C>::type i = std::unique(c.begin(), c.end(), f); typename C::size_type n = std::distance(i, c.end()); c.erase(i, c.end()); return n; } /// fill but for the second value in each pair in range template<typename R, typename V> inline void fill_second(R& r, const V& v) { boost::range_iterator<R>::type i(boost::begin(r)), end(boost::end(r)); for (; i != end; ++i) { i->second = v; } } /// applying function to corresponding pair through both ranges, min(r1.size(), r2,size()) applications template<typename R1, typename R2, typename F> void for_each2(R1& r1, R2& r2, const F& f) { boost::range_iterator<R1>::type i(boost::begin(r1)), i_end(boost::end(r1)); boost::range_iterator<R2>::type j(boost::begin(r2)), j_end(boost::end(r2)); for(;i != i_end && j != j_end; ++i, ++j) { f(*i, *j); } } /// applying function to corresponding pair through both ranges, min(r1.size(), r2,size()) applications template<typename R1, typename R2, typename R3, typename F> void for_each3(R1& r1, R2& r2, R3& r3, const F& f) { boost::range_iterator<R1>::type i(boost::begin(r1)), i_end(boost::end(r1)); boost::range_iterator<R2>::type j(boost::begin(r2)), j_end(boost::end(r2)); boost::range_iterator<R3>::type k(boost::begin(r3)), k_end(boost::end(r3)); for(;i != i_end && j != j_end && k != k_end; ++i, ++j, ++k) { f(*i, *j, *k); } } /// applying function to each possible permutation of objects, r1.size() * r2.size() applications template<class R1, class R2, class F> void for_each_permutation(R1 & r1, R2& r2, const F& f) { typedef boost::range_iterator<R1>::type R1_iterator; typedef boost::range_iterator<R2>::type R2_iterator; R1_iterator end_1 = boost::end(r1); R2_iterator begin_2 = boost::begin(r2); R2_iterator end_2 = boost::end(r2); for(R1_iterator i = boost::begin(r1); i != end_1; ++i) { for(R2_iterator j = begin_2; j != end_2; ++j) { f(*i, *j); } } } template <class R> inline boost::iterator_range<boost::indirect_iterator<typename boost::range_iterator<R>::type > > make_indirect_range(R& r) { return boost::iterator_range<boost::indirect_iterator<typename boost::range_iterator<R>::type > > (r); } template <class R, class F> inline boost::iterator_range<boost::transform_iterator<F, typename boost::range_iterator<R>::type> > make_transform_range(R& r, const F& f) { return boost::iterator_range<boost::transform_iterator<F, typename boost::range_iterator<R>::type> >( boost::make_transform_iterator(boost::begin(r), f), boost::make_transform_iterator(boost::end(r), f)); } template <class T> inline boost::iterator_range<boost::counting_iterator<T> > make_counting_range(T begin, T end) { return boost::iterator_range<boost::counting_iterator<T> >( boost::counting_iterator<T>(begin), boost::counting_iterator<T>(end)); } template <class R, class F> inline boost::iterator_range<boost::filter_iterator<F, typename boost::range_iterator<R>::type> > make_filter_range(R& r, const F& f) { return boost::iterator_range<boost::filter_iterator<F, typename boost::range_iterator<R>::type> >( boost::make_filter_iterator(f, boost::begin(r), boost::end(r)), boost::make_filter_iterator(f, boost::end(r), boost::end(r))); } namespace detail { template<class T> T* get_pointer(T& p) { return &p; } } /// compare member function/variable equal to value. Create using @ref mem_eq() to avoid specfying types template<class P, class V> struct mem_eq_type { mem_eq_type(const P& p, const V& v) : m_p(p), m_v(v) { } template<class T> bool operator()(const T& a) const { using boost::get_pointer; using orz::detail::get_pointer; return (get_pointer(a)->*m_p) == m_v; } P m_p; V m_v; }; template<class P, class V> mem_eq_type<P,V> mem_eq(const P& p, const V& v) { return mem_eq_type<P,V>(p, v); } /// helper macro to define function objects that compare member variables of a class #define ORZ_COMPARE_MEMBER(NAME, OP) \ template <class P> \ struct NAME##_type \ { \ NAME##_type(const P&p) : m_p(p) {} \ template<class T> \ bool operator()(const T& a, const T& b) const { \ return (a.*m_p) OP (b.*m_p); \ } \ P m_p; \ }; \ template <class P> \ NAME##_type<P> NAME(const P& p) { return NAME##_type<P>(p); } #define ORZ_COMPARE_MEMBER_FN(NAME, OP) \ template <class P> \ struct NAME##_type \ { \ NAME##_type(const P&p) : m_p(p) {} \ template<class T> \ bool operator()(const T& a, const T& b) const { \ return (a.*m_p)() OP (b.*m_p)(); \ } \ P m_p; \ }; \ template <class P> \ NAME##_type<P> NAME(const P& p) { return NAME##_type<P>(p); } /// helper macro to wrap range functions as function objects (value return) #define ORZ_RANGE_WRAP_VALUE_2(FUNC, RESULT) \ struct FUNC##_ \ { \ typedef RESULT result_type; \ template<typename R, typename F> \ inline RESULT operator() (R& r, const F& f) const \ { \ return FUNC(r, f); \ } \ }; /// helper macro to wrap range functions as function objects (void return) #define ORZ_RANGE_WRAP_VOID_2(FUNC) \ struct FUNC##_ \ { \ typedef void result_type; \ template<typename R, typename F> \ inline void operator() (R& r, const F& f) const \ { \ FUNC(r, f); \ } \ }; /// helper macro to wrap range functions as function objects (void return, one argument) #define ORZ_RANGE_WRAP_VOID_1(FUNC) \ struct FUNC##_ \ { \ typedef void result_type; \ template<typename R> \ inline void operator() (R& r) const \ { \ FUNC(r); \ } \ }; ORZ_RANGE_WRAP_VOID_2(for_each); ORZ_RANGE_WRAP_VOID_1(erase_all); ORZ_RANGE_WRAP_VALUE_2(contains, bool); ORZ_RANGE_WRAP_VALUE_2(contains_if, bool); ORZ_COMPARE_MEMBER(mem_equal, ==) ORZ_COMPARE_MEMBER(mem_not_equal, !=) ORZ_COMPARE_MEMBER(mem_less, <) ORZ_COMPARE_MEMBER(mem_greater, >) ORZ_COMPARE_MEMBER(mem_lessequal, <=) ORZ_COMPARE_MEMBER(mem_greaterequal, >=) ORZ_COMPARE_MEMBER_FN(mem_equal_fn, ==) ORZ_COMPARE_MEMBER_FN(mem_not_equal_fn, !=) ORZ_COMPARE_MEMBER_FN(mem_less_fn, <) ORZ_COMPARE_MEMBER_FN(mem_greater_fn, >) ORZ_COMPARE_MEMBER_FN(mem_lessequal_fn, <=) ORZ_COMPARE_MEMBER_FN(mem_greaterequal_fn, >=) #undef ORZ_COMPARE_MEMBER #undef ORZ_RANGE_WRAP_VALUE_2 #undef ORZ_RANGE_WRAP_VOID_1 #undef ORZ_RANGE_WRAP_VOID_2 }
I seem to need a Cartesian product, for example {A, B}, {1, 2} -> {(A,1), (A,2), (B,1), (B,2)}
// OutIt needs to be an iterator to a container of std::pair<Type1, Type2> template <typename InIt1, typename InIt2, typename OutIt> OutIt cartesian_product(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt out) { for (; first1 != last1; ++first1) for (InIt2 it = first2; it != last2; ++it) *out++ = std::make_pair(*first1, *it); return out; }
I would call such an append function by its name and would use operator+= , operator*= and so on for element-wise operations, such as:
template<typename X> inline void operator+= (std::vector<X>& vec1, const X& value) { std::transform( vec1.begin(), vec1.end(), vec1.begin(), std::bind2nd(std::plus<X>(),value) ); } template<typename X> inline void operator+= (std::vector<X>& vec1, const std::vector<X>& vec2) { std::transform( vec1.begin(), vec1.end(), vec2.begin(), vec1.begin(), std::plus<X>() ); }
some other simple and obvious wrappers as implied before:
template<typename X> inline void sort_and_unique(std::vector<X> &vec) { std::sort( vec.begin(), vec.end() ); vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() ); } template<typename X> inline void clear_vec(std::vector<X> &vec) { std::vector<X>().swap(vec); } template<typename X> inline void trim_vec(std::vector<X> &vec, std::size_t new_size) { if (new_size<vec.size()) std::vector<X>(vec.begin(),vec.begin() + new_size).swap(vec); else std::vector<X>(vec).swap(vec); }
Insert a new item and return it, useful for simple move semantics like push_back(c).swap(value)
and related cases.
template<class C> typename C::value_type& push_front(C& container) { container.push_front(typename C::value_type()); return container.front(); } template<class C> typename C::value_type& push_back(C& container) { container.push_back(typename C::value_type()); return container.back(); } template<class C> typename C::value_type& push_top(C& container) { container.push(typename C::value_type()); return container.top(); }
Pop and return an item:
template<class C> typename C::value_type pop_front(C& container) { typename C::value_type copy (container.front()); container.pop_front(); return copy; } template<class C> typename C::value_type pop_back(C& container) { typename C::value_type copy (container.back()); container.pop_back(); return copy; } template<class C> typename C::value_type pop_top(C& container) { typename C::value_type copy (container.top()); container.pop(); return copy; }
IMO there needs to be more functionality for pair
:
#ifndef pair_iterator_h_ #define pair_iterator_h_ #include <boost/iterator/transform_iterator.hpp> #include <functional> #include <utility> // pair<T1, T2> -> T1 template <typename PairType> struct PairGetFirst : public std::unary_function<PairType, typename PairType::first_type> { typename typename PairType::first_type& operator()(PairType& arg) const { return arg.first; } const typename PairType::first_type& operator()(const PairType& arg) const { return arg.first; } }; // pair<T1, T2> -> T2 template <typename PairType> struct PairGetSecond : public std::unary_function<PairType, typename PairType::second_type> { typename PairType::second_type& operator()(PairType& arg) const { return arg.second; } const typename PairType::second_type& operator()(const PairType& arg) const { return arg.second; } }; // iterator over pair<T1, T2> -> iterator over T1 template <typename Iter> boost::transform_iterator<PairGetFirst<typename std::iterator_traits<Iter>::value_type>, Iter> make_first_iterator(Iter i) { return boost::make_transform_iterator(i, PairGetFirst<typename std::iterator_traits<Iter>::value_type>()); } // iterator over pair<T1, T2> -> iterator over T2 template <typename Iter> boost::transform_iterator<PairGetSecond<typename std::iterator_traits<Iter>::value_type>, Iter> make_second_iterator(Iter i) { return boost::make_transform_iterator(i, PairGetSecond<typename std::iterator_traits<Iter>::value_type>()); } // T1 -> pair<T1, T2> template <typename FirstType, typename SecondType> class InsertIntoPair1st : public std::unary_function<FirstType, std::pair<FirstType, SecondType> > { public: InsertIntoPair1st(const SecondType& second_element) : second_(second_element) {} result_type operator()(const FirstType& first_element) { return result_type(first_element, second_); } private: SecondType second_; }; // T2 -> pair<T1, T2> template <typename FirstType, typename SecondType> class InsertIntoPair2nd : public std::unary_function<SecondType, std::pair<FirstType, SecondType> > { public: InsertIntoPair2nd(const FirstType& first_element) : first_(first_element) {} result_type operator()(const SecondType& second_element) { return result_type(first_, second_element); } private: FirstType first_; }; #endif // pair_iterator_h_
template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }
If you need to use a lot of functions that take pointer + number of bytes, it's always just
fun(vec.data(), bytesize(vec));
Duplicate a string with *:
std::string operator*(std::string s, size_t n) { std::stringstream ss; for (size_t i=0; i<n; i++) ss << s; return ss.str(); }
One of my favorite is the Transposer
that finds a transpose of a tuple of containers of the same size. That is, if you have a tuple<vector<int>,vector<float>>
, it converts it into a vector<tuple<int, float>>
. Comes handy in XML programming. 这是我如何做到的。
#include <iostream> #include <iterator> #include <vector> #include <list> #include <algorithm> #include <stdexcept> #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include <boost/type_traits.hpp> using namespace boost; template <class TupleOfVectors> struct GetTransposeTuple; template <> struct GetTransposeTuple<tuples::null_type> { typedef tuples::null_type type; }; template <class TupleOfVectors> struct GetTransposeTuple { typedef typename TupleOfVectors::head_type Head; typedef typename TupleOfVectors::tail_type Tail; typedef typename tuples::cons<typename remove_reference<Head>::type::value_type, typename GetTransposeTuple<Tail>::type> type; }; template <class TupleOfVectors, class ValueTypeTuple = typename GetTransposeTuple<TupleOfVectors>::type, unsigned int TUPLE_INDEX = 0> struct Transposer : Transposer <typename TupleOfVectors::tail_type, ValueTypeTuple, TUPLE_INDEX + 1> { typedef typename remove_reference<typename TupleOfVectors::head_type>::type HeadContainer; typedef typename TupleOfVectors::tail_type Tail; typedef Transposer<Tail, ValueTypeTuple, TUPLE_INDEX + 1> super; typedef std::vector<ValueTypeTuple> Transpose; Transposer(TupleOfVectors const & tuple) : super(tuple.get_tail()), head_container_(tuple.get_head()), head_iter_(head_container_.begin()) {} Transpose get_transpose () { Transpose tran; tran.reserve(head_container_.size()); for(typename HeadContainer::const_iterator iter = head_container_.begin(); iter != head_container_.end(); ++iter) { ValueTypeTuple vtuple; this->populate_tuple(vtuple); tran.push_back(vtuple); } return tran; } private: HeadContainer const & head_container_; typename HeadContainer::const_iterator head_iter_; protected: void populate_tuple(ValueTypeTuple & vtuple) { if(head_iter_ == head_container_.end()) throw std::runtime_error("Container bound exceeded."); else { vtuple.get<TUPLE_INDEX>() = *head_iter_++; super::populate_tuple (vtuple); } } }; template <class ValueTypeTuple, unsigned int INDEX> struct Transposer <tuples::null_type, ValueTypeTuple, INDEX> { void populate_tuple(ValueTypeTuple &) {} Transposer (tuples::null_type const &) {} }; template <class TupleOfVectors> typename Transposer<TupleOfVectors>::Transpose transpose (TupleOfVectors const & tupleofv) { return Transposer<TupleOfVectors>(tupleofv).get_transpose(); } int main (void) { typedef std::vector<int> Vint; typedef std::list<float> Lfloat; typedef std::vector<long> Vlong; Vint vint; Lfloat lfloat; Vlong vlong; std::generate_n(std::back_inserter(vint), 10, rand); std::generate_n(std::back_inserter(lfloat), 10, rand); std::generate_n(std::back_inserter(vlong), 10, rand); typedef tuples::tuple<Vint, Lfloat, Vlong> TupleOfV; typedef GetTransposeTuple<TupleOfV>::type TransposeTuple; Transposer<TupleOfV>::Transpose tran = transpose(make_tuple(vint, lfloat, vlong)); // Or alternatively to avoid copying // transpose(make_tuple(ref(vint), ref(lfloat), ref(vlong))); std::copy(tran.begin(), tran.end(), std::ostream_iterator<TransposeTuple>(std::cout, "\n")); return 0; }
Not sure if these qualify as std wrappers, but my commonly used helper functions are:
void split(string s, vector<string> parts, string delims); string join(vector<string>& parts, string delim); int find(T& array, const V& value); void assert(bool condition, string message); V clamp(V value, V minvalue, V maxvalue); string replace(string s, string from, string to); const char* stristr(const char* a,const char*b); string trim(string str); T::value_type& dyn(T& array,int index);
T and V here are template arguments. The last function works the same way as []-operator, but with automating resizing to fit needed index.
Similar to what people posted before, I have convenience overloads of algorithms for simplifying passing iterator arguments. I call algorithms like this:
for_each(iseq(vec), do_it());
I overloaded all the algorithms such that they take a single parameter of type input_sequence_range<>
instead of the two input iterators (input as in anything that isn't mere output).
template<typename In> struct input_sequence_range : public std::pair<In,In> { input_sequence_range(In first, In last) : std::pair<In,In>(first, last) { } };
And this is how iseq()
works:
template<typename C> input_sequence_range<typename C::const_iterator> iseq(const C& c) { return input_sequence_range<typename C::const_iterator>(c.begin(), c.end()); }
Similarly, I have specializations for
- const_iterators
- pointers (primitive arrays)
- stream iterators
- any range [begin,end) just for a uniform use: use iseq() for everything
Unordered erase for std::vector
. The most efficient way to erase an element from a vector
but it does not preserve the order of elements. I didn't see the point of extending it to other containers since most don't have the same penalty for removing items from the middle. It's similar to some other templates already posted but it uses std::swap
to move items instead of copying.
template<typename T> void unordered_erase(std::vector<T>& vec, const typename std::vector<T>::iterator& it) { if (it != vec.end()) // if vec is empty, begin() == end() { std::swap(vec.back(), *it); vec.pop_back(); } }
Signum returns the sign of a type. Returns -1
for negative, 0
for zero and 1
for positive.
template <typename T> int signum(T val) { return (val > T(0)) - (val < T(0)); }
Clamp is pretty self explanatory, it clamps a value so that it lies within the given range. It boggles my mind that the Standard Library includes min
and max
but not clamp
template<typename T> T clamp(const T& value, const T& lower, const T& upper) { return value < lower ? lower : (value > upper ? upper : value); }