2013-09-23 75 views
1

我不知道這是否可能,但也許有其他解決方案是我想要的。我正在嘗試從設置文件中獲取設置。它們可以是字符串(如名稱),整數或布爾值。當然,它們作爲文本存儲在文件中,但是我將創建一個用於打開和返回設置的類,但不是字符串,而是其中的每一個。例如,構造函數將加載文件,解析設置並將它們存儲爲地圖。 現在,當我調用設置成員函數時,我希望它能夠確定請求設置的值是什麼類型(如果它是數字,則爲整數,如果爲「true」或「false」,則爲布爾值,如果是字母數字字符串)並返回該類型的值。一個例子C++ - 返回不同的變量類型

Settings UserPreferences("Preferences.cfg"); 
bool AutoLogin = UserPreferences.Setting("autologin"); // return bool 
string UserName = UserPreferences.Setting("username"); // return string or char* 

我看過模板,但它看起來像我必須指定什麼變量我期望創建設置對象時,但這不是重點。我很高興聲明的變量類型返回像這樣:

bool AutoLogin = UserPreferences.Setting<bool>("autologin"); 
string UserName = UserPreferences.Setting<string>("username"); 

但我不知道這是可能的。你怎麼看?

+3

你有沒有想過? –

+1

我有這樣的班,工作正常。所以,是的,我認爲*這是可能的。 ;) – syam

+0

我感到非常驚訝它的工作,因爲你不能重載返回類型。 – BoBTFish

回答

2

這絕對是可能的,儘管你必須保證它可以轉換成給定的類型。這在XNA的ContentLoader中看到很多(儘管是一個非常不同的系統)。您可以使用這種方法來簡化和抽象檢索事物的方式。試想一下:

class Loader 
{ 
private: 
    vector<void*> _items; 
public: 
    template <typename Type> 
    Type GetItem(int index) { return (Type)(_items[ index ]); } 
}; 

的想法是,只要你可以施放內部數據所請求的類型可靠的(更可靠比例)比它是一個完全合法的操作。如何使保證的成功完全是另一個問題,但你肯定可以擁有返回類型與模板類型相同的方法。請看下面的例子(我用的這是一個資源加載一個大學項目):

Header.h

class BasicResource 
{ 
public: 
    static const int ResourceID; 
    const int ID; 
    BasicResource() 
     : ID(ResourceID) 
    { 
    } 
}; 

class Loader 
{ 
private: 
    vector<BasicResource*> _items; 
public: 
    template <typename Type> 
    Type GetItem(int index); 
}; 

#include "inline.inl" 

Inline.inl

template <typename Type> 
Type Loader::GetItem(int index) 
{ 
    auto item = _items[ index ]; 
    if(item != nullptr && item->ID == Type::ResourceID) 
    { 
     return (Type)_item; 
    } 
    else 
    { 
     // Handle the fail case somehow 
    } 
} 

內嵌文件允許您單獨的邏輯像通常那樣,但將其包含在允許導出模板方法的標題中。

+0

謝謝,威廉。你的例子工作,但我想這裏面的DLL。問題是,我不能(或不知道如何)在.h(接口)和.cpp(實現)之間拆分類。我甚至不知道這是否可能。是嗎? – ali

+0

@ali模板代碼必須位於某個頭文件中(通常)。因此,您只能在.cpp – leemes

+0

中移動非模板實現,如構造函數。因此,我無法將模板函數的代碼移動到.cpp中,並在頭中留下聲明?無法將模板化成員從共享/動態庫中導出? – ali

2

是的,這當然是可以的。我寫的完整代碼如下位爲了證明這一點:

#include <iostream> 
#include <map> 
#include <string> 
#include <sstream> 
#include <stdexcept> 


struct Settings 
{ 
    typedef std::map<std::string, std::string> SettingsMap; 
    template <class T> T as(const std::string& name) const 
    { 
    std::istringstream is(getEntry(name)); 
    T value; 
    if(is) 
    { 
     if((is >> value) || (is.eof() && !is.fail())) 
     { 
     return value; 
     } 
    } 
    //Exception handling not in scope of question 
    throw std::runtime_error("..."); 
}; 

const std::string& getEntry(const std::string& name) const 
{ 
    SettingsMap::const_iterator pos(settingsMap_.find(name)); 
    if(pos != settingsMap_.end()) 
    { 
    return pos->second; 
    } 
    //Not part of the scope of this answer.... 
    throw std::invalid_argument("No such setting..."); 
} 

Settings() 
{ 
    settingsMap_["mybool"] = "1"; 
    settingsMap_["myint"] = "5"; 
    settingsMap_["myfloat"] = "43.2"; 
} 

SettingsMap settingsMap_; 
}; 

int main() 
{ 
    Settings s; 
    std::cout << s.as<bool>("mybool") << " " 
    << s.as<int>("myint") << " " 
    << s.as<float>("myfloat"); 

    return 0; 
} 

我實現了類似這樣的東西,但我用的boost ::任何爲我的映射類型,我讀過第一次解析時的實際類型,因此確保存儲的類型是正確的。我也用boost :: lexical_cast代替原生的istringstream,但爲了證明這一點,我省略了它。