从表中检索混合数据类型的模板化 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 方法的主要内容,如果未能解决你的问题,请参考以下文章