1

次のようなDeviceSettingsManagerクラスがあります。

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_;

もちろん、2 つのマップ ( forint32_tと for std::string) を持つことができます。しかし、それはうまくスケーリングしません。floatまたはdoubleまたはのような引数タイプの新しいセッターを追加user_defined_classする必要がある場合は、さらにマップが必要になります。

他の方法はboost::any、この場合、必要なものにreinterpret_cast戻るboost::function必要があるため、問題に再び直面することによって使用することです。

質問: 何十億もの if-else ステートメントを書かないように、そのような PropertyManager をどのように管理すればよいでしょうか?

4

2 に答える 2

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"
于 2013-03-23T11:29:25.403 に答える
0

のマップを作成するだけ<key, parser>ですか?そして、初期文字列、位置、およびマネージャー オブジェクトをパーサーに渡します。多数のマップよりも柔軟性がありますが、具体的なパーサーでマップを埋める必要があります。

于 2013-03-23T10:29:29.887 に答える