boost::string_ Use of ref compile time strings

Posted by p0pb0b on Fri, 04 Mar 2022 23:13:40 +0100

std::string itself realizes the management of character array and provides common functions of string class,
For example,

  • lookup
  • toggle case
  • structure

wait

But std::string is a runtime calculation after all. As we all know, after std C++11, it supports calculating some constant values during compiler compilation, which can reduce runtime work and improve program performance.
The value that can be calculated at compile time is called compile time constant value, and the identifier in C + + is constexpr.

After all, the original intention of C + + language implementation is to maximize performance.
The performance of the runtime has been improved almost, so we can only squeeze the performance of the compiler to the greatest extent.
After all, compiling a program is only done once. As long as we do this well, we can save several calculations of the operation period, which is quite cost-effective.

To implement the string class at compile time, the boost library provides boost::string_ref class to improve the performance of std::string.
boost::string_ Another feature of ref is boost::string_ref does not copy the string, but directly assigns the character pointer, which is more efficient.

Let's look at the code
CMakeLists.txt

cmake_minimum_required(VERSION 2.6)
project(lexical_cast)

add_definitions(-std=c++14)

include_directories("/usr/local/include")
link_directories("/usr/local/lib")
file( GLOB APP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
foreach( sourcefile ${APP_SOURCES} )
    file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${sourcefile})
    string(REPLACE ".cpp" "" file ${filename})
    add_executable(${file} ${sourcefile})
    target_link_libraries(${file} boost_filesystem boost_regex boost_thread boost_system boost_serialization pthread boost_chrono)
endforeach( sourcefile ${APP_SOURCES} )

main.cpp

#include <boost/utility/string_ref.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/lexical_cast.hpp>

#include <iterator>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>

#include <cassert>

std::string between_str(const std::string& input, char starts, char ends) {
    auto pos_beg = std::find(input.begin(), input.end(), starts);
    if(pos_beg == input.end()) {
        return std::string();
    }
    // Add 1 to the starting string position and discard the starts character 
    ++ pos_beg;
    auto pos_end = std::find(pos_beg, input.end(), ends);
    // Don't use the last character. In fact, the index of the iterator is also the package left, not the package right, closed left and open right
    return std::string(pos_beg, pos_end);
}

boost::string_ref between(const boost::string_ref& input, char starts, char ends) {
    auto pos_beg = std::find(input.cbegin(), input.cend(), starts);
    if(pos_beg == input.cend()) {
        return boost::string_ref();
    }

    ++ pos_beg;
    auto pos_end = std::find(pos_beg, input.cend(), ends);
    return boost::string_ref(pos_beg, pos_end - pos_beg);
}

void string_ref_init_examples() {
    // Initialize a string with const char *_ ref
    boost::string_ref r0("^_^");  
    // Initialize string with std::string_ref
    std::string O_O("O_O");
    boost::string_ref r1 = O_O;
    // Initializing a string with std::vector_ ref
    std::vector<char> chars_vec(10, '#');
    boost::string_ref r2(&chars_vec.front(), chars_vec.size());
    
    // Avoid compiler unused warnings
    (void) r0;
    (void) r1;
    (void) r2;
}

void string_ref_algorithm_examples() {
    boost::string_ref r("O_O");
    // Search_ Symbol
    std::find(r.cbegin(), r.cend(), '_');
    // Convert to lowercase and print to standard output
    boost::to_lower_copy(std::ostream_iterator<char>(std::cout), r);
    std::cout << '\n';
    // Print original string
    std::cout << r << '\n';

    // Replace O with ^ and print
    boost::replace_all_copy(std::ostream_iterator<char>(std::cout), r, "O", "^");

    std::cout << '\n';

    // Verify boost:: decimal_ Cast available
    r = "100";
    assert(boost::lexical_cast<int>(r) == 100);
}

int main(int argc, char* argv[]) {
    // Verify that const char * can be used to construct strings_ ref
    std::cout << between("Getting expression (between brackets)", '(', ')') << '\n';
    // Verify that std::string can be used to construct string_ref
    std::string s("(expression)");
    std::cout << between(s, '(', ')') << '\n';

    string_ref_init_examples();
    string_ref_algorithm_examples();
}

The program output is as follows

Implementation details

About boost:: String_ The implementation principle of ref class. You can check the source code,

 typedef basic_string_ref<char,     std::char_traits<char> >        string_ref;

You can see boost::string_ref is actually basic_ string_ A typedef of Ref.
Let's take a look at basic_ string_ Implementation of ref class.

         // basic_string_ref provides an empty constructor to initialize an empty string
         // Set the character pointer to null and the character length to 0
        BOOST_CONSTEXPR basic_string_ref () BOOST_NOEXCEPT
            : ptr_(NULL), len_(0) {}

        // Default copy constructor, via string_ref construct string_ref
        BOOST_CONSTEXPR basic_string_ref (const basic_string_ref &rhs) BOOST_NOEXCEPT
#ifndef BOOST_STRING_REF_NO_CXX11_DEFAULTED_NOEXCEPT_FUNCTIONS
            = default;
 
       // Construct based on const char *
       basic_string_ref(const charT* str) BOOST_NOEXCEPT
            : ptr_(str), len_(traits::length(str)) {}
       // Construct based on std::string 
        template<typename Allocator>
        basic_string_ref(const std::basic_string<charT, traits, Allocator>& str)
            : ptr_(str.data()), len_(str.length()) {}
        //  Construct based on char pointer and length 
       BOOST_CONSTEXPR basic_string_ref(const charT* str, size_type len) BOOST_NOEXCEPT
            : ptr_(str), len_(len) {}

You can see basic_ string_ The overloaded constructor of ref class implements various construction methods, which can interoperate with std::string, const char * and character array (these types can be implicitly converted to basic_string_ref).
In addition, some functions realize constant calculation at compile time to improve performance.

Let's take a look at basic_ string_ How ref supports set class algorithms,
To support stl's set class algorithm, you only need to implement the iterative methods of begin and end

// iterators
        // Implement the begin, end, cbegin and cend methods of compiling time value
        BOOST_CONSTEXPR const_iterator   begin() const { return ptr_; }
        BOOST_CONSTEXPR const_iterator  cbegin() const { return ptr_; }
        BOOST_CONSTEXPR const_iterator     end() const { return ptr_ + len_; }
        BOOST_CONSTEXPR const_iterator    cend() const { return ptr_ + len_; }
        // Implement the direction iterator rbegin, rend
                const_reverse_iterator  rbegin() const { return const_reverse_iterator (end()); }
                const_reverse_iterator crbegin() const { return const_reverse_iterator (end()); }
                const_reverse_iterator    rend() const { return const_reverse_iterator (begin()); }
                const_reverse_iterator   crend() const { return const_reverse_iterator (begin()); }

Finally, to make boost::string_ref can be directly implicitly typed into std::string, and the operator std::string operator is explicitly overloaded, so that they can interoperate (std::string and boost::string_ref can be used instead).

#ifndef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS
         // Overload operator std::string operator
        template<typename Allocator>
        explicit operator std::basic_string<charT, traits, Allocator>() const {
            return std::basic_string<charT, traits, Allocator> ( begin(), end());
            }
#endif

Topics: C++ string compiler