C++ Compile Time 字串常數比較、取字元、長度測試

在 C++ crow utility.cpp 學到了

將 class 的 constructor 做成 member function template,來為字串常數,包裝成可以在 compile time 呼叫 size()operator[] 的 class。在上面的源始碼中,他使用這項技術來去做 html tag 的字串驗證。在這裡我就只簡單實作三個功能:

  • len_gt: 字串長度是否大於某個常數值。
  • is_char_at: 字串的第 i 個字元是否是 c 這個字元。
  • is_str_equal: 兩個字串是否相等。

這三個功能皆可在 compile time 時完成。

Client Code

int main(int argc, char** argv)
{
    static_assert(len_gt("Hello world", 5), "String is not long enough than 5.");
    static_assert(len_gt("Hello world", 15), "String is not long enough than 15.");
    static_assert(is_char_at("Hello world", 'o', 4), "String[4] should be 'o'");
    static_assert(is_char_at("Hello world", 'a', 4), "String[4] should be 'a'");
    static_assert(is_str_equal("Hello world", "Hello world"), "string is not equal");
    static_assert(is_str_equal("Hello world", "Hello earth"), "string is not equal");
    return 0;
}

在編譯期檢查出 failure 的 assertion。

$ g++ --std=c++11 constexpr.cpp
constexpr.cpp:51:5: error: static_assert failed "String is not long enough than 15."
    static_assert(len_gt("Hello world", 15), "String is not long enough than 15.");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~
constexpr.cpp:53:5: error: static_assert failed "String[4] should be 'a'"
    static_assert(is_char_at("Hello world", 'a', 4), "String[4] should be 'a'");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
constexpr.cpp:55:5: error: static_assert failed "string is not equal"
    static_assert(is_str_equal("Hello world", "Hello earth"), "string is not equal");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

const_str Class

constexpr int require_in_range(int i, int range)
{
    return i >= range? throw std::out_of_range(""): i;
}

struct const_str
{public:
    template<int N>
    constexpr const_str(const char (&a)[N])
        :m_size(N-1), m_p_char(a)
    {
        static_assert( N > 0, "Invalid string.");
    }
    constexpr int size () const
    {
        return m_size;
    }
    constexpr char operator[] (int index) const
    {
        return require_in_range(index, m_size), m_p_char[index];
    }
    const char* const m_p_char;
    int m_size;
};

Compile Time Functioin

constexpr bool len_gt(const_str str, int need_len)
{
    return str.size() > need_len? true: false;
}

constexpr bool is_char_at(const_str str, char c, int index)
{
    return str[index] == c? true: false;
}

constexpr bool is_str_equal(const_str lhs, const_str rhs, int index = 0)
{
    return index >= lhs.size()? true // end of cmp
            : lhs.size() != rhs.size()? false // basic length cmp
            : lhs[index] != rhs[index]? false
            : is_str_equal(lhs, rhs, index+1);
}
comments powered by Disqus