Какие функции оболочки стандартной библиотеки C ++ вы используете? - PullRequest
81 голосов
/ 31 марта 2010

Этот вопрос , заданный сегодня утром, заставил меня задуматься о том, какие функции, по вашему мнению, отсутствуют в стандартной библиотеке C ++, и как вы справились с заполнением пробелов функциями-обертками. Например, моя собственная служебная библиотека имеет эту функцию для векторного добавления:

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

и этот для очистки (более или менее) любого типа - особенно полезен для таких вещей, как std :: stack:

template <class C>
void Clear( C & c ) {
    c = C();
}

У меня есть еще несколько, но меня интересуют, какие вы используете? Пожалуйста, ограничьте ответы функциями wrapper - т.е. не более пары строк кода.

Ответы [ 26 ]

7 голосов
/ 31 марта 2010

Упаковка спринтф

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.


Вот версия, которая выделяется по мере необходимости, от Нила Баттерворта. [Просмотр истории изменений для версии Майка, которую я удалил как подмножество оставшихся двух. Он похож на Neil, за исключением того, что последний безопасен для исключительных ситуаций, используя vector вместо delete []: ctor строки выдаст ошибку при выделении. Майк также использует ту же технику, показанную позже, чтобы определить размер заранее. & Ndash; 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, которые предоставляются популярными реализациями, но явно требуются в C ++ 0x. [Просмотр истории изменений для версии Маркуса, которую я удалил, поскольку она немного отличается, но, по сути, представляет собой подмножество нижеприведенного. & Ndash; 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, ...);
6 голосов
/ 31 марта 2010

Утилита is_sorted для проверки контейнеров перед применением таких алгоритмов, как include, которые ожидают отсортированную запись:

  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:)

3 голосов
/ 31 марта 2010

Вот мой набор дополнительных утилит, построенных на основе оболочки std-algo boost.range'ish, которые могут вам понадобиться для некоторых функций.(написать тривиально, это интересный материал)

#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
}
3 голосов
/ 31 марта 2010
//! \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);
    }
}
3 голосов
/ 31 марта 2010

Определенно boost :: addressof

3 голосов
/ 31 марта 2010

Глядя на мои stl_util.h, многие классики (функции удаления, copy_if), а также эту (вероятно, также довольно распространенную, но я пока не вижу ее в ответах) для поиска по карта и возвращает либо найденное значение, либо значение по умолчанию, ala get в Python's 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;
   }

Использование по умолчанию null_result сконструированного по умолчанию V во многом аналогично поведению std::map operator[], но это полезно, когда карта является константной (обычной для меня), или если построенный по умолчанию V не подходит для использования.

3 голосов
/ 21 апреля 2010

Мне, кажется, нужен декартово произведение, например {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;
}
2 голосов
/ 31 марта 2010

Я бы назвал такую ​​функцию добавления по имени и использовал бы оператор + =, оператор * = и так далее для поэлементных операций, таких как:

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

некоторые другие простые и очевидные обертки, как подразумевалось ранее:

    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);
    }
1 голос
/ 07 марта 2014

Дублируйте строку с *:

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();
}
1 голос
/ 06 марта 2014
template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }

Если вам нужно использовать множество функций, которые принимают указатель + количество байтов, это всегда просто

fun(vec.data(), bytesize(vec));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...