c++stringtemplatesconstexprpolicy-based-design

How to declare a policy class that does nothing to a string, has consistent signature with other policies and is evaluated at compile-time


I am putting together a set of policy classes that operate a number of operations to a string. I would like these policies to be exchangeable, but the "do nothing" policy is problematic too me, as:

  1. I don't see how to avoid copy using move semantic while maintaining the same (non-move) interface with the sister policies
  2. The compiler should know that the call to the policy can be inlined and evaluated at compile time, but it does not accept constexpr because the return type is a string.
#include <string>
#include<regex>
#include<cassert>

///
/// @brief Do not change anything
///
struct identity
{
    static std::string edit(std::string&& s) // can not use constexpr: return type not a literal
    {
    return std::move(s);
    }
};
///
/// @brief Template class.
///
template<unsigned int N>
struct remove_comments_of_depth
{};
///
/// @brief Do not remove anything
///
template<> struct remove_comments_of_depth<0> : identity
{};
///
/// @brief Remove all comments substrings contained between square brackets
///
/// @note Assumes that text is well formatted so there are no such things like [[]] or unclosed bracket
///
template<> struct remove_comments_of_depth<1>
{
    static std::string edit(const std::string& s)
    {
        return std::regex_replace(s, std::regex(R"(\[[^()]*\])"), "");
    }
};

int main(int argc, char *argv[])
{
    std::string s = "my_string[this is a comment]";
    auto copy = s;
    assert(remove_comments_of_depth<1>::edit(s) == "my_string");
    assert(remove_comments_of_depth<0>::edit(std::move(copy)) == s); // <<< how to avoid this call to std::move
}

What is the "standard" way to go in this kind of situation?


Solution

  • What you want here is reduce unnecessary copy and don't use std::move when calling edit. Why not just return a const reference?

    struct identity
    {
        static const std::string& edit(const std::string& s) {
            return s;
        }
    };
    
    std::string s = "my_string[this is a comment]";
    assert(remove_comments_of_depth<0>::edit("my_string[this is a comment]") == "my_string[this is a comment]");
    assert(remove_comments_of_depth<0>::edit(s) == "my_string[this is a comment]");
    

    See Online Demo

    As to your second question, since C++20, std::string can be a literal type.

    struct identity
    {
        constexpr static std::string edit(std::string&& s) {
            return s;
        }
    };
    
    static_assert(remove_comments_of_depth<0>::edit("my_string[this is a comment]") == "my_string[this is a comment]");
    

    It's totally fine in C++20, see Demo.

    But since your variable s can't be constexpr (it's created at run-time), there's no big help here.