You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++编译时检测const char*格式错误?实现SQL占位符校验

C++20(MSVC)实现编译时SQL占位符校验方案

问题1:编译时统计字符数量并触发断言

原代码的核心问题是constexpr变量不可修改,且字符串字面量的计数逻辑需要完全在编译时上下文执行。修正方案是将计数逻辑抽离为独立的constexpr函数:

#include <cstddef>

constexpr size_t count_char(const char* str, size_t size, char target) {
    size_t cnt = 0;
    for (size_t i = 0; i < size; ++i) {
        if (str[i] == target) cnt++;
    }
    return cnt;
}

template <size_t Size>
void my_func(const char(&str)[Size]) {
    constexpr size_t a_count = count_char(str, Size, 'a');
    static_assert(a_count == 2, "字符串中'a'的数量必须为2");

    // 后续业务逻辑
}

int main() {
    my_func("abc abc"); // 编译错误:a_count为1,不符合断言要求
    // my_func("aab abc"); // 编译通过
}

问题2:解决SQL占位符与参数数量的重载匹配问题

原counting_string的模板参数设计导致编译器无法从sizeof...(Args)反向推导字符串对应的占位符数量,调整结构让计数结果成为结构体的静态编译时常量,再通过推导指引自动匹配模板参数:

#include <array>
#include <cstddef>

template <char TargetChar, size_t Size>
struct counting_string {
    std::array<char, Size> data;
    // 编译时计算目标字符数量
    static constexpr size_t count = []() {
        size_t cnt = 0;
        for (size_t i = 0; i < Size; ++i) {
            if (data[i] == TargetChar) cnt++;
        }
        return cnt;
    }();

    // consteval确保构造逻辑在编译时执行
    consteval counting_string(const char(&str)[Size]) : data{} {
        for (size_t i = 0; i < Size; ++i) {
            data[i] = str[i];
        }
    }
};

// 推导指引:让编译器自动推导Size参数
template <char TargetChar, size_t Size>
counting_string(const char(&)[Size]) -> counting_string<TargetChar, Size>;

// prepare函数:编译时校验占位符与参数数量一致
template <size_t Size, typename... Args>
void prepare(counting_string<'?', Size> sql, Args&&... args) {
    static_assert(sql.count == sizeof...(Args), "占位符数量与参数数量不匹配");

    // 使用sql.data()获取字符串内容执行预处理
    // ...
}

int main() {
    prepare("insert into test (name, age) values (?, ?)", 1, "Alice"); // 编译通过
    // prepare("insert into test (name) values (?)", 1, "Bob"); // 编译错误:数量不匹配
}

最终实现:符合需求的SQL编译时校验与绑定

针对你期望的代码结构,调整constexpr_counting_string的存储与推导逻辑,同时完善参数绑定的编译时类型校验:

#include <array>
#include <cstddef>
#include <type_traits>
#include <utility>

// 模拟业务类型,请根据实际项目替换
enum class code_t { ok, error };
struct statement_t {
    code_t bind(size_t idx, uint64_t val) { return code_t::ok; }
    code_t bind(size_t idx, int64_t val) { return code_t::ok; }
    code_t bind(size_t idx, int val) { return code_t::ok; }
    code_t bind(size_t idx, const char* val) { return code_t::ok; }
};

code_t helper_prepare(statement_t& stmt, const auto& sql) {
    // 模拟SQL预处理逻辑
    return code_t::ok;
}

template <char TargetChar, size_t Size>
struct constexpr_counting_string {
    std::array<char, Size> m_chars;
    // 编译时计算目标字符(占位符)的数量
    static constexpr size_t target_count = []() {
        size_t cnt = 0;
        for (size_t i = 0; i < Size; ++i) {
            if (m_chars[i] == TargetChar) cnt++;
        }
        return cnt;
    }();

    consteval constexpr_counting_string(const char(&str)[Size]) : m_chars{} {
        for (size_t i = 0; i < Size; ++i) {
            m_chars[i] = str[i];
        }
    }

    const char* data() const noexcept { return m_chars.data(); }
};

// 推导指引:自动推导字符串长度参数
template <char TargetChar, size_t Size>
constexpr_counting_string(const char(&)[Size]) -> constexpr_counting_string<TargetChar, Size>;

// 核心prepare函数:编译时校验占位符数量+参数绑定
template <typename... Args>
code_t prepare(statement_t& stmt, constexpr_counting_string<'?', auto> sql, Args&&... args) noexcept {
    static_assert(sql.target_count == sizeof...(Args), "SQL占位符数量与传入参数数量不匹配");

    code_t error = helper_prepare(stmt, sql);

    size_t index = 1; // SQL占位符通常从1开始计数,可按需调整
    auto do_bind = [&](auto&& arg) noexcept {
        if (error != code_t::ok) return;
        using ArgType = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<ArgType, uint64_t>) {
            error = stmt.bind(index, arg);
        } else if constexpr (std::is_same_v<ArgType, int64_t>) {
            error = stmt.bind(index, arg);
        } else if constexpr (std::is_same_v<ArgType, int>) {
            error = stmt.bind(index, arg);
        } else if constexpr (std::is_same_v<ArgType, const char*>) {
            error = stmt.bind(index, arg);
        } else {
            static_assert(std::is_void_v<ArgType>, "不支持的参数类型");
        }
        index++;
    };

    (do_bind(std::forward<Args>(args)), ...);
    return error;
}

// 适配无statement参数的调用场景
template <typename... Args>
code_t prepare(constexpr_counting_string<'?', auto> sql, Args&&... args) noexcept {
    statement_t dummy_stmt;
    return prepare(dummy_stmt, sql, std::forward<Args>(args)...);
}

int main() {
    // 编译通过:2个占位符对应2个参数
    prepare("insert into test (name, age) values (?, ?)", 25, "Alice");

    // 编译错误:占位符数量与参数数量不匹配
    // prepare("insert into test (name) values (?)", 25, "Alice");

    // 编译错误:参数类型不支持
    // prepare("insert into test (age) values (?)", 3.14f);
    return 0;
}

关键优化点

  1. 编译时计数:通过静态constexpr成员target_count在编译时完成占位符统计,无运行时开销
  2. 自动推导:利用C++20的推导指引,让编译器自动从字符串字面量推导模板参数
  3. 严格校验:通过static_assert在编译时拦截占位符数量不匹配、参数类型不支持的情况
  4. 零开销绑定:使用折叠表达式和constexpr分支完成参数绑定,编译时生成最优代码

内容的提问来源于stack exchange,提问作者microlife

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 02:47:32