2013-03-23 91 views
1

我有一個DeviceSettingsManager類,看起來像:的std ::不同參數的個數成員函數映射

class DeviceSettingsManager 
{ 
    int32_t PropertyA(); 
    void SetPropertyA(int32_t prop); 
    std::string PropertyB(); 
    void SetPropertyB(std::string prop); 

    // about 50 properties go here ... 
} 

,我需要根據屬性字符串我必須設置屬性。沒有代碼看起來像這樣:

// Parse property string ("PropertyA = 100, PropertyB = xyz, ...") to key/value pairs 

if (key == "PropertyA") 
    manager.SetPropertyA(value); 
else if (key == "PropertyB") 
    manager.SetPropertyB(value); 

// 50 more properties ... 

不是很好吧?

第一個想法是存儲在制定者的地圖:

setters_[key](value); // where for example key == "PropertyA" and value = 100 

但有一個問題:我無法在地圖不同的仿函數存儲:

typedef boost::function<void(int32_t)> setter_int_t; 
typedef boost::function<void(std::string)> setter_string_t; 
std::map<std::string, ???> setters_; 

當然,我可以有兩張地圖(int32_tstd::string)。但它不能很好地擴展。如果我將被迫添加新參數類型如floatdouble甚至user_defined_class,我將需要更多地圖。

其他方法是用boost::any通過在這種情況下我需要reinterpret_cast回到boost::function我需要的,所以再次面對問題。

問題:我如何管理這樣的PropertyManagers,以防止編寫數十億個if-else語句?

回答

0

可能只是製作一張<key, parser>的地圖?並將初始字符串,位置和管理器對象傳遞給解析器。 它比一堆地圖更靈活,但你仍然需要用具體的解析器填充地圖。

1

最後,順便做(不知道是否是最好的之一):

// checks (map.find(...)) omitted for brevity 

struct base_type_holder 
{ 
    virtual ~base_type_holder() {}; 
}; 

template<typename T> 
struct type_holder : base_type_holder 
{ 
    typedef T type; 
}; 

std::map<std::string, boost::shared_ptr<base_type_holder> > types_; 

template<typename T> 
void RegisterSetterType(const std::string& name) 
{ 
    types_[name].reset(new type_holder<T>); 
} 

boost::shared_ptr<base_type_holder> GetSetterType(const std::string& name) 
{ 
    return types_[name]; 
} 

template<typename T> 
std::map<std::string, boost::function<void(T)> >& SettersMap() 
{ 
    static std::map<std::string, boost::function<void(T)> > settersMap; 
    return settersMap; 
} 

template<typename T, typename H> 
void AddSetter(const std::string& name, void (H::*Setter)(T)) 
{ 
    static H settingsManager; 
    RegisterSetterType<T>(name); 

    SettersMap<T>()[name] = boost::bind(Setter, &settingsManager, ::_1); 
} 

void CallSetter(const std::string& name, const std::string& value) 
{ 
    boost::shared_ptr<base_type_holder> base_type = GetSetterType(name); 
    if (dynamic_cast<type_holder<int32_t> *>(base_type.get())) 
    { 
     SettersMap<int32_t>()[name](atoi(value.c_str())); 
    } 
    else if (dynamic_cast<type_holder<std::string> *>(base_type.get())) 
    { 
     SettersMap<std::string>()[name](value); 
    } 
} 

// Now somewhere in .cpp 
AddSetter<int32_t, DeviceSettingsManager>("PropertyA", &DeviceSettingsManager::SetPropertyA); 
AddSetter<const std::string&, DeviceSettingsManager>("PropertyB", &DeviceSettingsManager::SetPropertyB); 

// other... 

// later 
CallSetter(key, value); // key = "PropertyA", value = "100"