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

Posted

技术标签:

【中文标题】如果可能,尝试对字符串进行静态断言,或者在不是时回退到运行时检查【英文标题】:try to do a static assertion on a string if possible or fallback to a runtime check when it's not 【发布时间】:2015-11-24 16:05:17 【问题描述】:

我有一个名为 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);

【问题讨论】:

如果您可以使用StaticString 将宏转为openSharedDatabase(StaticString("I_m_a_database")); 之类的调用以将文字字符串转换为std::interger_sequence&lt;char, ..&gt;,那么您可以在编译时检查参数的内容... 【参考方案1】:

这是不可能的,因为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 #");

Here's 更多详情。

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

【讨论】:

不幸的是,参数不是constexpr,所以使用提供的代码,OP 可以做到static_assert(beginsWithHash("I_m_a_database"), "DB Name must begin with #");【参考方案2】:

好的,多亏了您的建议,我终于得到了(某种)我想做的事情。我使用了这个问题的答案:Conveniently Declaring Compile-Time Strings in C++ 创建了一个编译时字符序列,现在有两个重载:

template &lt;typename ct_str&gt; inline CollaoDatabase &amp;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...,'\0';
        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()));

【讨论】:

以上是关于如果可能,尝试对字符串进行静态断言,或者在不是时回退到运行时检查的主要内容,如果未能解决你的问题,请参考以下文章

带有回发的 MVC 组件

代理模式

手把手写C++服务器(13):C++11新特性之静态断言static_assert

静态代码检查报告

findbugsPMDCheckstyle等CI插件的使用

PHP:在不知道原始字符集的情况下将任何字符串转换为 UTF-8,或者至少尝试一下