Welche C ++ Standard Library Wrapper-Funktionen verwenden Sie?

81

Diese Frage , die heute Morgen gestellt wurde, hat mich gefragt, welche Funktionen Ihrer Meinung nach in der C ++ - Standardbibliothek fehlen und wie Sie die Lücken mit Wrapper-Funktionen gefüllt haben. Zum Beispiel hat meine eigene Dienstprogrammbibliothek diese Funktion zum Vektoranhängen:

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

und dieses zum Löschen (mehr oder weniger) jedes Typs - besonders nützlich für Dinge wie std :: stack:

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

Ich habe noch ein paar mehr, aber ich bin daran interessiert, welche Sie verwenden? Bitte beschränken Sie die Antworten auf Wrapper- Funktionen, dh nicht mehr als ein paar Codezeilen.

anon
quelle
9
Zählt es, dass ich den größten Teil des STL-Algorithmus so verpackt habe, dass er auf ganze Container anstatt auf Bereiche wirkt, nur weil das Durcheinander mit den Iteratoren ein so häufiger Fehler ist :)?
Matthieu M.
2
@ Billy eigentlich ist CW keine Entschuldigung dafür, subjektive Fragen zu stellen. Ich werde den Titel ändern, was die Leute glücklich machen soll.
4
@kts: Da vector :: insert Iteratoren mit wahlfreiem Zugriff erhält, verwendet eine gute Implementierung den Versand zur Kompilierungszeit, um dies selbst zu tun.
4
Ist es nicht besser zu schreiben c.swap(C()), um einen Container zu löschen?
Alexandre C.
5
@Alexandre: Das ist nicht erlaubt: Es bindet eine temporäre an eine nicht konstante Referenz. C (). Swap (c) würde funktionieren.

Antworten:

37

boost :: array

enthält (container, val) (ganz einfach, aber bequem).

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 (Anfang, Ende, Wert)

Eine schnellere Version von std :: remove mit der Ausnahme, dass die Reihenfolge der verbleibenden Objekte nicht beibehalten wird.

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

(Im Fall eines Vektors von Pod-Typen (int, float usw.) und fast allen Objekten, die entfernt werden, ist std :: remove möglicherweise schneller).

Viktor Sehr
quelle
4
Hat jemand denken , dass enthält Bedarf eine dritte Vorlage ( bool sorted=false) und eine Spezialisierung bei sorted==trueanrufen binary_searchstatt find?
KitsuneYMG
5
@kts: Wenn Sie wissen, dass der Container sortiert ist, rufen Sie einfach binary_search direkt auf.
2
Boost :: Array STL-Äquivalent ist im tr1-Namespace auf den neuesten Compilern (sogar Codewarrior) verfügbar: std :: tr1 :: array <>
Klaim
36

Sehr oft würde ich Vektor als eine Reihe von Elementen in keiner bestimmten Reihenfolge verwenden (und natürlich, wenn ich keine schnellen Überprüfungen dieses Elements in der Menge benötige). In diesen Fällen ist das Aufrufen von erase () Zeitverschwendung, da dadurch die Elemente neu angeordnet werden und mir die Reihenfolge egal ist. Dann ist die O (1) -Funktion nützlich - verschieben Sie einfach das letzte Element an die Position des Elements, das Sie löschen möchten:

template<typename T>
void erase_unordered(std::vector<T>& v, size_t index)
{
    v[index] = v.back();
    v.pop_back();
}
sbk
quelle
Gut. Hatte nicht darüber nachgedacht, es so zu machen .. :) Es sollte einen 'Bag'-Wrapper (ähnlich wie Stack und Queue) geben, der diese Operationen beschleunigt, wenn die Reihenfolge nicht wichtig ist.
Macke
12
Und in C ++ 0x v[index] = st::move(v.back()); v.pop_back();ist es ungefähr so ​​effizient wie es nur geht.
GManNickG
@Matthieu: Schau dir den Zeitstempel auf diesen an. : PI bemerkte ein paar Stunden später, weit nach der erlaubten Bearbeitungszeit.
GManNickG
@ GMan: Bist du dir da sicher? sieht für mich so aus, als ob v.pop_back () zu undefiniertem Verhalten führen könnte, wenn der Destruktor aufgerufen wird.
Viktor Sehr
1
@Viktor: Verschiebungssemantik bedeutet nicht "Ich nehme deine Ressourcen und das ist es", sondern "Ich nehme deine Ressourcen und versetze dich in einen Zustand ohne Ressourcen". Mit anderen Worten, Ihre Bewegungssemantik muss sicherstellen, dass das Objekt sicher zerstört werden kann. ziemlich einfach zu machen, setze einfach Zeiger auf null.
GManNickG
26
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, da dies anscheinend nicht so einfach ist, wie ich dachte, folgt eine Erklärung:
In seinem Konstruktor wird temp_valueein Verweis auf eine Variable und eine Kopie des ursprünglichen Werts der Variablen gespeichert. In seinem Destruktor wird die referenzierte Variable auf ihren ursprünglichen Wert zurückgesetzt. Unabhängig davon, was Sie mit der Variablen zwischen Konstruktion und Zerstörung gemacht haben, wird sie zurückgesetzt, wenn das temp_valueObjekt den Gültigkeitsbereich verlässt.
Verwenden Sie es so:

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
}

Hier ist ein weiterer Ansatz, der den Scope-Guard-Trick verwendet:

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 ist nur ein bisschen mehr Code für die Kesselplatte, ermöglicht aber eine sauberere Verwendung:

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

Es entfernt jedoch seine Fähigkeit, in einer Klasse verwendet zu werden.


Hier ist ein dritter Weg, um den gleichen Effekt zu erzielen (der nicht unter den Problemen des potenziellen Werfens von Destruktoren leidet):

Implementierung:

//none -- it is built into the language

Verwendung:

#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);
}
GManNickG
quelle
1
@ Billy: Es dient dazu, den Wert später automatisch wiederherzustellen. (Und val sollte aus dem ctor eliminiert werden.)
Entschuldigung, ich bin immer noch verloren (ich bin neu in C ++). Kann jemand es richtig dumm machen?
Dreamlax
1
@dreamlax: Ich habe der Antwort einen beschreibenden Text hinzugefügt, der ihn beschreibt. Ist es jetzt verständlich oder sollte ich tiefer in die Details gehen?
sbi
1
Oh, der zweite ist ziemlich schlau.
Jalf
1
Hmm, was ist ein realer Anwendungsfall dafür?
Paulm
22

Nicht wirklich ein Wrapper, aber der berüchtigte Vermisste copy_if. Von hier aus

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;
}
Glen
quelle
2
Beantwortet die Frage nicht, kein Wrapper für die stdlib.
10
@ Roger Pate, ja ich weiß, deshalb beginnt die Antwort mit den Worten "Nicht wirklich ein Wrapper, aber ...".
Glen
1
@ Roger Implementierungsdetail. Wenn Sie es wirklich wünschen, können Sie es in Bezug auf implementieren remove_copy_if(). : p
wilhelmtell
18
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;}
sbi
quelle
2
Ich habe diese auch. :) +1 Für das, was es wert ist, brauchst du nur zwei (lass die const-Versionen hinter dir). Wenn das Array const ist, Twird const Uund Sie erhalten die beabsichtigte Funktion.
GManNickG
@GMan: Es gab eine Version von GCC, die keinen Code nur mit den Nichtversionen kompilierte. constDeshalb constgibt es die Versionen. Da dies möglicherweise ein Fehler dieser bestimmten GCC-Version war, werde ich sie entfernen.
sbi
1
@Marcus: Diese sind viel älter als Boost.Range. :)
sbi
4
@ Roger: Es umschließt Arrays, damit sie mit der Standardbibliothek verwendet werden können. Los geht's. :)
sbi
2
@Stacked, @sbe: Arrays werden niemals als Wert übergeben, unabhängig davon, ob es einen gibt oder nicht &. Das &ist da, um den Typabzug der Länge des Arrays zu ermöglichen.
Mankarse
12

Manchmal fühle ich mich wie in der ich bin begin()und end()Hölle. Ich hätte gerne einige Funktionen wie:

template<typename T>
void sort(T& x)
{
    std::sort(x.begin(), x.end());
}

und andere ähnliche für std::find, std::for_eachund im Grunde alle STL-Algorithmen.

Ich denke, das sort(x)ist viel schneller zu lesen / verstehen als sort(x.begin(), x.end()).

Invers
quelle
1
Hinweis; Verwenden Sie stattdessen ´sort (boost :: begin (x), boost: end (x)); ´, und Sie können auch Arrays sortieren.
Viktor Sehr
4
Boost.Range v2 verfügt über solche Adapter für die gesamte Standardbibliothek.
ildjarn
9

Ich benutze dieses nicht mehr annähernd so oft, aber es war früher ein Grundnahrungsmittel:

template<typename T>
std::string make_string(const T& data) {
    std::ostringstream stream;
    stream << data;
    return stream.str();
}

Wird mit mehr aktualisiert, wenn ich mich an sie erinnere. : P.

Jon Purdy
quelle
3
Hehe - eine Art Abkürzung für boost::lexical_cast<t, t>.
Billy ONeal
1
Ja, das ist großartig, wenn Sie keinen Schub in ein Projekt bringen wollen
Steve
11
@ BillyONeal: Das ist der Grund, warum ich es nicht mehr benutze. @Steve: Das ist der Grund, warum ich es immer noch benutze.
Jon Purdy
Es wäre unnötig teuer, es auf a char*oder a zu nennen std::string. Vielleicht ist eine Vorlagenspezialisierung angebracht?
Wilhelmtell
Wenn ich mich richtig erinnere, boost::lexical_castgibt es eine Reihe solcher Spezialisierungen und Fehlerprüfungen. Um die ungerade Zahl zu kennzeichnen, funktioniert dies jedoch einwandfrei.
Jon Purdy
9

Die Utility-Funktion in jeder Toolbox ist natürlich copy_if. Nicht wirklich ein Wrapper.

Ein anderer Helfer, den ich normalerweise benutze, ist deleterein Funktor, mit dem ich std::for_eachalle Zeiger in einem Container lösche.

[Bearbeiten] Durch meine "etw.h" graben fand ich auch vector<wstring> StringSplit(wstring const&, wchar_t);

MSalters
quelle
+1 für Deleter-Funktoren. Mein Deleter-Funktor funktioniert gut mit den meisten Containern, aber ich habe herumgespielt, damit er mit std :: map funktioniert, bei denen entweder der Schlüssel oder der Wert ein Zeiger ist. Ich habe versucht, das Merkmal mithilfe von Typmerkmalen zu lösen, bin aber aus zeitlichen Gründen nicht wirklich weit gekommen. Haben Sie dieses Problem gelöst oder betrachten Sie es als Problem?
Glen
2
@Matthieu M. Leider können nicht alle von uns Boost verwenden.
Glen
@Glen: All diese Probleme können mit intelligenten Zeigern gelöst werden, vorzugsweise (aber nicht unbedingt) durch Boost. Als wichtiger Nebeneffekt werden Container mit Zeigern auf dynamisch zugewiesene Objekte plötzlich auch ausnahmesicher.
sbi
@Glen hier, hier für Projekte, die keine anderen Bibliotheken als die STD bis einschließlich Boost oder TR1 vorschreiben.
Wheaties
7
@die unglücklichen Opfer von Unternehmensdummheit: Holen Sie sich eine Ausnahme für interne Bibliotheken und importieren Sie dann die nützlichen Teile von Boost in die interne Bibliothek. Auch in der Politik können alle Probleme mit einer anderen Indirektionsebene gelöst werden.
MSalters
9

Ich habe einen Header, der Folgendes in den Namespace "util" einfügt:

// 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 &ltrim(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;
}
Evan Teran
quelle
2
Das split()Schwalben auftretende Fehler in std::getline()stumm einen Vektor Rückkehr , die zu kurz ist.
sbi
Natürlich sollten Sie das size()Ergebnis überprüfen, bevor Sie Ihre Zeichenfolgen abrufen.
Evan Teran
Und wie würde ich wissen, wie viele Zeichenfolgen das Ergebnis haben sollte?
sbi
2
@sbi: Dein Kommentar hat mein Interesse geweckt, was mit der stringstream/ getline-Schleife tatsächlich schief gehen könnte (abgesehen davon, dass die Zeichenfolge einfach nicht genug Token hat) . Ich habe hier eine Frage dazu gestellt: stackoverflow.com/questions/2562906/…
Evan Teran
2
@Evan: Ich stehe korrigiert. Siehe meinen Kommentar unter stackoverflow.com/2563542#2563542 . Es tut uns leid.
sbi
8

Der infamös fehlende eraseAlgorithmus:

  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
Matthieu M.
quelle
+1, war dabei, mein genaues Äquivalent zu veröffentlichen. Obwohl ich es remove_erase (...) genannt habe
Viktor Sehr
2
Das einzige Problem dabei ist, dass es die in der STL erwartete semantische Lösch-Entfernungs-Sprache bricht. Sie benötigen das Lösch-Entfernungs-Idiom für jeden Algorithmus, der eine Entfernungssemantik aufweist - nicht nur std::remove. Zum Beispiel std::unique.
Billy ONeal
Nun, ich habe Container-Anpassungen der meisten STL-Algorithmen dafür :) Aber dies ist die, die ich am häufigsten benutze, normalerweise, wenn ich Einzigartigkeit will, verwende ich zunächst eine set.
Matthieu M.
@Matthieu M.: Denken Sie daran, dass Sie auf diese Weise Menschen haben, die ständig mit der STL arbeiten und Alarmglocken in ihren Köpfen läuten. . Es ist wirklich nichts Falsches daran, aber ich würde es nicht tun, wenn ich meinen Code unter vielen Programmierern teilen müsste. Nur meine 2 Cent.
Billy ONeal
1
@ Billy: Deshalb habe ich es Löschen und nicht Entfernen genannt. Abgesehen davon kann ich nicht viel tun, außer dass sie den Code konsultieren. Zum Glück mit moderner IDE ist die Definition nur einen Klick entfernt :)
Matthieu M.
7

Sprintf einwickeln

string example = function("<li value='%d'>Buffer at: 0x%08X</li>", 42, &some_obj);
// 'function' is one of the functions below: Format or stringf

Das Ziel ist es, die Formatierung von der Ausgabe zu entkoppeln, ohne Probleme mit sprintf und seiner Art zu bekommen. Es ist nicht schön, aber sehr nützlich, insbesondere wenn Ihre Codierungsrichtlinien iostreams verbieten.


Hier ist eine Version von Neil Butterworth, die nach Bedarf zugewiesen wird. [Revisionsverlauf für Mikes Version anzeigen, die ich als Teilmenge der verbleibenden zwei entfernt habe. Es ähnelt dem von Neil, außer dass letzteres ausnahmesicher ist, indem anstelle von delete [] ein Vektor verwendet wird: Der ctor des Strings löst einen Zuordnungsfehler aus. Mike verwendet auch die später gezeigte Technik, um die Größe im Voraus zu bestimmen. –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] );
}

Hier ist eine Version von Roger Pate, die die benötigte Größe im Voraus bestimmt . Dies erfordert beschreibbare std :: -Strings, die von gängigen Implementierungen bereitgestellt werden, von C ++ 0x jedoch explizit benötigt werden. [Revisionsverlauf für Marcus 'Version anzeigen, die ich entfernt habe, da sie etwas anders ist, aber im Wesentlichen eine Teilmenge der folgenden. –RP]

Implementierung

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

Öffentliche Schnittstelle

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, ...);
Roger Pate
quelle
@Neil: Von man vsnprintf: "Diese Funktionen geben die Anzahl der gedruckten Zeichen zurück ... oder einen negativen Wert, wenn ein Ausgabefehler auftritt, mit Ausnahme von snprintf()und vsnprintf(), die die Anzahl der Zeichen zurückgeben, die gedruckt worden wären, wenn die n unbegrenzt wären ... "Daher der Dummy-Aufruf mit einem 0-Puffer, um die benötigte Puffergröße zu messen.
Mike DeSimone
@ Checker: Ah, Boost. So großes Potenzial, dass sie mich auch nicht nutzen lassen. Hoffentlich eines Tages. Wie auch immer, ist Boost groß genug geworden, um noch nicht vollständig verstanden zu werden? Ich würde mich freuen, nur zu bekommen boost::spirit.
Mike DeSimone
Dies ist eigentlich Windows-Code - von MSDN "Wenn für _vsnprintf die Anzahl der zu schreibenden Bytes den Puffer überschreitet, werden die Anzahl der Bytes geschrieben und –1 zurückgegeben." aber ich portiere es auf Linux. Ich kann mich nicht erinnern, ob meine Linux-App dies tatsächlich verwendet oder ob ich es dort auf große Puffergrößen getestet habe - muss dies tun. Vielen Dank.
1
Wenn Sie ohnehin Windows-Code verwenden _vscprintf, bestimmen Sie die erforderliche Größe des Puffers.
Smerlin
6

Das is_sortedDienstprogramm zum Testen von Containern vor dem Anwenden von Algorithmen, includedie einen sortierten Eintrag erwarten:

  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

Ja, ich weiß, es wäre besser, das Prädikat zu negieren und die Prädikatversion von adjacent_find:) zu verwenden.

Matthieu M.
quelle
1
solange du den test nur in einem assert(): p
wilhelmtell
Sie sollten die ungarische Notation nicht verwenden.
the_drow
2
@the_drow: Vielen Dank für diesen hilfreichen Kommentar :) Ich bin nicht so begeistert davon, aber es ist eine Voraussetzung, wo ich arbeite ... Ich habe seitdem die Gewohnheit abgeschüttelt, mach dir keine Sorgen um meine Seele;)
Matthieu M.
3
//! \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);
    }
}
sbi
quelle
Ich benutze lieber die Boost.BimapBibliothek (oder Boost.MultiIndexfür komplexere Situationen)
Matthieu M.
1
Ich verstehe es nicht, warum die Behauptung ()?
Viktor Sehr
@Viktor: um sicherzustellen, dass keine doppelten Schlüssel vorhanden sind reverse_map. Betrachten Sie maphat (1 -> "eins"; 2 -> "eins") reverse_maperhält ein Element ("eins" -> 1). Die Behauptung wird das fangen. Siehe auch: bijection
sbk
3
Übrigens, sbi, wenn Code mit Nebenwirkungen in einer assert () vorhanden ist, werden Sie ziemlich schlecht gebissen, wenn Sie mit NDEBUG kompilieren und assert () s vollständig entfernt werden.
sbk
2
gah, nach dem Update sieht mein erster Kommentar wirklich dumm aus, Stackoverflow ist die Nummer 1 beim googeln meines Namens, also hoffe ich, dass kein zukünftiger Arbeitgeber dies sieht =)
Viktor Sehr
3

Ein Blick auf meine stl_util.h, viele der Klassiker (deleter Funktionen copy_if), und auch diese ein (wahrscheinlich auch durchaus üblich, aber ich sehe es in den Antworten bisher nicht gegeben) für die Suche durch eine Karte und Rückkehr entweder den gefundenen Wert oder ein Standard, ala getin Pythons 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;
   }

Die Verwendung der Standardeinstellung null_resulteiner Standardkonstruktion entspricht Vweitgehend dem Verhalten von std::map's operator[], ist jedoch nützlich, wenn die Zuordnung const ist (für mich üblich) oder wenn die Standardkonstruktion V nicht die richtige Verwendung ist.

Jack Lloyd
quelle
Was ist, wenn V ein int oder float oder ein anderes Grundelement ist?
Inverse
Die leere Wertinitialisierung funktioniert für Basistypen in C ++. Für ganze Zahlen und Gleitkommazahlen würde dies den Standardwert null_result 0 ergeben.
Jack Lloyd
3

Hier ist mein Satz von Extra-Utils, die auf einem boost.range'ish std-algo-Wrapper basieren, den Sie möglicherweise für einige Funktionen benötigen. (das ist trivial zu schreiben, das ist das interessante Zeug)

#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
}
Macke
quelle
+1 für for_each_permutation (...) , hauptsächlich weil ich einen ähnlichen Wrapper geschrieben habe =). Aber warum gibt erase_duplicates (...) ein signiertes int zurück?
Viktor Sehr
Hallo Viktor! Du hättest for_each_permutation auch bei deinem vorherigen Job sehen sollen , denke ich. ;) erase_duplicates gibt die Anzahl der gelöschten Elemente zurück, was für die Protokollierung und das Debuggen hilfreich ist.
Macke
Hmm hat es vielleicht durchgesehen und nicht bemerkt, was es tut ;) Ich verstehe trotzdem, warum es eine Ganzzahl zurückgibt. Ich verstehe nur nicht, warum die Ganzzahl signiert ist (oder genauer gesagt, warum ist sie nicht vorzeichenlos )?
Viktor Sehr
Ah. Nur Faulheit meinerseits :-P. size_t ist der entsprechende Typ.
Macke
3

Ich brauche anscheinend ein kartesisches Produkt, zum Beispiel {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;
}
rlbond
quelle
Beachten Sie, dass InIt2 ein Vorwärtsiterator anstelle eines Eingabeiterators sein muss . Eingabe-Iteratoren sind nicht für mehrere Durchgänge geeignet.
2

Ich würde eine solche Append-Funktion beim Namen nennen und Operator + =, Operator * = usw. für elementweise Operationen verwenden, wie zum Beispiel:

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

einige andere einfache und offensichtliche Wrapper, wie zuvor impliziert:

    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);
    }
Däne
quelle
7
Diese Operatoren sind ein sehr gutes Beispiel dafür, warum das Überladen von Operatoren selten erfolgen sollte. Ich hätte gedacht, vec+=valwürde einen Wert an den Vektor anhängen . (Siehe stackoverflow.com/questions/2551775/. ) Nachdem ich Ihre Implementierung gesehen habe, denke ich, dass dies eine ebenso richtige Interpretation der Bedeutung von +=ist. Ich würde nicht wissen , was man richtig oder falsch wäre, so dass es wahrscheinlich ist genau so gut , dass wir nicht haben , +=für std::vector.
sbi
1
@sbi Ich stimme zu. Mir operator+()fehlt ein erstaunlicher früher Einblick in den Standard. Normalerweise erwarte ich überall dort, wo ich den Plus-Operator sehe, eine O (1) -Operation. C ++ macht Dinge, die teuer oder gefährlich sind, ausführlicher oder schwieriger, und ich mag es so. Schauen Sie sich Java an: Einer der schlimmsten Codierungsfehler ist der Missbrauch des Plus-Operators. Andererseits macht C ++ nicht immer billige und schnelle Dinge einfach, aber hey. Gute C ++ - Programmierer sind sehr leistungsbewusst. ;)
wilhelmtell
2
Ich stimme Ihnen beiden zu, dass op+()dies aufgrund seiner Mehrdeutigkeit überhaupt nicht definiert werden sollte. Vektoren sind jedoch normalerweise Teil eines (mathematischen) Vektorraums, und es gibt eine kanonische Definition für das Hinzufügen von zwei Vektoren und die Skalarmultiplikation. Um Ihr Argument weiter zu führen: Ein einfaches doubleist auch ein Vektor. Wenn Sie also zwei doubleVariablen wie hinzufügen, a+berwarten Sie eine neue doubleund keine pairdoppelte (a,b). Das Multiplizieren mit einem Skalar ist ebenfalls kanonisch, das Multiplizieren von zwei Vektoren jedoch nicht. Das Überladen sollte also mit Vorsicht erfolgen.
Dane
1

Fügen Sie ein neues Element ein und geben Sie es zurück. Dies ist nützlich für einfache Verschiebungssemantiken wie push_back(c).swap(value)und verwandte Fälle.

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 und Rückgabe eines Artikels:

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;
}
Roger Pate
quelle
1

IMO muss es mehr Funktionen geben für 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_
rlbond
quelle
1
Warum nicht die PairTypeVorlage nach operator () verschieben? Außerdem sind die doppelten Unterstriche in der Kennung reserviert.
GManNickG
@GMan - Weil du dann nicht verwenden kannst unary_function, was ich irgendwann in meinem Code brauche. Was die doppelten Unterstriche betrifft, danke, dass Sie mich informiert haben - das muss ich ändern.
Rlbond
Dies verwendet abhängige Namen (argument_type, result_type) falsch, und Compiler müssen sie ablehnen. "Wenn bei der Definition einer Klassenvorlage oder eines Mitglieds einer Klassenvorlage eine Basisklasse der Klassenvorlage von einem Vorlagenparameter abhängt, wird der Bereich der Basisklasse auch bei der Suche nach unqualifizierten Namen zum Zeitpunkt der Definition der nicht überprüft Klassenvorlage oder Mitglied oder während einer Instanziierung der Klassenvorlage oder des Mitglieds. " [14.6.2 / 3, C ++ 03]
@ Roger Pate: Diese Regel war mir nicht bekannt. Es ist jetzt behoben.
Rlbond
1
template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }

Wenn Sie viele Funktionen verwenden müssen, die Zeiger + Anzahl der Bytes benötigen, ist dies immer gerecht

fun(vec.data(), bytesize(vec));
hrehfeld
quelle
1

Dupliziere einen String mit *:

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

Einer meiner Favoriten ist der Transposer, der eine Transponierte eines Tupels von Behältern derselben Größe findet. Das heißt, wenn Sie ein haben tuple<vector<int>,vector<float>>, konvertiert es es in ein vector<tuple<int, float>>. Praktisch in der XML-Programmierung. Hier ist, wie ich es gemacht habe.

#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;
}
Sumant
quelle
0

Ich bin mir nicht sicher, ob diese als Standard-Wrapper qualifiziert sind, aber meine häufig verwendeten Hilfsfunktionen sind:

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 und V sind hier Vorlagenargumente. Die letzte Funktion funktioniert genauso wie der [] -Operator, jedoch mit der Automatisierung der Größenänderung, um sie an den erforderlichen Index anzupassen.

AareP
quelle
1
Der Name 'assert' wird (in allen Bereichen) von der Standardbibliothek für das Makro dieses Namens reserviert.
1
Ich denke, es gibt auch ein assert () -Makro, das in Windows- oder MFC-Headern deklariert ist. Beide schlagen im WM_PAINT-Ereignis fehl, da das Anzeigen des Bestätigungsdialogs in einigen Fällen die nächste Assestion auslöst. Am Ende ist es also keine große Sache, diese fehlerhaften Implementierungen durch eine dritte zu ersetzen. Alles, was Sie tun müssen, ist, das eigene Assert-Makro nach #include <assert> explizit neu zu deklarieren oder einfach #undef assert zu verwenden.
AareP
0

Ähnlich wie bei den zuvor veröffentlichten Beiträgen habe ich eine Vielzahl von Algorithmen zur Vereinfachung der Übergabe von Iteratorargumenten. Ich nenne Algorithmen wie diese:

for_each(iseq(vec), do_it());

Ich habe alle Algorithmen so überladen, dass sie input_sequence_range<>anstelle der beiden Eingabe-Iteratoren einen einzigen Parameter vom Typ verwenden (Eingabe wie bei allem, was nicht nur Ausgabe ist).

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

Und so iseq()funktioniert es:

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

Ebenso habe ich Spezialisierungen für

  • const_iterators
  • Zeiger (primitive Arrays)
  • Stream-Iteratoren
  • Jeder Bereich [Anfang, Ende] nur für eine einheitliche Verwendung: Verwenden Sie iseq () für alles
wilhelmtell
quelle
3
... oder Sie können einfach Boost.Range verwenden und die Vorteile von Bereichsadaptern und Peer-Review-Code nutzen.
Mankarse
0

Ungeordnetes Löschen für std::vector. Der effizienteste Weg, ein Element aus a zu löschen, behält vectorjedoch nicht die Reihenfolge der Elemente bei. Ich habe nicht den Sinn gesehen, es auf andere Container auszudehnen, da die meisten nicht die gleiche Strafe für das Entfernen von Gegenständen aus der Mitte haben. Es ähnelt einigen anderen bereits veröffentlichten Vorlagen std::swap, verschiebt jedoch Elemente, anstatt sie zu kopieren.

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 gibt das Vorzeichen eines Typs zurück. Gibt -1für negativ, 0für null und 1für positiv zurück.

template <typename T>
int signum(T val)
{
    return (val > T(0)) - (val < T(0));
}

Clamp ist ziemlich selbsterklärend, es klemmt einen Wert so, dass er innerhalb des angegebenen Bereichs liegt. Es verwirrt mich, dass die Standardbibliothek enthält minund maxaber nichtclamp

template<typename T>
T clamp(const T& value, const T& lower, const T& upper)
{
    return value < lower ? lower : (value > upper ? upper : value);
}
Fibbles
quelle