2011-04-25 23 views
2

我想創建一個列表(std ::容器或甚至列表*將是好的)數字(int和double),可以對它們施加限制。C++:限制數字的異構模板

template<typename T> 
class setting { 
    public: 
    std::string name, units; 
    T value, min, max, step; 

    setting(std::string n, T val) : name(n), value(val) { } 

    setting operator++(int) { 
     setting tmp = *this; 
     value += step; if(value > max) value = max; 
     return tmp; 
    } 
}; 
... 
list.add(new setting<int>("channel", 4)); 
list.add(new setting<double>("amplitude", 5.6)); 
... 
for(int i = 0; i < list.size(); i++) 
    std::cout << list[i].name << ": " << list[i].value << std::endl; 

我已經嘗試了這兩種不同的方式,但我不滿意他們中的任何一個。無法從公共基礎派生,因爲基礎類型不知道「值」,因爲它不知道類型或必須提前定義類型。試用了宏模板,但低調感覺很sl。。有沒有辦法做到這一點,而不訴諸所有類型的聯合與類型標識符來選擇正確的成員?

的boost ::用的重載構造變形,似乎做什麼,我想,到目前爲止,除了具有在編譯時枚舉所有類型:

class setting { 
    public: 
     boost::variant< 
     bool, 
     int8_t, 
     uint8_t, 
     int16_t, 
     uint16_t, 
     int32_t, 
     uint32_t, 
     int64_t, 
     uint64_t, 
     float, 
     double, 
     std::string 
      > value; 

     std::string name; 

     setting(std::string n, int v) : name(n), value(v) { } 
     setting(std::string n, double v) : name(n), value(v) { } 
     setting(std::string n, std::string v) : name(n), value(v) { } 
}; 

typedef std::map<std::string, setting*> MapType; 
typedef MapType::const_iterator MapItr; 

int main() { 
    MapType settinglist; 

    settinglist["height"] = new setting("height", 1.3); 
    settinglist["width"] = new setting("width", 5); 
    settinglist["name"] = new setting("name", "the name"); 

    for(MapItr i = settinglist.begin(); i != settinglist.end(); ++i) { 
     std::cout << i->second->name 
     << " : " << i->second->value 
     << std::endl; 
    } 

    return 0; 
}; 

給出:

height : 1.3 
name : the name 
width : 5 
+0

我不知道這是否已經在圖書館解決。使用名稱 - 值對的API非常常見,數值屬性的最小和最大有效值也是如此。 – paperjam 2011-04-25 21:34:41

+0

要清楚,你想要產生一個原始的或有界的類型之一的價值嗎?此外,它可以是任何原始或只是整數和雙打? – 2011-04-26 00:52:40

+0

「值」應該只需要是一個基本類型,但我可以看到需要int64_t。 – lister 2011-04-26 14:38:51

回答

0

也許通用基類虛擬toStringfromString函數?然後您的for循環變爲:

list<setting_base> lst; 
for(list<setting_base>::iterator it = lst.begin(); it != lst.end(); ++it) 
    std::cout << it->name << ": " << it->toString() << std::endl; 
+0

那麼,我希望能夠操縱一般的「價值」;我只是用stdout作爲例子。轉換爲一個字符串將得到的值,但然後我需要轉換爲適當的數字類型來使用它。這可能會很方便,但不是我所希望的解決方案。例如,我可能想要將「value」傳遞給另一個需要「double」或其他函數的函數。我曾想過在基類中有一個函數可以檢索T是什麼,然後從那裏施放。 – lister 2011-04-25 20:02:31

0

如何將簡單類型包裝到Boost.Operators

template <class T, T max = numeric_limits<T>::max(), T min = 0> 
class Saturate 
    : boost::operators<Saturate<T, max, min>, T > 
{ 
private: 
    T _value; 
    void normalize() { 
     if(_value < min) _value = min; 
     if(_value > max) _value = max; 
    } 
    void toNormal(T t) { 
     if(t < min) return min; 
     if(t > max) return max; 
     return t; 
    } 
public: 
    Saturate(T t = T()) : _value(toNormal(t)) {} 
    T value() { return _value; } 
    Saturate& operator+=(const Saturate& x) 
     { _value += x._value; normalize(); return *this; } 
    Saturate& operator-=(const Saturate& x) 
     { _value -= x._value; normalize(); return *this; } 
    ... 
}; 
... 
std::vector<Saturate<int, 1023, -1023> > volume; 
... 
volume[3] = 50000; // really loud 
std::cout << volume[3].value(); // Not so loud 
+0

我想要矢量是異構的,不會提前知道列表中我想要的類型。你大多可以把它壓縮成一個整數和一個真正的列表,這意味着我最多需要2個,但通常至少是2個。我曾經希望我只是不知道足夠的魔法,因爲它感覺真的很接近。 – lister 2011-04-26 14:45:17

+0

@lister - 好吧,我專注於標題中的「有界成員」部分,而不是「異類型」部分。你看過[Boost.Any](http://www.boost.org/doc/libs/1_46_1/doc/html/any/s02.html)嗎? – 2011-04-26 16:21:04

+0

這是我嘗試的方法之一;你必須知道你輸入any_cast後的類型。作業是乾淨的,檢索需要額外的信息。我正在尋找boost :: variant。我也想過使用聯合和重載訪問器,這可能不會太糟糕。 – lister 2011-04-26 16:43:42

0

我認爲你必須要麼適應一切都變成一種類型(指算了一下INT,只需使用雙),或者定義一個更通用的基本類型。

您使用泛型基類型記下的問題是泛泛而談。您正在丟失您訪問容器中的元素時無法返回的類型信息。

我認爲你的設計目標不一致。你應該創建一個足夠滿足你的需求的通用接口(這是類型不可知的),或者選擇一種類型並堅持下去。

0

我會先說你可以在基類中提供幾個toInttoDoubletoInt64方法。那麼,如果你需要雙倍的價值,你只需要它。這需要每個人都付出最少的努力。

如果失敗了,您可以用virtual void value(Operator&) const替換一個常規的virtual int value() const方法。 Operator將提供自己的虛擬功能,每個虛擬功能都接受您想要執行的其中一種類型。本質:

struct Operator { 
    virtual act(int) = 0; 
    virtual act(double) = 0; 
    //repeat for other types 
}; 

struct Base { 
    virtual value(Operator&) const = 0; 
}; 

當你在你的基地型呼叫value,虛擬調度將確保正確執行被調用。在該實現中,您提供了適當的靜態類型到act,並且重載決議開始執行。實際上您可以在Operator實例內執行計算,或者僅存儲結果並提供訪問者。在前一種情況下,你可以爲每種類型做一些獨特的事情,比如使用一種運算速度更快,整數類型但沒有精度的算法。在後一種情況下,你可以去一步,內Base

struct Base { 
    //other stuff 
    template<typename Operator> 
    typename Operator::ResultType value() const { 
     Operator op; 
     value(op); 
     return op.result(); 
    } 
} 
// later 
cout << basePtr->get<IntGetter>(); 

當然提供了一個模板訪問,那最終的結果是做什麼我最初建議的極其錯綜複雜的方式。

/////// 我剛剛注意到您對原始問題的修改。有了這麼多可能的基本類型,這變得不太可行。您必須在您的操作員類中提供每種基本類型的重載。您可以將默認實現提供給基類​​Operator類;比如對所有的整型類型調用act(int),對於所有浮點類型調用act(double),那麼你會回到每個實現只需要兩個重載,再加上你需要用於該特定用例的其他任何重載。

但現在我正在尋找YAGNI。你有一個複雜的基類,你可以通過不存儲完整的int來提供節省幾個字節的設置?你真的不能把所有東西都存儲爲double?它精確地存儲了大量的整數值。或者使用boost::variant並限制自己輸入,雙打和字符串。

+0

我只是這樣做,看看它是否會容忍很多類型,雖然這是爲了儀器控制,並且一些功能實際上確實需要一個特定的時間,所以具有這些功能將節省另一個演員。我知道IAGN的所有這些,所以我會拋棄更具體的東西,如果真的需要的話,可以在以後填入。此外,玩變種的東西,我可以templatize成員函數而不是整個類,只需要枚舉它們在變體聲明。 – lister 2011-04-26 18:53:42