2011-06-24 31 views
6

我目前正在編寫一個函數,它將採用可變數目的參數。我將參數的數量傳遞給函數,然後遍歷參數列表。驗證變量參數是預期的類型

每個傳遞的參數都應該是一個整數。我將把這個整數加到後面將要使用的整數向量上。

我想確保一些小丑在未來不會試圖傳遞這個函數而不是其他整數。我知道我可以從va_arg中檢查當前參數以確保它不是NULL,並且我可以使用類似isanum(va_arg())的東西來確定它是否是有效整數。我想我甚至可以檢查sizeof(va_arg)並將其與sizeof(int)進行比較並確保它們相等。

是否有任何其他檢查,我可以運行驗證我已通過一個有效的整數?

在此先感謝您的幫助

+0

您正在使用什麼編譯器? – Xeo

+0

**真的很好**問題!+1 –

+0

gcc可以對'printf'參數進行完整性檢查,我敢肯定有一種方法可以將這種行爲擴展到自定義函數。顯然,它是非常不便攜的,但如果你碰巧使用gcc,它可能是值得研究的。 –

回答

8

沒有明智的方法可以做到這一點。變量參數函數通過將參數的所有原始二進制表示形式連接成堆棧中的一大塊數據來工作。因此,它依賴於調用者和被調用者就參數的數量和類型達成一致(否則,您最終會讀取例如int,就好像它是float一樣)。

至於你的具體想法:

  • va_arg()是簡單地解釋爲任何類型指定原始堆棧數據的字節數的一些宏。所以調用sizeof()就會告訴你所要求的數據類型的大小。

  • 通常,沒有形成無效整數的原始二進制數據模式。所以假設的isanum()無法工作。

+0

儘管我感謝大家的反饋,但您直接回答了我的問題。謝謝。 – BSchlinker

1

變量參數是不安全的設計。您無法以任何方式檢查用戶是否傳遞了正確的類型。 C++ 0x用可變參數模板來拯救,但現在很多編譯器都不支持它(僅GCC afaik)。

+0

如果所有參數的類型相同,則不需要變量模板。在這種情況下'initializer_list '就足夠了。 – fredoverflow

4

中的每一個傳遞的參數應該是整數。

如果你有一個C++ 0x中的編譯器,我建議一個initializer_list<int>而不是可變參數:

#include <initializer_list> 

void foo(std::initializer_list<int> numbers) 
{ 
    my_vector.insert(my_vector.end(), numbers.begin(), numbers.end()); 
} 

int main() 
{ 
    foo({2, 3, 5, 7}); 
} 

這是直截了當的和完全的類型安全的。

+0

不幸的是,我的環境沒有C++ 0x編譯器,所以我不能使用這個方法。雖然很高興知道,所以謝謝。 – BSchlinker

+0

你可以使用boost :: assign來獲得接近此值的東西。 –

1

不幸的是,確實沒有辦法做到這一點。像printf()這樣的函數可以通過傳遞無效或錯誤數量的參數來輕鬆實現。

在C++中,這是一項高級功能,需要使用此類代碼進行編程以確保傳遞正確的參數。

3

每個傳遞的參數都應該是 的整數。我將把這個整數加到整數向量中,其中 稍後會被使用。

那麼爲什麼不只是接受一個整數向量呢?

void AddIntegers(const std::vector<int>& vec); 

然後,您可以總是使用迭代器連接向量。

還是作出這樣的接口:

void AddInteger(int newInt); 

甚至這樣的:

void AddIntegers(const int* integers, unsigned int numIntegers); 

template <unsigned int Size> 
void AddIntegers(int (&integers)[Size]) 
{ 
    AddIntegers(integers, Size); 
} 

int main() 
{ 
    int i[] = {1, 2, 3, 4}; 
    AddIntegers(i); 
} 

如果你需要一個C++編譯器03的工作,這些將工作。如果你有一個C++ 0x編譯器,那麼有很多優秀的解決方案可用。

1

你不能做任何形式的類型可變參數檢查。我建議使用一個迭代器範圍,而不是(像標準庫函數)或可能是一個std::vector<int>。這種方式不能被顛覆。

0

既然您使用的是C++,那麼如何重載一些運算符並逐個傳遞參數呢?例如

class MyFunction { 
    std::vector<int> param; 
    public: 
    MyFunction() { /* some initialisation? */ } 
    MyFunction &operator,(int eatMe) { 
    param.push_back(eatMe); 
    return *this; 
    } 
    ~MyFunction() { 
    //the implementation of your function goes here 
    } 
} 

然後,你可以這樣調用:

MyFunction(),2,3,5,7; 

注意,使用逗號操作符的可能看起來嚇人,但它實際上是在這種情況下非常有用。它是最低可能的左聯合算子。

如果你的函數有一些額外的參數,int -s不僅是未知的長度,你可以在構造函數中傳遞它們。

如果有人使用別的東西而不是int,將使用默認的逗號運算符(評估左側,放棄,評估右側)。如果你不喜歡那樣 - 選一個不同的操作員,例如類似於流股<<或升壓式%

+1

我通常爲此使用'operator()',並且還允許構造函數接受「第一個」參數,因此您可以編寫例如MyFunction的(2)(3)(5)(7)。析構函數很簡潔,我已經使用過它,但有時使用顯式調用會更有用。 –

0

如果您僅限於C++ 03,並且所有參數都應該是整數,則一種解決方案是簡單地隱藏變量參數函數(例如,在「詳細」命名空間中),併爲一系列重載函數1到N的參數。這些函數將是簡單的內聯函數,用於將調用轉發給真實函數的可變參數版本。這樣,你有一個真正的實現,沒有運行時間的開銷,並且向調用者公開類型安全接口(並且如果調用者需要多於N個參數,調用者總是可以使用可變參數版本)。

Boost.PP還可以幫助生成這些類型的重複模式。

當然,如果你有一定程度的C++ 0x支持,可以用很多方式解決問題,包括initializer_list或variadic模板。

0

只是爲了說明我對CygnusX1的答案評論,你可以做到這一點,如:

class MyFunction { 
    std::vector<int> params; 
    public: 
    MyFunction() { (*this)(); } 
    MyFunction(int eatMe) { (*this)(eatMe); } 
    MyFunction& operator()(int eatMe) { 
    params.push_back(eatMe); 
    return *this; 
    } 
    void operator()() { 
    // use params to do something interesting 
    } 
} 

MyFunction(2)(3)(5)(7)(); 
相關問題