Die Boost C++ Bibliotheken

Kapitel 19. Boost.MultiArray

Boost.MultiArray ist eine Bibliothek, die den Umgang mit mehrdimensionalen Arrays vereinfacht. Sie bietet vor allem den Vorteil, dass sich ein mehrdimensionales Array wie ein Container aus der Standardbibliothek verwenden lässt. So stehen zum Beispiel Methoden wie begin() und end() zur Verfügung, um über Iteratoren auf Elemente in mehrdimensionalen Arrays zuzugreifen. So muss nicht mehr wie bei herkömmlichen C-Arrays mit Zeigern hantiert werden, was vor allem bei Arrays mit vielen Dimensionen unübersichtlich werden kann.

Beispiel 19.1. Eindimensionales Array mit boost::multi_array
#include <boost/multi_array.hpp>
#include <iostream>

int main()
{
  boost::multi_array<char, 1> a{boost::extents[6]};

  a[0] = 'B';
  a[1] = 'o';
  a[2] = 'o';
  a[3] = 's';
  a[4] = 't';
  a[5] = '\0';

  std::cout << a.origin() << '\n';
}

Um Arrays mit Boost.MultiArray zu erstellen, muss auf die Klasse boost::multi_array zugegriffen werden. Diese ist die wichtigste der von dieser Bibliothek angebotenen Klassen. Sie ist in der Headerdatei boost/multi_array.hpp definiert.

boost::multi_array ist ein Template, das zwei Parameter erwartet: Als ersten Parameter geben Sie den Typ an, den Sie im Array speichern wollen. Der zweite Parameter legt fest, wie viele Dimensionen das Array erhalten soll.

Beachten Sie, dass der zweite Parameter ausschließlich die Anzahl der Dimensionen festlegt – nicht die Anzahl der Elemente pro Dimension. Für Beispiel 19.1 bedeutet dies, dass es sich beim Array a um ein eindimensionales Array handelt.

Die Anzahl der Elemente pro Dimension wird zur Laufzeit festgelegt. So wird im Beispiel 19.1 auf das von Boost.MultiArray zur Verfügung gestellte globale Objekt boost::extents zugegriffen, mit dem Dimensionen eine Größe vorgegeben wird. Dieses Objekt wird dazu an den Konstruktor von a übergeben.

Ein Objekt vom Typ boost::multi_array kann grundsätzlich wie ein herkömmliches C-Array verwendet werden. So kann über operator[] und einen Index auf Elemente zugegriffen werden. Im Beispiel 19.1 werden, da es sich bei a um ein eindimensionales Array mit sechs Elementen handelt, fünf Buchstaben gefolgt vom Null-Zeichen gespeichert. Über die Methode origin() wird ein Zeiger auf das erste Elemente erhalten, der es ermöglicht, das im Array gespeicherte Wort Boost auszugeben.

Beachten Sie, dass beim Zugriff auf operator[] im Gegensatz zu anderen bekannten Containern aus der Standardbibliothek die Gültigkeit von Indizes überprüft wird. Bei einem ungültigen Index wird Ihr Programm mit std::abort() beendet. Möchten Sie, dass boost::multi_array keine Überprüfung auf Gültigkeit durchführt, müssen Sie das Macro BOOST_DISABLE_ASSERTS definieren, bevor Sie boost/multi_array.hpp einbinden.

Beispiel 19.2. Ansichten und Subarrays eines zweidimensionalen Arrays
#include <boost/multi_array.hpp>
#include <algorithm>
#include <iostream>
#include <cstring>

int main()
{
  boost::multi_array<char, 2> a{boost::extents[2][6]};

  typedef boost::multi_array<char, 2>::array_view<1>::type array_view;
  typedef boost::multi_array_types::index_range range;
  array_view view = a[boost::indices[0][range{0, 5}]];

  std::memcpy(view.origin(), "tsooB", 6);
  std::reverse(view.begin(), view.end());

  std::cout << view.origin() << '\n';

  boost::multi_array<char, 2>::reference subarray = a[1];
  std::memcpy(subarray.origin(), "C++", 4);

  std::cout << subarray.origin() << '\n';
}

Im Beispiel 19.2 wird ein zweidimensionales Array erstellt. Die Anzahl der Elemente wird für die erste Dimension auf 2 und für die zweite Dimension auf 6 gesetzt. Sie können sich das Array als eine Tabelle vorstellen, die aus zwei Zeilen und sechs Spalten besteht.

In der ersten Zeile der Tabelle soll das Wort Boost gespeichert werden. Da für dieses Wort nur fünf Zeichen gespeichert werden müssen, wird eine Ansicht auf das Array erstellt, die genau fünf Stellen aus dem gesamten Array umfasst.

Eine Ansicht basiert auf der Klasse boost::multi_array::array_view. Es handelt sich hierbei um einen Typ, der von boost::multi_array abhängt, da eine Ansicht einen Teilausschnitt eines bestehenden Arrays beschreibt. Sie können über eine Ansicht auf einen bestimmten Teil eines Arrays zugreifen und diesen Teil so behandeln, als würde es sich um ein eigenständiges Array handeln.

boost::multi_array::array_view ist ein Template und erwartet als Template-Parameter die Anzahl der Dimensionen der Ansicht. Da im Beispiel 19.2 1 übergeben wird, das Array a jedoch aus zwei Dimensionen besteht, wird die neu zu erstellende Ansicht eine Dimension nicht berücksichtigen. Um das Wort Boost zu speichern, wird nur ein eindimensionales Array benötigt – weitere Dimensionen würden nur stören.

Während die Anzahl der Dimensionen als Template-Parameter angegeben wird, muss die Ausdehnung jeder Dimension zur Laufzeit festgelegt werden. Dies geschieht bei boost::multi_array::array_view nicht über boost::extents, sondern über boost::indices. Auch hierbei handelt es sich um ein globales Objekt, das von Boost.MultiArray zur Verfügung gestellt wird.

Ähnlich wie bei boost::extents müssen hinter boost::indices Indizes angegeben werden. Während bei boost::extents ausschließlich Zahlen angegeben werden dürfen, akzeptiert boost::indices auch Bereiche. Diese werden über die Klasse boost::multi_array_types::index_range definiert.

Der erste an boost::indices übergebene Parameter ist kein Bereich, sondern die Zahl 0. Wird eine Zahl angegeben, darf nicht auf boost::multi_array_types::index_range zugegriffen werden. boost::indices interpretiert diese Angabe derart, dass aus allen Elementen der ersten Dimension im Array a die Ansicht nur das erste Element zur Verfügung stellen soll – das mit dem Index 0.

Für den zweiten Parameter wird auf boost::multi_array_types::index_range zugegriffen, um einen Bereich zu definieren. Indem 0 und 5 an den Konstruktor dieser Klasse übergeben werden, wird auf die ersten fünf Elemente aus der ersten Dimension im Array a zugegriffen. Die Angaben werden derart interpretiert, dass der Bereich bei Index 0 beginnt und bei Index 5 endet, wobei Index 5 nicht mehr zum Bereich gehört. Das sechste Element aus der ersten Dimension wird demnach ignoriert.

Die Ansicht view stellt ein eindimensionales Array zur Verfügung, das aus fünf Elementen besteht. Es handelt sich hierbei um die ersten fünf Elemente in der ersten Zeile des Arrays a. Wenn im Folgenden auf view zugegriffen wird, um mit std::memcpy() eine Zeichenkette zu kopieren und die Elemente in der Ansicht mit std::reverse() umzudrehen, spielt dieser Zusammenhang keine Rolle. Ist eine Ansicht einmal erstellt, kann sehr einfach auf einen Teil eines größeren Arrays zugegriffen werden. Die Ansicht verhält sich wie ein eigenes Array.

Wenn mit operator[] direkt auf ein Array vom Typ boost::multi_array zugegriffen wird, hängt der Rückgabewert von der Anzahl der Dimensionen ab. So konnte im Beispiel 19.1 direkt auf einzelne char-Elemente im Array zugegriffen werden, weil das Array eindimensional war.

Im Beispiel 19.2 handelt es sich bei a um ein zweidimensionales Array. Bei einem Zugriff über den Operator operator[] wird nun nicht mehr ein char-Element zurückgegeben, sondern ein Subarray. Der entsprechende Typ des Subarrays wird von Boost.MultiArray nicht öffentlich gemacht, so dass auf boost::multi_array::reference zugegriffen werden muss. Bei diesem Typ handelt es sich jedoch nicht um boost::multi_array::array_view, auch wenn sich ein Subarray wie eine Ansicht verhält. Eine Ansicht muss explizit definiert werden und kann beliebige Teilbereiche eines Arrays umfassen, während ein Subarray automatisch bei einem Zugriff mit operator[] zurückgegeben wird und jeweils alle Elemente in jeder Dimension umfasst.

Beispiel 19.3. C-Array mit boost::multi_array_ref als MultiArray behandeln
#include <boost/multi_array.hpp>
#include <algorithm>
#include <iostream>
#include <cstring>

int main()
{
  char c[12] =
  {
    't', 's', 'o', 'o', 'B', '\0',
    'C', '+', '+', '\0', '\0', '\0'
  };

  boost::multi_array_ref<char, 2> a{c, boost::extents[2][6]};

  typedef boost::multi_array<char, 2>::array_view<1>::type array_view;
  typedef boost::multi_array_types::index_range range;
  array_view view = a[boost::indices[0][range{0, 5}]];

  std::reverse(view.begin(), view.end());
  std::cout << view.origin() << '\n';

  boost::multi_array<char, 2>::reference subarray = a[1];
  std::cout << subarray.origin() << '\n';
}

Boost.MultiArray stellt mit boost::multi_array_ref eine Klasse zur Verfügung, um ein MultiArray über ein bereits bestehendes C-Array zu stülpen. So stellt im Beispiel 19.3 a die von boost::multi_array bekannte Schnittstelle zur Verfügung, ohne Speicherplatz zu reservieren. Mit boost::multi_array_ref kann demnach ein C-Array – egal, aus wie vielen Dimensionen es besteht – wie ein mehrdimensionales Array vom Typ boost::multi_array behandelt werden. Dazu muss das C-Array lediglich als zusätzlicher Parameter neben dem bereits bekannten Objekt boost::extents an den Konstruktor übergeben werden.

Neben boost::multi_array_ref stellt Boost.MultiArray mit boost::const_multi_array_ref auch eine Klasse zur Verfügung, um ein C-Array als ein konstantes mehrdimensionales Array zu behandeln.