2017-07-28 57 views
1

我處理很多座標數據,認爲預定義庫類型如何包裝對象,使其成爲不能互動的獨立類型?

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

,並從徵和OpenCV等。

現在,每個點的座標都表示在某個參照系中。我希望類型系統能夠跟蹤每個點被表達的幀。一些東西沿線

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; 

最好我可以包裝任何類型的任何「標籤」,使其成爲一個標記類型。然後,所有類似標記的類型在相互使用時表現爲它們的未標記類型,但將不同標記混合的對象應該是編譯時錯誤。我想這也是有道理的,未標記和標記類型之間的操作結果被標記。

這與單位類似,但我想把任何東西變成多種互斥的「單位」。因此,TagWrapper<Person, type1>具有Person的接口,但不會與例如TagWrapper<Person, type2>交互。

+0

這有點不清楚。在我看來,你正在尋找像單元庫這樣的東西,你可以有'1m + 1m = 2m',但是'1m + 1N'是一個編譯錯誤。除非你想限制它,使它必須是完全相同的「單位」,所以你不能做像'1米/ 1秒= 1米/秒' – Justin

+0

單位是一個很好的思考方式,但我想把任何東西變成多種相互排斥的「單位」。所以'TagWrapper '具有'Person'的接口,但不會與例如TagWrapper 交互。 – daemacles

+0

您無法完全複製'Person'的接口。你必須手動寫出你想要支持的接口(所以算術運算符等)。除此之外,你可以做的是添加一個get()成員函數來獲取底層類型。稍後,我會回到這裏回答您的問題 – Justin

回答

1

要爲不同的幀製作不同的類型,只需將其作爲模板參數。然後我們需要定義我們想要的類型的任何接口。下面是一些例子,你可以寫:

#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; 
}; 

如果你想你的問題想確切的語法,你可以用template <typename T, Frames frame>取代template <typename T, typename Tag, Tag kTag>並進行必要的修改,或者你可以使用這種類型的別名:

template <typename T, Frames frame> 
using MyTagWrapper = TagWrapper<T, Frames, frame>; 

因此,混合兩種標記類型會導致編譯錯誤,但混合標記類型和未標記類型會轉換爲標記類型。所有剩下的就是定義標籤類型之間的轉換功能,這是很容易做到:

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

然後將此:

auto pt4 = (tf_1_to_2 * pt1) + pt2; 

變成這樣:

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

//因爲不可能將成員函數完全委託給 // T可以做的所有事情,請爲T提供訪問器。 噢好吧...等待對'reflexpr'和'operator $'等。 – daemacles

相關問題