提问人:qingl 提问时间:9/16/2023 最后编辑:qingl 更新时间:9/20/2023 访问量:102
如何在C++编译时检测const char*格式错误?
How to detect const char* format errors at C++ compile time?
问:
我正在尝试编写一个函数“my_func()”,该函数在编译时计算字符串中的字符数“a”,这使得当“a”的计数错误时代码无法编译。
我受到 C++ 标准库中函数的启发,该函数检查格式字符串中的数字。std::format()
{}
我使用的编译器是msvc,C++20。
我下面的代码无法编译,因为我不知道如何实现这样的功能。那么我该如何修复功能呢?my_func()
template <size_t Size>
auto my_func(const char(&str)[Size]) -> void {
// dosomething...
constexpr size_t count = 0;
const char* c = str;
for (size_t i = 0; i < Size; ++i) {
if (*c == 'a') {
count++; // const values cannot be modified
}
c++;
}
// If the variable `count ` is not set to constexpr, an error will be reported here.
static_assert(count == 2);
// dosomething...
}
auto main() -> int {
my_func("abc abc"); // is error
}
下一个
感谢@ecatmur的回答,但是当 str 转换为 .counting_string
我尝试将构造函数的模板参数传递给类,但这阻止了我在调用函数时找到匹配的重载函数。prepare
template <char Char, size_t Count, size_t Size>
struct counting_string {
std::array<char, Size> m_chars;
// NOLINTNEXTLINE(google-explicit-constructor)
consteval explicit(false) counting_string(char const (&str)[Size]) {
size_t count = 0;
const char* c = str;
for (size_t i = 0; i < Size; ++i) {
if (*c == Char) {
count++;
}
c++;
}
if (count != Count) {
throw "invalid str";
}
}
};
template <size_t Size, class... Args>
auto prepare(const counting_string<'?', sizeof...(Args), Size> sql, Args... args) -> void {
// use sql as: sql.m_chars.data();
}
auto main() -> int {
// call function error
// prepare("insert into test (name, age) values (?, ?)", 1, 2);
}
下面的代码是我希望实现的最终结果。 在没有额外开销(编译时检查)的情况下,检查 SQL 语句中的占位符数量以避免错误。
template <char Char, size_t Count>
struct constexpr_counting_string {
template <size_t Size>
// NOLINTNEXTLINE(google-explicit-constructor)
consteval explicit(false) constexpr_counting_string(char const (&str)[Size]) {
size_t count = 0;
const char* c = str;
for (size_t i = 0; i < Size; ++i) {
if (*c == Char) {
count++;
}
c++;
}
if (count != Count) {
throw "invalid str";
}
}
};
// prepare and bind
template <typename... Args>
auto prepare(statement_t& stmt, constexpr_counting_string<'?', sizeof...(Args)> sql, Args... args) noexcept -> code_t {
// prepare
code_t error = helper_prepare(stmt, sql);
// bind
auto do_bind = [](statement_t& stmt, size_t n, auto arg, code_t& error) noexcept {
if (error != code_t::ok) {
return;
}
using arg_type = decltype(arg);
if constexpr (std::is_same_v<arg_type, uint64_t>) {
error = stmt.bind(n, arg);
}
else if constexpr (std::is_same_v<arg_type, int64_t>) {
error = stmt.bind(n, arg);
}
else if constexpr (std::is_same_v<arg_type, int>) {
error = stmt.bind(n, arg);
}
else if constexpr (std::is_same_v<arg_type, const char*>) {
error = stmt.bind(n, arg);
}
else {
static_assert(std::is_same_v<arg_type, uint64_t>, "args error");
}
};
size_t index = 0;
(do_bind(stmt, index++, args, error), ...);
return error;
}
auto main() -> int {
prepare("insert into test (name, age) values (?, ?)", 1, 2);
}
答:
关键是检查 format-string 参数的构造函数格式是否正确,并且适合后面的参数。std::format
consteval
std::format_string
std::format
这意味着你需要颠倒你的逻辑;最简单的方法是编写一个类型,其构造函数仅接受包含 2 个 S 的字符串文本:counting_string
consteval
'a'
struct counting_string {
template<unsigned Size>
consteval explicit(false) counting_string(char const (&str)[Size]) {
unsigned count = 0;
const char* c = str;
for (unsigned i = 0; i < Size; ++i) {
if (*c == 'a') {
count++; // const values cannot be modified
}
c++;
}
if (count != 2) throw "invalid str";
}
};
auto my_func(counting_string str) -> void {
; // if we get here we know `str` contains 2 'a's
}
示例。
对于样式点,您可能希望counting_string
创建类模板。
对于后续问题,问题是我们不能在构造函数中使用,因为它需要分配内存,并且(目前)内存分配不能从编译时传递到运行时。此外,我们不能使用静态字符串(或者,例如,),因为类模板参数推导 (CTAD) 不够智能,无法从函数模板函数参数推断类模板参数。(令人讨厌的是,CTAD 足够聪明,可以从非类型函数模板模板参数中做到这一点 - 但这在这里无济于事。std::string
consteval
std::array<char, Size>
Size
因此,唯一要做的就是将指向字符串文本的指针(或例如,a )存储在 中,并希望用户传入的参数具有足够长的生存期(如果它确实是字符串文本,那就没问题了 - 唯一的问题是它是否是静态字符串的数组数据成员或。事实上,这就是 std::format_string
的工作方式,所以它有同样的潜在问题。示例:std::string_view
counting_string
char
std::array<char>
#include <string_view>
struct counting_string {
template<unsigned Size>
consteval explicit(false) counting_string(char const (&str)[Size]) : sv(str, Size) {
unsigned count = 0;
for (char const c : sv)
if (c == 'a')
count++; // const values cannot be modified
if (count != 2) throw "invalid str";
}
std::string_view sv;
};
评论
counting_string
您可以添加一个检查器:constexpr
#include <cstddef> // size_t
#include <utility> // index_sequence's
template <std::size_t Size>
constexpr std::size_t number_of_as(const char (&str)[Size]) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return (... + (str[Is] == 'a')); // fold expression
}(std::make_index_sequence<Size>());
}
int main() {
static_assert(number_of_as("abc abc") == 2); // pass
}
评论
const char*
str
str[i]