如果可能,尝试对字符串执行静态断言,或者在不是时回退到运行时检查

try to do a static assertion on a string if possible or fallback to a runtime check when it's not

本文关键字:检查 运行时 或者 回退 静态 如果 执行 字符串 断言      更新时间:2023-10-16

我有一个名为databaseManager的类,它可以打开受保护的共享数据库。如果数据库的名称以"#"开头,您就可以知道它受到了保护。我还有两种方法:

  • openProtectedDatabase(QString name)(私有方法)
  • openSharedDatabase(QString name)(公共方法)

由于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在编译时需要一个返回整数值的常量表达式。编译无法知道QString中有什么,因此无法静态断言它是健全的。

这是可以做到的,但您需要使用一个自定义的字符串类,该类在编译时接受文字,不允许突变,并对所有成员函数使用constexpr。给定这个包装器实现为类myString,执行static_assert的代码如下所示:

constexpr unsigned beginsWithHash(myString str)
{
    return str.size() > 0 && str[0] == "#";
}
static_assert(beginsWithHash(dbname), "DB Name must begin with #");

以下是更多详细信息。

编辑:代表@jarod42的评论,我应该补充一点,除非封闭函数也是constexpr,否则不能将参数从封闭函数传递给beginsWithHash。需要给它一个直接的字符串文字。你需要做一些时髦的预处理器魔术和/或函子技巧,让它按照你想要的方式运行,并且看起来/感觉仍然干净。

好的,多亏了你的建议,我终于做了一些我想做的事情。我使用了这个问题的答案:在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...,''};
        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()));
}