代码之家  ›  专栏  ›  技术社区  ›  microlife

如何在C++编译时检测constchar*格式错误?

  •  0
  • microlife  · 技术社区  · 1 年前

    我正试图编写一个函数“my_func()”,它在编译时计算字符串中“a”的字符数,这会使代码在“a”计数错误时无法编译。

    我受到了这个功能的启发 std::format() 在C++标准库中,它检查 {} 格式字符串中。

    我使用的编译器是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);
    }
    
    2 回复  |  直到 1 年前
        1
  •  1
  •   ecatmur    1 年前

    关键是 std::format 检查 consteval 的构造函数 std::format_string 格式字符串参数格式正确,适用于 std::格式 随后的论点。

    这意味着你需要颠倒你的逻辑;最简单的方法是写 counting_string 类型 consteval 构造函数只接受包含2的字符串文字 'a' s:

    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
    }
    

    Example

    对于样式点,您可能需要 make counting_string a class template

        2
  •  1
  •   Ted Lyngmo    1 年前

    您可以添加 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
    }