簡體   English   中英

模板化的get方法可從表中檢索混合數據類型

[英]Templated get method to retrieve mixed data types from table

我知道標題沒有意義,找不到更好的名稱。

我需要為SQlite表提供C ++接口,在其中可以存儲鍵/值/類型配置設置,例如

    Key     |   Value    |   Type
PATH        | /path/to/  |  STRING
HAS_FEATURE |    Y       |  BOOLEAN
REFRESH_RATE|    60      |  INTEGER

為了簡單和靈活起見,數據模型將值托管為字符串,但提供一列以保留原始數據類型。

這就是我想象的一個客戶端調用這種c ++接口的方式。

Configuration c;
int refreshRate = c.get<int>("REFRESH_RATE");

// Next line throws since type won't match
std::string refreshRate = c.get<std::string>("REFRESH_RATE");

這就是我想象的實現方式(我知道代碼不會按原樣編譯,將其視為偽C ++,比這里的語法我更質疑設計)

class Parameter
{
    public:
        enum KnownTypes 
        {
            STRING = 0,
            BOOLEAN,
            INTEGER,
            DOUBLE,
            ...
        }

        std::string key;
        std::string value;
        KnownTypes type;
}

class Configuration 
{
    public:
        template<class RETURNTYPE>
        RETURNTYPE get(std::string& key)
        {
            // get parameter(eg. get cached value or from db...)
            const Parameter& parameter = retrieveFromDbOrCache(key);

            return <parameter.type, RETURNTYPE>getImpl(parameter);
        }

    private:
        template<int ENUMTYPE, class RETURNTYPE>
        RETURNTYPE getImpl(const Parameter& parameter)
        {
            throw "Tthe requested return type does not match with the actual parameter's type"; // shall never happen
        }

        template<Parameter::KnownTypes::STRING, std::string>
        std::string getImpl(const Parameter& parameter)
        {
            return parameter.value;
        }

        template<Parameter::KnownTypes::BOOLEAN, bool>
        std::string getImpl(const Parameter& parameter)
        {
            return parameter.value == "Y";
        }

        template<Parameter::KnownTypes::INTEGER, int>
        int getImpl(const Parameter& parameter)
        {
            return lexical_cast<int>(parameter.value)
        }

        // and so on, specialize once per known type
};

那是一個好的實現嗎? 有什么改進建議嗎?

我知道我可以get每個返回類型的公眾直接get專用,但是我會在每個模板專用化中重復一些代碼(類型一致性檢查以及參數檢索)

如果嘗試實施,您的方法將嚴重失敗! 問題是:

return <parameter.type, RETURNTYPE>getImpl(parameter);

或使用正確的C ++語法:

return getImpl<parameter.type, RETURNTYPE>(parameter);

模板參數必須是編譯時間常數,而parameter.type不是! 因此,您必須嘗試這樣的事情:

switch(parameter.type)
{
case STRING:
    return getImpl<STRING, RETURNTYPE>(parameter);
//...
}

看起來您一點都沒有收獲,是嗎?

但是,您可以嘗試采用另一種方法,專門研究吸氣劑本身:

public:
    template<class RETURNTYPE>
    RETURNTYPE get(std::string const& key);

    template<>
    std::string get<std::string>(std::string const& key)
    {
        return getImpl<STRING>(key);
    }
    template<>
    int get<int>(std::string const& key)
    {
        return lexical_cast<int>(getImpl<STRING>(key));
    }

private:
    template<KnownTypes Type>
    std::string getImpl(std::string const& key)
    {
        Parameter parameter = ...;
        if(parameter.type != Type)
            throw ...;
        return parameter.value;
    }

或沒有模板(請參閱Nim的評論...):

public:
    int getInt(std::string const& key)
    {
        return lexical_cast<int>(getImpl(STRING, key));
    }

private:
    inline std::string getImpl(KnownTypes type, std::string const& key)
    {
        Parameter parameter = ...;
        if(parameter.type != type)
            throw ...;
        return parameter.value;
    }

您可能已經注意到的一項更改:我修復了參數的常數性...

旁注:在類范圍內,不允許上述模板專門化(為簡短起見,已寫成以上)。 在您的真實代碼中,您必須將專長移出類:

struct S { template<typename T> void f(T t); };

template<> void S::f<int>(int t) { }

除了接受的答案外,我還要添加一個演示,該演示在驗證類型的正確性方面稍有不同,而無需在所有模板專業上都重復編寫代碼。 還要糾正不允許的類范圍中的顯式模板專業化。

class Parameter {
public:
  enum KnownTypes { STRING = 0, BOOLEAN, INTEGER, DOUBLE };

  std::string key;
  std::string value;
  KnownTypes type;
};

class Configuration {
public:
  template <class RETURNTYPE>
  RETURNTYPE get(std::string const& key) {
    // get parameter(eg. get cached value or from db...)
    std::map<std::string, Parameter> map{
      {"int", Parameter{"int", "100", Parameter::KnownTypes::INTEGER}},
      {"string", Parameter{"string", "string_value", Parameter::KnownTypes::STRING}},
      {"throwMe", Parameter{"throwMe", "throw", Parameter::KnownTypes::DOUBLE}},
      {"bool", Parameter{"bool", "Y", Parameter::KnownTypes::BOOLEAN}}};
    const Parameter& parameter = map.at(key);

    bool isMatchingType = false;
    switch (parameter.type) {
    case Parameter::STRING:
      isMatchingType = std::is_same<RETURNTYPE, std::string>::value;
      break;
    case Parameter::BOOLEAN:
      isMatchingType = std::is_same<RETURNTYPE, bool>::value;
      break;
    case Parameter::INTEGER:
      isMatchingType = std::is_same<RETURNTYPE, int>::value;
      break;
    case Parameter::DOUBLE:
      isMatchingType = std::is_same<RETURNTYPE, double>::value;
      break;
    };

    if (!isMatchingType)
      throw "Tthe requested return type does not match with the actual parameter's type";

    return getImpl<RETURNTYPE>(parameter);
  }

private:
  template <class RETURNTYPE>
  RETURNTYPE getImpl(const Parameter& parameter);
};

template <>
std::string Configuration::getImpl<std::string>(const Parameter& parameter) {
  return parameter.value;
}

template <>
bool Configuration::getImpl<bool>(const Parameter& parameter) {
  return parameter.value == "Y";
}

template <>
int Configuration::getImpl<int>(const Parameter& parameter) {
  return std::stoi(parameter.value);
}

int main() {
  Configuration conf;
  cerr << conf.get<int>("int") << endl;
  cerr << conf.get<bool>("bool") << endl;
  cerr << conf.get<string>("string") << endl;
  cerr << conf.get<string>("throwMe") << endl;

  return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM