2009-01-22 36 views
2

我已經偶然發現了有關在C#中驗證參數的this great post,現在我想知道如何在C++中實現類似的東西。我喜歡這個東西的主要原因是,直到第一次驗證失敗時纔會花費任何東西,因爲Begin()函數返回null,其他函數檢查這個。在C++中創建懶惰對象,或者如何執行零成本驗證

顯然,我可以使用Validate* v = 0; IsNotNull(v, ...).IsInRange(v, ...)在C++中實現類似的功能,並且每個指針都通過v指針,並返回一個代理對象,爲其複製所有函數。

現在我想知道是否有類似的方法來實現這個沒有臨時對象,直到第一次驗證失敗。雖然我猜想在堆棧上分配類似std::vector的東西應該是免費的(這是真的嗎?我猜懷疑是空的向量沒有在堆上分配,對不對?)

回答

1

除了C++沒有擴展方法(這會阻止能夠輕鬆添加新驗證)這一事實,它應該太難了。

class Validation 
{ 
    vector<string> *errors; 
    void AddError(const string &error) 
    { 
     if (errors == NULL) errors = new vector<string>(); 
     errors->push_back(error); 
    } 

public: 
    Validation() : errors(NULL) {} 
    ~Validation() { delete errors; } 

    const Validation &operator=(const Validation &rhs) 
    { 
     if (errors == NULL && rhs.errors == NULL) return *this; 
     if (rhs.errors == NULL) 
     { 
      delete errors; 
      errors = NULL; 
      return *this; 
     } 
     vector<string> *temp = new vector<string>(*rhs.errors); 
     std::swap(temp, errors); 
    } 

    void Check() 
    { 
     if (errors) 
      throw exception(); 
    } 

    template <typename T> 
    Validation &IsNotNull(T *value) 
    { 
     if (value == NULL) AddError("Cannot be null!"); 
     return *this; 
    } 

    template <typename T, typename S> 
    Validation &IsLessThan(T valueToCheck, S maxValue) 
    { 
     if (valueToCheck < maxValue) AddError("Value is too big!"); 
     return *this; 
    } 

    // etc.. 

}; 


class Validate 
{ 
public: 
    static Validation Begin() { return Validation(); } 
}; 

使用..

Validate::Begin().IsNotNull(somePointer).IsLessThan(4, 30).Check(); 
+0

這幾乎是我在的地方,現在,我只是想知道是否有延遲創建對象的一些更好的方案(還挺建設上首先使用 - 指針)一應俱全。 – Anteru 2009-01-22 19:04:36

0

不能說太多這個問題的休息,但我也想指出這一點:

雖然我猜分配 像一個std ::矢量 堆棧上應該是免費的(這是 實際上真的嗎?我懷疑一個空的 矢量沒有在 堆上分配,對不對?)

不需要。您仍然必須在向量中分配任何其他變量(例如存儲長度),並且我相信如果在構建時預先爲向量元素分配任何空間,那麼這取決於實現。無論哪種方式,你正在分配SOMETHING,雖然它可能沒有太多的分配永遠不是「免費」,不管發生在堆棧或堆上。這就是說,我會想象做這種事情的時間會很短暫,以至於如果你連續很多次這樣做,它纔會真正的重要。

0

我建議查看Boost.Exception,它提供了基本相同的功能(向任意一個異常對象添加任意的詳細異常信息)。

當然,您需要編寫一些實用方法,以便您可以獲得所需的界面。但要小心:在C++中取消引用空指針會導致未定義的行爲,並且甚至不存在空引用。因此,您不能以某種方式返回空指針,因爲您的鏈接示例在C#擴展方法中使用了空引用。

對於零成本的事情:一個簡單的堆棧分配是相當便宜的,並且一個boost::exception對象本身不會執行任何堆分配,但只有當您將任何error_info對象附加到它時。所以它並不完全是成本,但幾乎與其可以獲得的一樣便宜(一個用於異常對象的vtable-ptr,再加上sizeof(intrusive_ptr <>))。

因此這應該是其中一個試圖進一步優化的最後一部分...

0

重新鏈接的文章:很顯然,在C#創建對象的overhaead是如此之大,函數調用是比較自由的。

我個人建議像

Validate().ISNOTNULL(src).ISNOTNULL(dst); 

驗證()的語法contructs一個臨時的對象,基本上只是一個std ::問題列表。空列表非常便宜(無節點,大小= 0)。如果列表不爲空,則驗證將拋出。如果性能分析顯示這個代價太高,那麼只需將std :: list更改爲手動列表。請記住,指針也是一個對象。只是堅持原始指針的不幸語法,您不會保存對象。相反,用一個很好的語法來包裝原始指針的開銷純粹是編譯時的價格。

PS。 ISNOTNULL(x)對於IsNotNull(x,#x)將是#define - 類似於assert()如何打印出失敗的條件,而不必重複它。