Initialisieren eines ublas-Vektors aus einem C-Array

Lesezeit: 9 Minuten

Initialisieren eines ublas Vektors aus einem C Array
DR

Ich schreibe eine Matlab-Erweiterung mit der C++-ublas-Bibliothek und möchte meine ublas-Vektoren aus den C-Arrays initialisieren können, die vom Matlab-Interpeter übergeben werden. Wie kann ich den ublas-Vektor aus einem C-Array initialisieren, ohne (aus Gründen der Effizienz) die Daten explizit zu kopieren. Ich suche etwas entlang der folgenden Codezeilen:

using namespace boost::numeric::ublas;

int pv[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };
vector<int> v (pv);

Im Allgemeinen ist es möglich, ein C++ zu initialisieren std::vector aus einem Array? Etwas wie das:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int pv[4] = { 4, 4, 4, 4};
    vector<int> v (pv, pv+4);

    pv[0] = 0;
    cout << "v[0]=" << v[0] << " " << "pv[0]=" << pv[0] << endl;

    return 0;
}

aber wo die Initialisierung die Daten nicht kopieren würde. In diesem Fall ist die Ausgabe

v[0]=4 pv[0]=0

aber ich möchte, dass die Ausgabe dieselbe ist, wobei das Aktualisieren des C-Arrays die Daten ändert, auf die der C++-Vektor zeigt

v[0]=0 pv[0]=0

Initialisieren eines ublas Vektors aus einem C Array
Amro

Ich bin mir nicht sicher, wie sich Ihre Frage auf MATLAB/MEX bezieht, aber als Randnotiz möchten Sie vielleicht wissen, dass MATLAB a implementiert Copy-on-Write Strategie.

Das bedeutet, dass beim Kopieren eines Arrays beispielsweise nur einige Header tatsächlich kopiert werden, während die Daten selbst von den beiden Arrays gemeinsam genutzt werden. Und sobald einer von ihnen geändert wird, wird tatsächlich eine Kopie der Daten erstellt.

Das Folgende ist eine Simulation dessen, was unter der Haube passieren könnte (entlehnt von this alter Beitrag):

-----------------------------------------
>> a = [35.7 100.2 1.2e7];

 mxArray a
    pdata -----> 35.7 100.2 1.2e7
  crosslink=0

-----------------------------------------
>> b = a;

 mxArray a
    pdata -----> 35.7 100.2 1.2e7
  crosslink     / \
    |  / \       |
    |   |        |
    |   |        |
   \ /  |        |
   crosslink     |
 mxArray b       |
    pdata --------

-----------------------------------------
>> a(1) = 1;

mxArray a
    pdata -----> (1) 100.2 1.2e7
  crosslink=0


   crosslink=0
 mxArray b
    pdata ------> 35.7 100.2 1.2e7 ...

Ich weiß, das beantwortet Ihre Frage nicht wirklich, ich dachte nur, Sie könnten das Konzept hilfreich finden.

  • Sie können diese Metadaten im Einstellungsformat des MATLAB-Befehlsfensters mit anzeigen format debug

    – Michail Poda

    7. August 2010 um 9:25 Uhr


  • Ein kleiner Punkt zu Ihrem Diagramm – Sie lassen es so aussehen, als ob MATLAB eine neue Kopie der Daten erstellt und neu zuweist b darauf zu zeigen, und das mutiert die Daten, die a verweist auf. Was tatsächlich passiert, ist, dass eine neue Kopie der Daten erstellt wird und a neu zugewiesen, um darauf zu zeigen, und dann werden die neuen Daten mutiert.

    – Chris Taylor

    3. März 2015 um 9:09 Uhr

  • Dies hat wenig bis gar keine Bedeutung für die Frage. Die Frage bezieht sich auf C++. Wenn Sie Matlab-Klassen verwenden, haben sie möglicherweise einige Kompilierzeitoptimierungen gegen redundantes Kopieren. Sobald Sie einen rohen Zeiger daraus nehmen, kann Matlab andere Bibliotheken nicht daran hindern, nutzlose Kopien anzufertigen. Tatsächlich wird der Vorgang des Anforderns eines Rohzeigers das Matlab dazu veranlassen, die Eingabeparameter zu kopieren.

    – Dimitri

    25. August 2018 um 11:15 Uhr

  • @Dimitry Downvote ist fair genug, da es die Frage nicht beantwortet. Trotzdem werde ich diese 9-jährige Antwort behalten, auch wenn sie etwas relevant ist … Was Ihre letzte Aussage betrifft, sollte ich Sie korrigieren und das bei der MEX-API sagen Ebene erstellt MATLAB keine Kopie, wenn Sie die Rohdaten eines numerischen Arrays anfordern (z mxGetData und dergleichen). Das hindert Sie natürlich nicht daran, Kopien zu erstellen, indem Sie den rohen Zeiger in a einschließen std::vector.

    – Amro

    26. August 2018 um 2:53 Uhr

  • Nun, um ehrlich zu sein, stammen meine Erfahrungen von Octave, die Google sowieso auf Matlab-Antworten umleitet. Durch die Bereitstellung von Rohspeicherzeigern geben Matlab\Octave-Array-Container jegliche Kontrolle über den Speicherzugriff auf, und um sicherzustellen, dass durch die Verwendung dieses Zeigers keine Nebenwirkungen verursacht werden, müssen sie sicherstellen, dass der Speicher nicht von anderen Objekten gemeinsam genutzt wird . Octave tut dies auf die Pointer-Anforderung, es sei denn, Sie würzen alles mit const Modifikatoren. Der Matlab-Mex-Compiler ist möglicherweise ausgefeilter und verfolgt die Verwendung des Zeigers oder auch nicht.

    – Dimitri

    26. August 2018 um 13:35 Uhr


Beide std::vector und ublas::vector sind Behälter. Der ganze Sinn von Containern besteht darin, die Speicherung und Lebensdauer ihrer enthaltenen Objekte zu verwalten. Aus diesem Grund müssen sie beim Initialisieren Werte in den Speicher kopieren, der ihnen gehört.

C-Arrays sind Speicherbereiche mit fester Größe und Position, sodass Sie ihre Werte naturgemäß nur durch Kopieren in einen Container übertragen können.

Sie können C-Arrays als Eingabe für viele Algorithmusfunktionen verwenden, also können Sie das vielleicht tun, um die anfängliche Kopie zu vermeiden?

  • Außer dass in der Theorie Sie könnten eine Unterklasse von ublas::vector erstellen, die dies tut. Ihre Unterklasse könnte sich wie ein const ublas::vector verhalten, dessen Größe niemals geändert werden könnte, oder Sie müssten alle Methoden überschreiben, die an der Größenänderung des Containers beteiligt sind, um sicherzustellen, dass kein Speicher freigegeben wird, der nicht dazu gehört . Nur ein absoluter Masochist würde dies versuchen.

    – Sterben in Sente

    16. November 2009 um 17:55 Uhr

Sie können einen std::vector einfach aus einem C-Array initialisieren:

vector<int> v(pv, pv+10);

  • Danke für deine Antwort, aber das würde die Daten kopieren. ich will v und pv auf denselben Datenblock zeigen.

    – DR

    14. November 2009 um 22:50 Uhr

  • Das kannst du nicht haben. std::vector besitzt immer seinen Speicher. Sie können jedoch Ihre eigene Vektorklasse schreiben …

    – tschüss

    14. November 2009 um 22:56 Uhr

Es gibt zwei undokumentierte Klassen in uBLAS storage.hpp. Sie können die Standardspeicherklasse (unbounded_array) in ublas::vector mit einer davon ändern.

  • Die erste Klasse, array_adaptor, erstellt eine Kopie Ihrer Daten, wenn ublas::vector den Kopierkonstruktor aufruft, überhaupt keine sehr nützliche Klasse. Ich würde lieber einfach den entsprechenden Konstruktor verwenden, um dies in den Klassen unbounded_array oder bounded_array zu tun.
  • Der Zweite, shallow_array_adaptor, enthalten nur einen Verweis auf Ihre Daten, sodass Sie Vektor verwenden können, um Ihr C-Array direkt zu ändern. Leider hat es einige Fehler, wenn Sie einen Ausdruck zuweisen, geht der ursprüngliche Datenzeiger verloren. Sie können jedoch eine abgeleitete Klasse erstellen, die dieses Problem behebt.

Hier der Patch und ein Beispiel:

// BOOST_UBLAS_SHALLOW_ARRAY_ADAPTOR must be defined before include vector.hpp
#define BOOST_UBLAS_SHALLOW_ARRAY_ADAPTOR

#include <boost/numeric/ublas/vector.hpp>
#include <algorithm>
#include <iostream>

// Derived class that fix base class bug. Same name, different namespace.    
template<typename T>
class shallow_array_adaptor
: public boost::numeric::ublas::shallow_array_adaptor<T>
{
public:
   typedef boost::numeric::ublas::shallow_array_adaptor<T> base_type;
   typedef typename base_type::size_type                   size_type;
   typedef typename base_type::pointer                     pointer;

   shallow_array_adaptor(size_type n) : base_type(n) {}
   shallow_array_adaptor(size_type n, pointer data) : base_type(n,data) {}
   shallow_array_adaptor(const shallow_array_adaptor& c) : base_type(c) {}

   // This function must swap the values ​​of the items, not the data pointers.
   void swap(shallow_array_adaptor& a) {
      if (base_type::begin() != a.begin())
         std::swap_ranges(base_type::begin(), base_type::end(), a.begin());
   }
};

void test() {
    using namespace boost::numeric;
    typedef ublas::vector<double,shallow_array_adaptor<double> > vector_adaptor;

    struct point {
        double x;
        double y;
        double z;
    };

    point p = { 1, 2, 3 };
    vector_adaptor v(shallow_array_adaptor<double>(3, &p.x));

    std::cout << p.x << ' ' << p.y << ' ' << p.z << std::endl;
    v += v*2.0;
    std::cout << p.x << ' ' << p.y << ' ' << p.z << std::endl;
}

Ausgabe:

1 2 3
3 6 9

Der übliche Vorschlag, einen flachen Array-Adapter zu verwenden, erscheint mir irgendwie sarkastisch – um einfach über einen Zeiger auf ein Array zugreifen zu können, sollten Sie es in ein shared_array mit all dem Verweiszähl-Kram einfügen (das führt zu nichts, da Sie das Array nicht besitzen) und darüber hinaus mit einem Albtraum von Daten-Aliasing. Tatsächlich verfügt uBLAS über eine vollwertige Speicherimplementierung (array_adaptor), was es ermöglicht, Vektoren mit externen c-Arrays zu verwenden. Der einzige Haken ist der Vektorkonstruktor, der eine Kopie erstellt. Warum diese nette Funktion nicht in der Bibliothek verwendet wird, ist mir ein Rätsel, aber wir können trotzdem eine kleine Erweiterung verwenden (es sind eigentlich 2 Zeilen Code, umgeben von gewöhnlichem C++-Bloat).

template<class T>
class extarray_vector :
    public vector<T, array_adaptor<T> >
{
    typedef vector<T, array_adaptor<T> > vector_type;
public:
    BOOST_UBLAS_INLINE
    extarray_vector(size_type size, pointer p)
    { data().resize(size, p); }

    template <size_type N>
    BOOST_UBLAS_INLINE
    extarray_vector(T (&a)[N])
    { data().resize(N, a); }

    template<class V>
    BOOST_UBLAS_INLINE
    extarray_vector& operator = (const vector<T, V>& v)
    {
        vector_type::operator = (v);
        return *this;
    }

    template<class VC>
    BOOST_UBLAS_INLINE
    extarray_vector& operator = (const vector_container<VC>& v)
    {
        vector_type::operator = (v);
        return *this;
    }

    template<class VE>
    BOOST_UBLAS_INLINE
    extarray_vector& operator = (const vector_expression<VE>& ae)
    {
        vector_type::operator = (ae);
        return *this;
    }
};

Sie können es wie folgt verwenden:

int i[] = {1, 4, 9, 16, 25, 36, 49};
extarray_vector<int> iv(i);
BOOST_ASSERT_MSG(i == &iv[0], "Vector should attach to external array\n");
iv[3] = 100;
BOOST_ASSERT(i[3] == 100);
iv.resize(iv.size() + 1, true);
BOOST_ASSERT_MSG(i != &iv[0], "And detach from the array on resize\n");
iv[3] = 200;
BOOST_ASSERT(i[3] == 100);
iv.data().resize(7, i, 0);
BOOST_ASSERT_MSG(i == &iv[0], "And attach back to the array\n");
BOOST_ASSERT(i[3] == 200);

Sie können den Vektor über die resize-Methode von array_adaptor dynamisch an externe Speicher anhängen und von diesem trennen (Daten beibehalten oder verwerfen). Beim Ändern der Größe löst es sich automatisch vom Speicher und wird zum regulären Vektor. Die Zuweisung aus Containern geht direkt in den Speicher, aber die Zuweisung aus dem Ausdruck erfolgt über ein temporäres und der Vektor wird vom Speicher getrennt, Verwendung noalias() um das zu verhindern. Es gibt einen kleinen Overhead im Konstruktor, da data_ ein privates Mitglied ist und wir es standardmäßig mit new T initialisieren müssen[0], dann einem externen Array neu zuweisen. Sie können es in geschützt ändern und direkt im Konstruktor dem Speicher zuweisen.

1645703413 801 Initialisieren eines ublas Vektors aus einem C Array
Nasorenga

Hier ein paar Funktionen zur syntaktisch bequemen Zuweisung (zugegebenermaßen keine Initialisierung):

vector<int> v;
setVector(v, 3, 
          1, 2, 3);

matrix<int> m;
setMatrix(m, 3, 4,
            1,   2,   3,   4,
           11,  22,  33,  44,
          111, 222, 333, 444);

Die Funktionen:

/**
 * Resize a ublas vector and set its elements
 */
template <class T> void setVector(vector<T> &v, int n, ...)
{
    va_list ap;
    va_start(ap, n);
    v.resize(n);
    for (int i = 0; i < n; i++) {
        v[i] = va_arg(ap, T);
    }
    va_end(ap);
}

/**
 * Resize a ublas matrix and set its elements
 */
template <class T> void setMatrix(matrix<T> &m, int rows, int cols ...)
{
    va_list ap;
    va_start(ap, cols);
    m.resize(rows, cols);
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            m(i, j) = va_arg(ap, T);
        }
    }
    va_end(ap);
}

842820cookie-checkInitialisieren eines ublas-Vektors aus einem C-Array

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy