如果可能,尝试对字符串进行静态断言,或者在不可能时回退到运行时检查
try to do a static assertion on a string if possible or fallback to a runtime check when it's not
我有一个名为 class 的 databaseManager,它可以打开受保护和共享的数据库。如果数据库的名称以“#”开头,则可以知道该数据库是受保护的。
我也有两个方法:
openProtectedDatabase(QString name)
(私有方法)
openSharedDatabase(QString name)
(public 方法)
因为 99.99% 的时间用户会这样使用 openSharedDatabase
:
openSharedDatabase("I_m_a_database")
在这种特定情况下,我想在编译时检查他是否有权这样做(理解字符串开头没有 "#"
)。所以我可以立即抛出错误。
这是我开始做的事情:
bool DatabaseManager::isDatabaseProtected(QString name) {
return name[0] == '#';
}
CollaoDatabase &DatabaseManager::openSharedDatabase(QString name){
//if a static assertion is possible
//static_assert(static_assertion_possible(name) && isDatabaseProtected(name), "this database name is protected")
//run time check
if (isDatabaseProtected(name)) {
qWarning() << name + " is a protected database";
return nullptr;
}
return openProtectedDatabase(name);
}
这是不可能的,因为 static_assert
在编译时需要一个 常量 表达式 returns 一个整数值。编译无法知道您的 QString 中有什么,因此无法静态断言它是正常的。
可以做到,但您需要使用自定义字符串 class,它在编译时接受文字,不允许突变,并且对所有成员使用 constexpr
职能。鉴于此包装器实现为 class myString
,执行 static_assert
的代码将如下所示:
constexpr unsigned beginsWithHash(myString str)
{
return str.size() > 0 && str[0] == "#";
}
static_assert(beginsWithHash(dbname), "DB Name must begin with #");
Here's 更多详情。
编辑:代表@jarod42 的评论,我应该补充一点,您不能将参数从封闭函数传递给 beginsWithHash
,除非封闭函数也是一个 constexpr。需要给它一个直接的字符串文字。你需要做一些时髦的预处理器魔术 and/or 一些仿函数技巧来让它按照你想要的方式运行并且仍然 look/feel 干净。
好的,多亏了你的建议,我终于得到了可以做(某种程度上)我想做的事情。我使用这个问题的答案:Conveniently Declaring Compile-Time Strings in C++ 创建一个编译时字符序列,现在有两个重载:
template <typename ct_str> inline CollaoDatabase &openSharedDatabase()
inline CollaoDatabase openSharedDatabase(QString name)
第一个是这样使用的(这个做静态断言):
openSharedDatabase<CT_STR("#I_m_a_protected_name")>();
第二个像这样(这个做运行时检查):
openSharedDatabase("Hithere");
代码如下:
#define MACRO_GET_1(str, i) \
(sizeof(str) > (i) ? str[(i)] : 0)
#define MACRO_GET_4(str, i) \
MACRO_GET_1(str, i+0), \
MACRO_GET_1(str, i+1), \
MACRO_GET_1(str, i+2), \
MACRO_GET_1(str, i+3)
#define MACRO_GET_16(str, i) \
MACRO_GET_4(str, i+0), \
MACRO_GET_4(str, i+4), \
MACRO_GET_4(str, i+8), \
MACRO_GET_4(str, i+12)
#define MACRO_GET_64(str, i) \
MACRO_GET_16(str, i+0), \
MACRO_GET_16(str, i+16), \
MACRO_GET_16(str, i+32), \
MACRO_GET_16(str, i+48)
#define CT_STR(str) ct_string<MACRO_GET_64(str, 0), 0>
template <char firstL, char... letters>
struct ct_string{
static char const * c_str() {
static constexpr char string[]={firstL, letters...,'[=12=]'};
return string;
}
static constexpr char first() {
return firstL;
}
};
inline bool isDatabaseProtected(QString name){
return name[0] == '#';
}
template<typename ct_str> static constexpr inline bool isDatabaseProtected() {
return ct_str::first() == '#';
}
inline CollaoDatabase &openSharedDatabase(QString name){
if (isDatabaseProtected(name)) {
qWarning() << name + " is a protected database";
}
return openProtectedDatabase(name);
}
template <typename ct_str> inline CollaoDatabase &openSharedDatabase() {
static_assert(!isDatabaseProtected<ct_str>(), "you are trying to open a protected database");
return openProtectedDatabase(QString(ct_str::c_str()));
}
我有一个名为 class 的 databaseManager,它可以打开受保护和共享的数据库。如果数据库的名称以“#”开头,则可以知道该数据库是受保护的。 我也有两个方法:
openProtectedDatabase(QString name)
(私有方法)openSharedDatabase(QString name)
(public 方法)
因为 99.99% 的时间用户会这样使用 openSharedDatabase
:
openSharedDatabase("I_m_a_database")
在这种特定情况下,我想在编译时检查他是否有权这样做(理解字符串开头没有 "#"
)。所以我可以立即抛出错误。
这是我开始做的事情:
bool DatabaseManager::isDatabaseProtected(QString name) {
return name[0] == '#';
}
CollaoDatabase &DatabaseManager::openSharedDatabase(QString name){
//if a static assertion is possible
//static_assert(static_assertion_possible(name) && isDatabaseProtected(name), "this database name is protected")
//run time check
if (isDatabaseProtected(name)) {
qWarning() << name + " is a protected database";
return nullptr;
}
return openProtectedDatabase(name);
}
这是不可能的,因为 static_assert
在编译时需要一个 常量 表达式 returns 一个整数值。编译无法知道您的 QString 中有什么,因此无法静态断言它是正常的。
可以做到,但您需要使用自定义字符串 class,它在编译时接受文字,不允许突变,并且对所有成员使用 constexpr
职能。鉴于此包装器实现为 class myString
,执行 static_assert
的代码将如下所示:
constexpr unsigned beginsWithHash(myString str)
{
return str.size() > 0 && str[0] == "#";
}
static_assert(beginsWithHash(dbname), "DB Name must begin with #");
Here's 更多详情。
编辑:代表@jarod42 的评论,我应该补充一点,您不能将参数从封闭函数传递给 beginsWithHash
,除非封闭函数也是一个 constexpr。需要给它一个直接的字符串文字。你需要做一些时髦的预处理器魔术 and/or 一些仿函数技巧来让它按照你想要的方式运行并且仍然 look/feel 干净。
好的,多亏了你的建议,我终于得到了可以做(某种程度上)我想做的事情。我使用这个问题的答案:Conveniently Declaring Compile-Time Strings in C++ 创建一个编译时字符序列,现在有两个重载:
template <typename ct_str> inline CollaoDatabase &openSharedDatabase()
inline CollaoDatabase openSharedDatabase(QString name)
第一个是这样使用的(这个做静态断言):
openSharedDatabase<CT_STR("#I_m_a_protected_name")>();
第二个像这样(这个做运行时检查):
openSharedDatabase("Hithere");
代码如下:
#define MACRO_GET_1(str, i) \
(sizeof(str) > (i) ? str[(i)] : 0)
#define MACRO_GET_4(str, i) \
MACRO_GET_1(str, i+0), \
MACRO_GET_1(str, i+1), \
MACRO_GET_1(str, i+2), \
MACRO_GET_1(str, i+3)
#define MACRO_GET_16(str, i) \
MACRO_GET_4(str, i+0), \
MACRO_GET_4(str, i+4), \
MACRO_GET_4(str, i+8), \
MACRO_GET_4(str, i+12)
#define MACRO_GET_64(str, i) \
MACRO_GET_16(str, i+0), \
MACRO_GET_16(str, i+16), \
MACRO_GET_16(str, i+32), \
MACRO_GET_16(str, i+48)
#define CT_STR(str) ct_string<MACRO_GET_64(str, 0), 0>
template <char firstL, char... letters>
struct ct_string{
static char const * c_str() {
static constexpr char string[]={firstL, letters...,'[=12=]'};
return string;
}
static constexpr char first() {
return firstL;
}
};
inline bool isDatabaseProtected(QString name){
return name[0] == '#';
}
template<typename ct_str> static constexpr inline bool isDatabaseProtected() {
return ct_str::first() == '#';
}
inline CollaoDatabase &openSharedDatabase(QString name){
if (isDatabaseProtected(name)) {
qWarning() << name + " is a protected database";
}
return openProtectedDatabase(name);
}
template <typename ct_str> inline CollaoDatabase &openSharedDatabase() {
static_assert(!isDatabaseProtected<ct_str>(), "you are trying to open a protected database");
return openProtectedDatabase(QString(ct_str::c_str()));
}