array versus boost::array



  • Hallo,

    warum sollte man in C++ boost::array den C-array vorziehen?
    wegen den Iteratoren, wegen dem Range-check bei Verwendung von at(),
    wegen der Größeninformationen bei Übergabe an eine Template-Function.
    Man kann sie prinzipiell an eine template-function übergeben, die für
    std::vector geschrieben ist, da sie die gleichen typedefs hat.

    Gibt es weitere Gründe?
    Kann ich eigentlich ein irgendwie ein boost::array übergeben und die
    Größeninformationen in einer nicht-Template-Function abfragen?

    Gruß,
    Newbie



  • Newbie19 schrieb:

    Hallo,

    warum sollte man in C++ boost::array den C-array vorziehen?

    Garnicht. Wenn du C++ kannst, wirste keine Probleme mit C-Arrays haben.

    Boost is eher was für Theoretiker und Frickler.



  • ralf22 schrieb:

    Newbie19 schrieb:

    Hallo,

    warum sollte man in C++ boost::array den C-array vorziehen?

    Garnicht. Wenn du C++ kannst, wirste keine Probleme mit C-Arrays haben.

    Boost is eher was für Theoretiker und Frickler.

    Sollte diese Aussage einfach nur provokativ sein oder meinst du das ernst?
    Ansonsten schreib mal beispielsweise eine Template-Funktion, die dir die
    Summe aller Elemente eines int[] und eines std::vector<int> berechnet.
    Du musst es doppelt implementieren. Bei Verwendung von boost::arrays nicht, oder?


  • Administrator

    Newbie19 schrieb:

    Sollte diese Aussage einfach nur provokativ sein oder meinst du das ernst?

    Ziemlich sicher nur ein Troll 😉

    Newbie19 schrieb:

    Ansonsten schreib mal beispielsweise eine Template-Funktion, die dir die Summe aller Elemente eines int[] und eines std::vector<int> berechnet. Du musst es doppelt implementieren. Bei Verwendung von boost::arrays nicht, oder?

    Nein, das stimmt so nicht:

    #include <numeric>
    #include <iostream>
    
    int main()
    {
      int arr[] = { 1, 2, 3, 4, 5, 6 };
      std::vector<int> vec(arr, arr + 6);
    
      int total = std::accumulate(arr, arr + 6, 0);
      std::cout << "Total arr: " << total << std::endl;
    
      total = std::accumulate(vec.begin(), vec.end(), 0);
      std::cout << "Total vec: " << total << std::endl;
    
      return 0;
    }
    

    Zu deinen bisher aufgelisteten Vorteilen, gibt es noch zwei wichtige Vorteile von boost::array / std::tr1::array gegenüber C Arrays. Sie sind nicht so stark flüchtig. Aus einem C Array wird innert kürzester Zeit ein Zeiger ohne Informationen mehr über das ursprüngliche Array. Auch bieten boost::array / std::tr1::array die Möglichkeit der Kopie an, was man bei einem normalen C Array nicht einfach so machen kann.

    Und das Beste am ganzen ist noch, dass man all diese Funktionalität ohne irgendwelche Nachteile bekommt.

    Grüssli



  • Aus der Kopiersemantik resultiert auch vor allem der Vorteil, dass man boost::array problemlos als Parameter oder Rückgabewert verwenden kann. Ich hatte mal einen Algorithmus, der erforderte, dass ein Array rekursiv per Call-by-Value übergeben wird (die Änderungen in der aufgerufenen Funktion sollten sich in der aufrufenden Funktion nicht auswirken). Da man C-Arrays nur per Referenz übergeben kann, wäre das ein Krampf geworden, ich hätte immer ein lokales Array einrichten müssen und die Elemente kopieren. Mit boost::array ging das aber ohne zusätzliche Anpassungen problemlos.



  • Newbie19 schrieb:

    Kann ich eigentlich ein irgendwie ein boost::array übergeben und die
    Größeninformationen in einer nicht-Template-Function abfragen?

    Nicht direkt. Warum brauchst du das? Ist für die Fälle, wo die Grösse dynamisch abgefragt werden soll, ein std::vector nicht geeigneter?



  • Nexus schrieb:

    Newbie19 schrieb:

    Kann ich eigentlich ein irgendwie ein boost::array übergeben und die
    Größeninformationen in einer nicht-Template-Function abfragen?

    Nicht direkt. Warum brauchst du das? Ist für die Fälle, wo die Grösse dynamisch abgefragt werden soll, ein std::vector nicht geeigneter?

    Hi Nexus,

    ich dachte da z. B. an den Fall eines OutputStreams, wo ich beliebige Daten
    reinschreiben kann, die dann komprimiert werden. Dabei können verschiedene
    Komprimierungstypen verwendet werden, die zur Laufzeit gewählt werden.
    Also ein virtueller operator<<(const boost::array<char, int>),
    wo bei ich allerdings nicht auf Zeichen verzichten möchte, so dass wegen
    \0 kein std::string verwendet werden kann.

    Danke für alle eure Antworten!

    Gruß,
    Newbie



  • Nein, da musst du wohl Templates nehmen. Um einem möglichen Template-Bloat (durch etliche Instanziierungen erzeugter Laufzeitcode-Overhead) vorzubeugen, könntest du innerhalb eine Nicht-Template-Methode aufrufen, die weniger generisch ist und z.B. einzelne char s verarbeitet.



  • Natürlich kann ich mir einen eigenen Typ schreiben, der die Größeninformation
    nicht als template-Parameter beinhaltet.
    Ich würde nur gerne wissen, ob bereits Klassen existieren, die genau
    diesen Fall behandeln.



  • Ah...

    gotcha!

    Du dachtest an etwas wie:

    template<typename T, std::size_t N>
    mystream& operator<<(mystream& stream, const boost::array<T, N>& array){
    stream.write(array.c_array(), N);
    return stream;
    }

    als Brücke, oder?



  • Hier nochmal mit Code-Tags:

    template<std::size_t N> 
    mystream& operator<<(mystream& stream, const boost::array<char, N>& array){ 
    stream.write(array.c_array(), N); 
    return stream; 
    }
    

    wobei T, wenn ich es mir richtig überlege kein Template-Parameter ist 🙂



  • wobei mystream die Basisklasse aller meiner möglichen streams ist.



  • In deinem Fall ist aber wirklich zu überlegen, ob ein vector nicht günstiger wäre.

    Also:

    mystream& operator<<(mystream& stream, const std::vector<char>& v){
    stream.write(&v[0], v.size());
    return stream;
    }
    

    Das hätte zum einen den Vorteil, dass nicht für jede Größe eine neue Funktion instantiiert wird, zum anderen könntest du da eventuell flexibler beim Erstellen der Daten sein (ich kenne deine Algorithmen nicht, aber offenbar willst du dynamische Daten in ein statisches array stecken. Das stelle ich mir ziemlich kompliziert vor). Außerdem hat der vector auch wenig Overhead, v.a. wenn du mit reserve schon vorher genug Speicher reservierst.



  • ipsec schrieb:

    Das hätte zum einen den Vorteil, dass nicht für jede Größe eine neue Funktion instantiiert wird

    Wieso das? Dabei handelt es sich doch dann um einen Funktionsaufruf, der
    geinlined(?) wird. Das ist quasi als ob ich eine virtuelle Funktion mit
    2 Parametern und Rückgabewert direkt aufrufe, oder?
    Deine std::vector Befürwortung kann ich verstehen. Die Funktion mit dem
    boost::array schließt ja eine Funktion für einen Vektor nicht aus 😉
    Im Fall des std::vector<char> kann man den operator<< aber doch auch als member-Funktion machen
    Aber es kommt auch schonmal vor, dass man ein paar fixe Bytes reinschreiben möchte.
    Die Verwendung von reserve ist mir auch bekannt.

    Danke euch!!!



  • ipsec schrieb:

    Das hätte zum einen den Vorteil, dass nicht für jede Größe eine neue Funktion instantiiert wird,

    Das ist kein so großer Vorteil. Wie Newbie schon schreibt wird der op<< bei jedem einigermaßen vernünftig optimierenden Compiler eh geinlined und die Template-Instantiierug selber kostet nur minimal Compilezeit.

    Du könntest die Template-Instantiierung auch verstecken:

    mystream& operator<< (mystream& stream, boost::iterator_range<char*> ir)
    {
      stream.write(ir.begin(), ir.end());
      return stream;
    }
    
    /* ... */
    boost::array<char, 15> ba15;
    boost::array<char,3> ba3;
    std::vector<char> vec;
    
    mystream& ms;
    ms << ba15; // iterator_range hat einen impliziten (template-)Konstruktor für Container,
                // und zwar Container.begin() bis Container.end()
    ms << ba3;  // - und array<char,N>::iterator ist char*
    ms << boost::make_iterator_range(&vec.front(), &vec.back()); //naja...
    

    Die verstecke Template-Instantiierung findet hier beim impliziten Aufruf des Konvertierungs-Konstruktors von array<> nach iterator_range<> statt. Da iterator_range aber nur ein Paar aus iteratoren (in dem Fall Zeigern) ist, kann der Compiler da mit ziemlicher Sicherheit fast alles wegoptimieren und übergibt am Ende tatsächlich nur die beiden Pointer aus dem Array an stream.write()


Anmelden zum Antworten