从表中检索混合数据类型的模板化 get 方法

Posted

技术标签:

【中文标题】从表中检索混合数据类型的模板化 get 方法【英文标题】:Templated get method to retrieve mixed data types from table 【发布时间】:2017-05-30 11:50:33 【问题描述】:

我知道标题没有意义,找不到更好的。

我需要为 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,但我会在每个模板专门化中复制一些代码(类型一致性检查以及参数检索)

【问题讨论】:

我会为不同的类型使用不同的名称,这里不需要任何模板的东西,getInt(..)getString(..)getBool() 等等。更容易.. 【参考方案1】:

如果您尝试实施,您的方法将失败!问题是:

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);
//...

看起来你根本没有任何收获,是吗?

不过,您也可以尝试反过来,专门化 getter 本身:

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)  

【讨论】:

感谢您的回答。除了使用模板来定义每种类型的专用方法(Nim 的评论),您是否看到除了外观差异之外的任何优势? @codeJack 在这种特殊情况下 - 不。如上所述,完全定义在头文件和类中,模板和普通函数无论如何都应该内联,所以在编译的代码中,应该没有任何区别了...... 你为什么要改变 constness 让它处于参考水平? @codeJack 好吧,您不会修改字符串 - 是吗?因此,通过 const 引用,您可以将 const 和非 const 字符串传递给您的函数。如果您的函数不修改对象(就其外部表示而言),则将函数本身设为 const 甚至是一个好主意。这将允许在 const 对象上使用该函数...反例:尝试在 const std::string 上调用 append... ***.com/questions/3694630/…【参考方案2】:

除了已接受的答案之外,我还想添加一个演示,它在验证类型的正确性方面略有不同,而无需对所有模板专业化代码进行样板化。以及更正不允许的类范围内的显式模板特化。

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;

【讨论】:

谢谢,我喜欢这个解决方案,我认为它是更好的因式分解! 那是我没有实现的 switch-case 东西,因为我更愿意避免它......我建议在 switch 指令中添加一个默认值(也可能会抛出)。一些编码标准甚至强制它(不是100%肯定,但我认为例如MISRA)。好点:类范围内的模板专业化——当然,我很清楚,写这个只是为了简短。不过,似乎我至少应该提到它…… 我知道为什么要在枚举中使用 switch 语句的一个实际原因:当您向枚举添加新项时,如果您忘记将该项添加到交换机,编译器会返回错误。

以上是关于从表中检索混合数据类型的模板化 get 方法的主要内容,如果未能解决你的问题,请参考以下文章

MySQL - 查询临时表以从表中检索 2 行

sql从表中检索数据并使它们链接

无法找到创建的 Hive 表,也无法从表中检索数据

使用存储过程从视图中检索或过滤数据是不是比使用存储过程从表中获取或过滤数据更快?

如何从表中检索我的最后一个插入行?

MySQL 查询从表中检索数据和第二个查询以提取其他结果,没有重复