2013-10-17 49 views
1

我之前的問題(Programming model for classes with const variables)收到了一個完美的答案,但現在我有了一個新的要求,答案似乎不再適用。具有可變數量和類型的常量變量的類的編程模型

說我有包含幾個常量變量的類:

class Base 
{ 
    protected: 
     const int a, b; 
    public: 
     Base(string file); 
}; 

常量需要初始化列表中進行初始化,也需要提前一些其他的方法來計算值。

答案是使用一個輔助類:

class FileParser 
{ 
public: 
    FileParser (const string& file) 
    { 
    Parse (file); 
    } 

    int GetA() const { return mA; } 
    int GetB() const { return mB; } 

private: 
    int mA; 
    int mB; 

    void Parse (const string& file) 
    { 
    // MAGIC HAPPENS! 
    // Parse the file, compute mA and mB, then return 
    } 
}; 

這完全解決了我的問題,但現在,怎麼樣,如果我有一個從基礎的一系列派生類的具有不同數量和類型的常量,並且我想使用相同的幫助程序(FileParser)?我不能使用boost C++,但我有C++ 11。我嘗試使用可變參數的模板來返回可變長度的元組,但它看起來並不重要。以下是修改後的輔助類我想:

template <typename ... Types> 
class LineParser 
{ 
    private: 
     std::tuple<Types...> _t; 
    public: 
     LineParser(const std::string & line) 
     { 
      // local variables 
      std::stringstream ss; 

      // parse the line 
      ss.str(line); 
      for (int i=0; i<sizeof...(Types); i++) 
      { 
       ss>>std::get<i>(_t); 
      } 
     } 
}; 

它失敗,編譯:

error: the value of ‘i’ is not usable in a constant expression 

我解決不了這個問題,我可能會尋找一些替代solutions.C++

+0

嘿,謝謝你的道具。 :) –

+0

您不能使用元組來訪問元組元素。它在編譯時完成。可能是這個鏈接對你有幫助:http://stackoverflow.com/questions/18251815/creating-an-array-initializer-from-a-tuple-or-variadic-template-parameters –

回答

2

所以這會變得更復雜一點。這也有點XY Problem,但至少在這裏你明確的X和Y.

讓我們從你提出的方法開始。這是從來沒有去上班:

std::get<i>(_t); 

get是一個函數模板,所以i必須是一個整型常量表達式。換句話說,編譯時必須知道i

由於您提出的解決方案基本上基於tuple,當您無法制作i和ICE時,整個事情就會解開並分解。所以,讓我們忘記提出的方法,並再次查看問題。你有一個包含一堆東西的文件,大概分成了一些看起來像字段的東西。這些字段表示(據我所知)不同類型的數據。假設這是這樣一個文件的例子:

IBM 
123.45 
1000 

在這裏,我們有一個字符串,一個浮點數和一個整數。不同的文件可能完全有不同的數據,並且一個文件中給定位置處的數據可能與另一個文件中相同位置處的數據不同。然後你有一堆不同的類需要用這些不同的文件進行初始化,每個類都有自己的不同類型數據成員的集合,從文件中的不同位置拉出來。呸。

鑑於問題的複雜性,我自然傾向於儘可能簡化解決方案。這裏已經有足夠的複雜性了。我能想到的最簡單的方法是簡單地爲要分析的每種類型的文件分別設置不同的具體LineParser類。然而,如果你有很多不同類型的文件,這會導致代碼膨脹,隨着這個數字的增長,它會以指數級的方式更難維護。所以讓我們繼續假設你不想這樣做。

然而,不會增加的一件事是文件中不同類型的字段的數量。最終,真的有一些:字符串,整數,浮點數,以及其他一些特定於您的域的特殊內容。但是,即使添加更多數據文件,字段類型的數量也會保持相對恆定。另一個常量是文件本身:它是字符數據。所以讓我們利用這一點。

實現一些從文件存儲類型(字符數據,我在這裏假設)轉換到不同字段的免費函數。如果您使用Boost,則可以使用lexical_cast來完成大部分操作。否則,你可以使用stringstream或其他東西。這裏是一個可能的實現,也有許多人:

template <typename Return> Return As (const std::string& val) 
{ 
    std::stringstream ss; 
    ss << val; 
    Return retval; 
    ss >> retval; 
    return retval; 
} 

現在,我猜想:對於給定Base型類,你知道你感興趣的領域的位置和類型,而這些都是不變的。例如,對於表示股票報價的Base,您知道第一個字段是股票代碼,它是一個字符串。

您的FileParser類可以是通用的,如果它所做的只是將所有內容拉出文件並將其作爲字符數據緩存在數組中,則文件中每個字段一個元素。同樣,這裏有很多可能的實現 - 我的重點是設計,而不是實際的代碼。

class LineParser 
{ 
    private: 
     std::vector <string> mItems; 
    public: 
     LineParser(const std::string & fileName) 
     { 
      std::ifstream fs(fileName); 
      std::copy(
      std::istream_iterator<int>(fs), 
      std::istream_iterator<int>(), 
      std::back_inserter(mItems)); 
     } 

     std::string GetAt (size_t i) const 
     { 
      return mItems [i]; 
     } 
}; 

現在在Base構造,每個const數據成員,從LineParser拉一個具體項目,並與您的免費功能將其轉換:

class Base 
{ 
private: 
    const std::string mTicker; 
    const uint32_t mSize; 
    const float mPrice; 
public: 
    Base (const LineParser& parser) 
    : 
    mTicker (As <std::string> (parser.GetAt (0))), // We know the ticker is at field 0 
    mPrice (As <float> (parser.GetAt (1))), // Price is at field 1... 
    mSize (As <uint32_t> (parser.GetAt (2)) 
    { 
    } 
}; 

有一些事情我喜歡這個做法。首先,即使涉及許多類和功能,每一個都很簡單。這裏的每一個小小的gizmo都有一個明確定義的責任,並且不會做太多的事情。

另一個原因是,你的業務邏輯代碼的個人文檔是簡潔,它屬於:在該const成員初始化代碼:

Base (const LineParser& parser) 
: 
    mTicker (As <std::string> (parser.GetAt (0))), // We know the ticker is at field 0 
    mPrice (As <float> (parser.GetAt (1))), // Price is at field 1... 
    mSize (As <uint32_t> (parser.GetAt (2)) 
{ 
} 

mTicker例如,該初始化說「的股票符號是一個字符串,它從文件中的位置1拉出來。「清潔。

+0

啊,我陷入了困境XY問題!我知道現在有多少時間花更多時間讓我的問題更加廣泛和具體。順便說一句,建議的矢量爲基礎的方法工作得很好! –

+0

@HaliangZhang:很好,很高興它爲你工作! –