sirve - ¿Qué funciones de contenedor de la biblioteca estándar de C++ usas?
librerias de dev c++ (26)
Envolver 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
El objetivo es desacoplar el formato de la salida sin tener problemas con sprintf y su tipo. No es bonito, pero es muy útil, especialmente si tus directrices de codificación prohíben iostreams.
Aquí hay una versión que asigna según sea necesario, de Neil Butterworth. [Ver el historial de revisión de la versión de Mike, que eliminé como un subconjunto de los otros dos. Es similar a la de Neil, excepto que este último es a prueba de excepciones mediante el uso de vector en lugar de eliminar []: el ctor de cadena lanzará en la falla de asignación. Mike también usa la misma técnica que se muestra más adelante para determinar el tamaño por adelantado. -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] );
}
Aquí hay una versión que determina el tamaño necesario por adelantado, de Roger Pate . Esto requiere std :: strings escribibles, que son proporcionados por implementaciones populares, pero son requeridos explícitamente por C ++ 0x. [Ver el historial de revisiones de la versión de Marcus, que eliminé porque es un poco diferente, pero básicamente es un subconjunto del siguiente. -RP]
Implementación
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);
}
Interfaz pública
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, ...);
Esta pregunta , hecha esta mañana, me hizo preguntarme qué características cree que faltan en la Biblioteca Estándar de C ++, y cómo ha ido llenando las lagunas con las funciones de envoltura. Por ejemplo, mi propia biblioteca de utilidades tiene esta función para vector append:
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;
}
y este para borrar (más o menos) cualquier tipo, particularmente útil para cosas como std :: stack:
template <class C>
void Clear( C & c ) {
c = C();
}
Tengo algunos más, pero estoy interesado en cuáles usar? Limite las respuestas a las funciones del contenedor , es decir, no más de un par de líneas de código.
A menudo uso el vector como un conjunto de elementos sin ningún orden en particular (y, obviamente, cuando no necesito verificaciones rápidas es-este-elemento-en-el-conjunto). En estos casos, llamar a erase () es una pérdida de tiempo ya que reordenará los elementos y no me importa el orden. Ahí es cuando la función O (1) a continuación es útil: simplemente mueva el último elemento en la posición del que desea eliminar:
template<typename T>
void erase_unordered(std::vector<T>& v, size_t index)
{
v[index] = v.back();
v.pop_back();
}
A veces siento que estoy en el begin()
y el end()
infierno. Me gustaría tener algunas funciones como:
template<typename T>
void sort(T& x)
{
std::sort(x.begin(), x.end());
}
y otros similares para std::find
, std::for_each
, y básicamente todos los algoritmos STL.
Creo que sort(x)
es mucho más rápido de leer que de sort(x.begin(), x.end())
.
Aquí está mi conjunto de utilidades extra, construido sobre un contenedor boost-stock''ish std-algo que podría necesitar para algunas funciones. (Eso es trivial de escribir, esta es la cosa interesante)
#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
}
Definitivamente boost::addressof
El infame algoritmo de erase
faltante:
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
La función de utilidad en la caja de herramientas de everyones es por supuesto copy_if
. Aunque no es realmente una envoltura.
Otro ayudante que uso comúnmente es el deleter
, un funtor que uso con std::for_each
para eliminar todos los punteros en un contenedor.
[edit] Explorando mi "sth.h" También encontré el vector<wstring> StringSplit(wstring const&, wchar_t);
La utilidad is_sorted
, para probar contenedores antes de aplicar algoritmos como include
que esperan una entrada ordenada:
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
Sí, lo sé, sería mejor negar el predicado y usar la versión de predicado de adjacent_find
:)
Mirando mi stl_util.h
, muchos de los clásicos (funciones de eliminación, copy_if
), y también este (probablemente también bastante común, pero no lo veo dado en las respuestas hasta el momento) para buscar a través de un mapa y regresar ya sea el valor encontrado o predeterminado, ala get
en el dict
de Python:
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;
}
El uso del valor null_result
predeterminado de una V
construida por defecto es muy similar al comportamiento del operator[]
de std::map
operator[]
, pero esto es útil cuando el mapa es const (común para mí), o si el valor predeterminado es V no es lo correcto para usar
Tengo un encabezado que pone lo siguiente en el espacio de nombres "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;
}
Ya no uso este tanto, pero solía ser un elemento básico:
template<typename T>
std::string make_string(const T& data) {
std::ostringstream stream;
stream << data;
return stream.str();
}
Se actualizará con más según los recuerdo. :PAG
contiene (contenedor, val) (bastante simple, pero conveniente).
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 (begin, end, value)
Una versión más rápida de std :: remove con la excepción de que no conserva el orden de los objetos restantes.
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;
}
(en el caso de un vector de tipos de vainas (int, float, etc.) y se eliminan casi todos los objetos, std :: remove puede ser más rápido).
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();
}
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);
}
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_
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;
}
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.
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. Here is how I did it.
#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;
}
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
Borrado desordenado para std::vector
. La forma más eficiente de borrar un elemento de a, vector
pero no conserva el orden de los elementos. No veo el sentido de extenderlo a otros contenedores, ya que la mayoría no tienen la misma penalización por eliminar elementos del medio. Es similar a algunas otras plantillas ya publicadas pero se usa std::swap
para mover elementos en lugar de copiar.
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 devuelve el signo de un tipo. Devuelve -1
para negativo, 0
para cero y 1
para positivo.
template <typename T>
int signum(T val)
{
return (val > T(0)) - (val < T(0));
}
La abrazadera se explica por sí misma, sujeta un valor para que se encuentre dentro del rango dado. Se perturba mi mente que la biblioteca estándar incluye min
y max
aunque noclamp
template<typename T>
T clamp(const T& value, const T& lower, const T& upper)
{
return value < lower ? lower : (value > upper ? upper : value);
}
//! /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);
}
}
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&);
};
Ok, ya que parece que esto no es tan directo como pensé, aquí hay una explicación:
En su constructor temp_value
almacena una referencia a una variable y una copia del valor original de la variable. En su destructor, restaura la variable referenciada a su valor original. Entonces, no importa lo que le haya hecho a la variable entre la construcción y la destrucción, se reiniciará cuando el objeto temp_value
se salga del alcance.
Úselo así:
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
}
Aquí hay otro enfoque que usa el truco de alcance-guardia:
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);
}
Es solo un poco más código de placa de caldera, pero permite un uso más limpio:
#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);
}
Sin embargo, elimina su capacidad de ser utilizado en una clase.
Aquí hay una tercera forma de lograr el mismo efecto (que no sufre los problemas de potencialmente lanzar destructores):
Implementación:
//none -- it is built into the language
Uso:
#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);
}
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));
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;}