2012-06-11 11 views
14

Chciałbym użyć biblioteki macierzy Eigen jako silnika algebry liniowej w moim programie. Eigen wykorzystuje szablony wyrażeń, aby przeprowadzić leniwą ocenę i uprościć pętle i obliczenia.Jak zintegrować bibliotekę korzystającą z szablonów wyrażeń?

Na przykład:

#include<Eigen/Core> 

int main() 
{ 
    int size = 40; 
    // VectorXf is a vector of floats, with dynamic size. 
    Eigen::VectorXf u(size), v(size), w(size), z(size); 
    u = 2*v + w + 0.2*z; 
} 

Ponieważ Eigena pomocą szablonów ekspresyjne kod jak

u = 2*v + w + 0.2*z; 

W wyżej wymienionej próbie zmniejsza się do jednej pętli o długości 10 (nie 40, pływaki są wprowadzane do rejestratora według fragmentów 4) bez tworzenia tymczasowego. Jakie to jest świetne?

Ale jeśli zintegrować bibliotekę takiego:

class UsingEigen 
{ 
    public: 
     UsingEigen(const Eigen::VectorXf& data): 
      data_(data) 
     {} 

     UsingEigen operator + (const UsingEigen& adee)const 
     { 
      return UsingEigen(data_ + adee.data_); 
     } 

     ... 
    private: 
     Eigen::VectorXf data_; 
} 

Następnie wyrażenia takie jak:

UsingEigen a, b, c, d; 
a = b + c + d; 

nie może skorzystać z drogi Eigen jest realizowany. I to nie jest ostatnia z nich. Istnieje wiele innych przykładów, w których szablony ekspresji są używane w Eigen.

Proste rozwiązanie nie byłoby zdefiniowanie operatorów przez siebie, sprawi data_ publicznego i po prostu napisać wyrażeń takich jak:

UsingEigen a, b, c, d; 
a.data_ = b.data_ + c.data_ + d.data_; 

To łamie hermetyzację, ale zachowuje sprawność Eigen.

Innym sposobem może być tworzenie własnych operatorów, ale niech zwrócą szablony wyrażeń. Ale ponieważ jestem początkującym w C++, nie wiem, czy to jest właściwa droga.

Przykro mi, jeśli pytanie ma zbyt ogólny charakter. Jestem początkującym i nie mam ochoty pytać. Do tej pory używałem wszędzie std::vector<float>, ale teraz potrzebuję również użyć macierzy. Przejście z std::vector<float> na Eigena w całym moim projekcie to duży krok i obawiam się, że na początku popełnię błąd. Wszelkie rady są mile widziane!

+1

Interesujący problem. Pierwszą rzeczą jest: dlaczego w ten sposób chcesz hermetyzować wektory biblioteki własnej? Jakie zachowanie dodają twoje klasy? –

+0

Moje klasy nie dodają żadnej funkcjonalności do klas bibliotek własnych, ale ich używają. Na przykład jedna z moich podstawowych klas przechowuje dwa wektory. Jeden jako dane wejściowe do pewnych obliczeń matematycznych, a drugi jako wynik. Obiekty te muszą współpracować w podobny sposób, jak wspomniałem powyżej. Po dodaniu dwóch takich obiektów należy dodać dane wejściowe. –

+1

Nie sądzę, że jest to możliwe bez samodzielnego reprodukowania znacznej części samego szablonu wyrażenia. Na przykład, '(a + b) * c' będzie mieć postać' ExprCwiseAdd * UsingEigen' (nazwa jest wymyślona, ​​nie przywołuj jej więcej) i musi być gdzieś 'ExprCwiseAdd * UsingEigen' zdefiniowany gdzieś , ale także 'ExprCwiseAdd * ExprCWiseAdd' i tak dalej. W skrócie, dodanie nie będzie miało wartości "UsingEigen" jako typu zwracanego. (Możesz rzucić okiem na [boost :: proto] (http://www.boost.org/doc/libs/1_49_0/doc/html/proto.html), które jest ramą dla szablonów ekspresji). Powodzenia. – eudoxos

Odpowiedz

4

Dlaczego ujawnienie data_ złamałoby hermetyzację? Enkapsulacja oznacza ukrywanie szczegółów implementacji i tylko eksponowanie interfejsu. Jeśli klasa opakowująca UsingEigen nie doda żadnego zachowania ani stanu do natywnej biblioteki Eigen, interfejs się nie zmieni. W takim przypadku należy całkowicie usunąć to opakowanie i napisać program przy użyciu struktur danych Eigen.

Ujawnienie macierzy lub wektora nie przerywa enkapsulacji: wystarczyłaby ekspozycja implementacji macierzy lub wektora. Biblioteka Eigen udostępnia operatory arytmetyczne, ale nie ich implementację.

W przypadku bibliotek szablonów wyrażeń najpowszechniejszym sposobem rozszerzenia funkcjonalności biblioteki jest dodanie zachowania, a nie dodanie przez dodanie stanu. A do dodawania zachowania nie trzeba pisać klas opakowania: można również dodawać funkcje nie będące członkami, które są implementowane pod względem funkcji składowych klasy klasy Eigen. Zobacz: this column "Jak funkcje poza członkami poprawiają enkapsulację" Scotta Meyersa.

Jeśli chodzi o Państwa obawy, że przekształcenie obecnego programu w wersję, która wyraźnie wykorzystuje funkcję Eigen: można krok po kroku przeprowadzać zmianę, zmieniając za każdym razem małe części programu, upewniając się, że testy jednostki (masz testy jednostkowe, nieprawdaż?) nie rób sobie przerwy, kiedy idziesz dalej.

+2

Eksponowanie * elementów danych * przerywa enkapsulację. Ale dodanie przeciążenia szablonów wyrażeń dla klas Eigenów spowodowałoby jego rozbicie w ten sam sposób. –

+0

@KonradRudolph Nie przerywa enkapsulacji, jeśli 'UsingEigen' ma dokładnie ten sam interfejs i dokładnie taką samą implementację (tj. Literalną klasę otoki z czystym przekazywaniem i brakiem rejestrowania/sprawdzania itp.). Miałbyś rację, gdyby "UsingEigen" zdefiniował dodatkową warstwę abstrakcji, ale nie wydaje się, aby tak było (i nie powinno to być w ogóle nazwane 'UsingEigen', ponieważ to ujawnia implementację!) – TemplateRex

-2

Nie rozumiem wszystkich Twoich pytań. Postaram się odpowiedzieć na większość z nich. W tym zdaniu:

UsingEigen operator + (const UsingEigen& adee)const 
    { 
     return UsingEigen(data_ + adee.data_); 
    } 

Masz operatora przeciążeniowe (przepraszam nie wiem, czy jest to poprawny sposób pisać w języku angielskim), z tego powodu można napisać:

a = b + c + d; 

zamiast:

a.data_ = b.data_ + c.data_ + d.data_; 

Nie będzie żadnych problemów, koszt programu będzie taki sam. Ponadto będziesz miał enkapsulację i wydajność.

Z drugiej strony, jeśli chcesz zdefiniować własnego operatora, możesz to zrobić tak, jak robi to szablon. Można znaleźć informacje w internecie szukając „operator przeciążenie”, ale jest podobny do tego:

UsingEigen operator + (const UsingEigen& adee)const 
    { 
     return UsingEigen(data_ + adee.data_); 
    } 

Zamiast „+” można umieścić operatora i wykonać operacje, których potrzebujesz.

Jeśli chcesz utworzyć matrycę, jest to proste. Trzeba tylko utworzyć tablicę tablic lub wektor wektora.

moim zdaniem jest coś takiego:

std::vector<vector<float>> 

Nie jestem pewien, ale to jest łatwe, z drugiej strony można używać prostych macierzy na tej drodze:

pływak YourMatrix [size] [rozmiar];

Mam nadzieję, że to może ci pomóc. Nie rozumiem całego twojego pytania, jeśli potrzebujesz czegoś więcej dodaj mnie na Google +, a ja postaram się ci pomóc.

Przepraszam za mój angielski, mam nadzieję, że rozumiesz wszystko i to ci pomaga.

+1

Niestety, ty jest źle. Musisz przeczytać, co [szablony wypowiedzi] (http://en.wikipedia.org/wiki/Expression_templates) są i jak wpływają na ten kod. Przeciążeni operatorzy nie będą korzystać z szablonów wyrażeń, dokładnie tak, jak opisano w OP. Co więcej, biblioteka Eigen oferuje znacznie więcej semantyki niż naiwne podejście oparte na macierzy, które jest całkowicie niewystarczające dla OP. –

+0

Dziękuję Manuel, Jeśli użyję twojego podejścia, program się skompiluje i działa poprawnie. Ale wyrażenia takie jak a = b + c + d; spowoduje coś takiego: pierwszy b + c zostanie obliczony i zapisany w tymczasowym bctemp. Następnie bctemp zostanie dodane do d i utworzy bcdtemp. W końcu bcdtemp zostanie przypisany do a. To jest 3 pętle i 2 tymczasowe. Moim celem jest tego uniknąć. –

+0

@Manuel Jeśli jesteś zainteresowany tym, co rozumiem przez unikanie tymczasowych i pętli, możesz znaleźć lepsze wyjaśnienie tutaj http://eigen.tuxfamily.org/dox/TopicInsideEigenExample.html –

2

Moim zdaniem wygląda bardziej na obiektowy problem projektowy niż na problem z wykorzystaniem biblioteki. Cokolwiek czytasz z książek, to odpowiednie zalecenia. tj. nie ujawnia zmiennych składowych i nie chroni górnych warstw przed niuansami użycia warstwy trzeciej.

Czego można oczekiwać to odpowiednie abstrakcje funkcji matematycznych, które można zaimplementować za pomocą tej biblioteki wewnętrznie. tzn. można wystawić własną bibliotekę z funkcjami wysokiego poziomu niż operacje elementarne i operacje macierzy. W ten sposób możesz wykorzystać osobliwości interakcji między obiektami biblioteki, a jednocześnie nie musisz ujawniać swoich zmiennych członkowskich do wyższych warstw.

Na przykład można oddzielić moje interfejsy API wyższego poziomu, takie jak obliczanie odległości od punktu do płaszczyzny, odległość między dwoma płaszczyznami, obliczanie nowych współrzędnych punktu w innym układzie współrzędnych za pomocą macierzy transformacji itp. Aby zaimplementować te metody wewnętrznie można wykorzystać obiekty biblioteki.Można ograniczyć, aby nie używać żadnej z klas bibliotek używanych w podpisach API, aby uniknąć zależności od wyższych warstw w tej bibliotece.

Górne warstwy programu powinny być wyższe w poziomie abstrakcji i nie muszą martwić się o elementarne szczegóły implementacji, takie jak sposób obliczania odległości od punktu do płaszczyzny jest zaimplementowany itd. Ponadto, nawet nie muszą wiedzieć, czy ta niższa warstwa jest zaimplementowana przy użyciu tej biblioteki lub czegoś innego. Po prostu użyją interfejsów twojej biblioteki.

Powiązane problemy