2017-07-28 14 views
1

Ich bin von Koordinatendaten mit viel zu tun, denken vordefinierte BibliothekstypenWie kann ich Objekte umbrechen, so dass sie separate Typen sind, die nicht interagieren können?

struct Point3d { double x,y,z; }; 

und dergleichen von Eigen und OpenCV.

Nun werden die Koordinaten jedes Punktes in einem Bezugssystem ausgedrückt. Ich möchte, dass das Typsystem den Rahmen verfolgt, in dem jeder Punkt ausgedrückt wird. Etwas nach dem Vorbild von

enum Frames { frame1, frame2 }; 

using Point_1 = TagWrapper<Point3d, frame1>; 
using Point_2 = TagWrapper<Point3d, frame2>; 

Point3d untagged_pt = ...; 
Point_1 pt1 = ...; 
Point_2 pt2 = ...; 
Transform<frame1, frame2> tf_1_to_2 = ...; // from frame1 to frame2 

// Compile time error, pt1 and pt2 are in different frames 
auto pt3 = pt1 + pt2; 

// Ok!, and typeof(pt4) == Point_2 
auto pt4 = (tf_1_to_2 * pt1) + pt2; 

// Compile time error, pt2 is not in frame1 
auto pt5 = tf_1_to_2 * pt2; 

// Ok!, and typeof(pt5) == Point_1 
auto pt5 = untagged_pt + pt1; 

Vorzugsweise könnte ich jede Art mit jedem „Tag“ wickeln, damit es einen markierter Typ. Dann verhalten sich alle ähnlich gekennzeichneten Typen wie ihre unmarkierten Typen, wenn sie miteinander verwendet werden, aber das Mischen von Objekten mit verschiedenen Tags sollte ein Kompilierzeitfehler sein. Ich nehme an, dass es auch Sinn macht, dass das Ergebnis von Operationen zwischen einem unmarkierten und einem markierten Typ markiert wird.

Dies ist ähnlich wie Einheiten, aber ich möchte alles in mehrere Arten von sich gegenseitig ausschließenden "Einheiten" verwandeln. So ein TagWrapper<Person, type1> hat die Schnittstelle von Person, wird aber nicht mit einem TagWrapper<Person, type2> interagieren, zum Beispiel.

+0

Diese ist ein bisschen unklar. Es scheint mir, als ob du nach einer Unit-Bibliothek suchst, wo du '1m + 1m = 2m' haben könntest, aber' 1m + 1N' ist ein Kompilierungsfehler. Außer es scheint, dass Sie es einschränken möchten, so dass es genau die gleiche "Einheit" sein muss, so konnten Sie etwas wie 1m/1s = 1 m/s nicht tun – Justin

+0

Einheiten ist eine gute Möglichkeit, darüber nachzudenken, aber ich möchte alles in mehrere Arten von sich gegenseitig ausschließenden "Einheiten" verwandeln. Also hat ein 'TagWrapper ' die Schnittstelle von 'Person', interagiert aber nicht mit einem' TagWrapper 'zum Beispiel. – daemacles

+0

Sie können die Schnittstelle von "Person" nicht vollständig replizieren. Sie müssen manuell die Schnittstellen ausschreiben, die Sie unterstützen möchten (arithmetische Operatoren usw.). Was Sie darüber hinaus tun können, ist das Hinzufügen einer 'get()' -Memberfunktion, die den zugrunde liegenden Typ erhält. In ein bisschen, komme ich hier zurück und beantworte deine Frage – Justin

Antwort

1

Um separate Typen für verschiedene Frames zu erstellen, nehmen Sie es einfach als Vorlageparameter. Wir müssten dann definieren, welche Schnittstelle wir über den Typ möchten. Hier ist ein Beispiel für etwas, könnten Sie schreiben:

#include <utility> // for std::move 
#include <iterator> // for std::begin, std::end 

template <typename T, typename Tag, Tag kTag> 
class TagWrapper 
{ 
    T value_; 

public: 
    TagWrapper(T value) 
     : value_{ std::move(value) } 
    {} 

    // Note: This will allow you to add a T to a TagWrapper<T, ...> 
    // However, if T had an implicit constructor, you wouldn't be able 
    // to use that. If you wanted to support it, you'd have to 3x the operator overloads 
    // you implement. That is, you'd also need: 
    // 
    // friend auto operator+(T const& lhs, TagWrapper<T, Tag, kTag> const& rhs); 
    // friend auto operator+(TagWrapper<T, Tag, kTag> const& lhs, T const& rhs); 
    friend auto operator+(TagWrapper<T, Tag, kTag> const& lhs, TagWrapper<T, Tag, kTag> const& rhs) 
    { 
     return TagWrapper<T, Tag, kTag>{ lhs.value_ + rhs.value_ }; 
    } 

    friend auto operator*(TagWrapper<T, Tag, kTag> const& lhs, TagWrapper<T, Tag, kTag> const& rhs) 
    { 
     return TagWrapper<T>{ lhs.value_ + rhs.value_ }; 
    } 

    // the other arithmetic operators... 

    // You'd also want to do comparison operators 

    // Because it's impossible to completely delegate member functions on to 
    // everything that T can do, provide accessors to T. You may also prefer 
    // to define conversions, explicit or implicit: 
    // 
    // operator T const&() const { return value_; } 
    // explicit operator T const&() const { return value_; } 
    T const& get() const { return value_; } 
    T& get() { return value_; } 

    // As an example of generally wrapping, you could do this: 
    auto begin() { return std::begin(value_); } 
    auto begin() const { return std::begin(value_); } 
    auto end() { return std::end(value_); } 
    auto end() const { return std::end(value_); } 
    // This would make it so that if your type T was a "collection", then 
    // TagWrapper<T, ...> is as well. You could even use TagWrapper<T, ...> 
    // in a for-each loop 

    // Provide some way to see what the tag is. You may or may not want to expose this 
    static Tag tag = kTag; 
}; 

Wenn Sie die genaue Syntax wollte man in der Frage wollten Sie template <typename T, typename Tag, Tag kTag> mit template <typename T, Frames frame> und die notwendigen Änderungen ersetzen könnten, oder Sie können diese Art Alias:

Dadurch würde das Mischen von zwei markierten Typen zu einem Kompilierungsfehler führen, aber das Mischen eines markierten Typs und eines nicht markierten würde zu dem markierten Typ führen. Alles, was noch ist die Definition Konvertierungsfunktionen zwischen getaggt Arten, das ist ziemlich einfach zu tun:

MyTagWrapper<T, frame1> to_frame1(MyTagWrapper<T, frame2> const&); 

Und dann das:

auto pt4 = (tf_1_to_2 * pt1) + pt2; 

wird dies:

auto pt4 = to_frame1(pt1) + pt2; 
+1

// Weil es unmöglich ist, Mitgliederfunktionen auf // alles, was T tun kann, zu delegieren, stelle Accessoren zu T. Oh gut ... Hier wartet für 'reflexpr' und' operator $ 'und ilk. – daemacles

Verwandte Themen