Can constexpr replace a macro that instantiates a template and uses passed argument twice

Viewed 120

Excuse the title but I lack the terminology for conveying what I mean.

I am creating a class that will store data parsed at compile-time from a string, however, it's members templates are dependant on the number of "words" found on that string. I came up with a solution that uses a separate function for computing the number of words on a given string, and then I use a macro CREATE_PATTERN to pass the returned value to instantiate the class I mentioned before while also passing the string to the class constructor since it will need it.

Here's my code as it stands:

template <size_t N>
class Pattern
{
    std::array<unsigned char, N> m_pattern{};
    std::bitset<N> m_mask{};

public:
    constexpr Pattern(std::string_view pattern)
    {
        // do some logic with pattern
    }

    // for testing
    size_t getWordCount()
    {
        return N;
    }
};

// count 'words' in a string and return result
constexpr size_t wordCount(std::string_view pattern)
{
    size_t count{ 0 };
    bool lastWS{ true };

    for (const char c : pattern)
    {
        if (c == ' ')
            lastWS = true;
        else
        {
            if (lastWS)
                ++count;

            lastWS = false;
        }
    }

    return count;
}

// macro for instantiating templated pattern with counted words, also passing pattern string to pattern constructor
#define CREATE_PATTERN(STR) Pattern<wordCount(STR)>(STR)

// I want to create my patterns in this nice one-liner fashion
auto pattern_a = CREATE_PATTERN(" ? AA BB CC DD EE  ");
auto pattern_b = CREATE_PATTERN(" ? AA BB CC DD EE  ");
auto pattern_c = CREATE_PATTERN(" ? AA BB CC DD EE  ");
// etc...

int main()
{
    std::cout << pattern_a.getWordCount(); // correctly returns 6
    return 0;
}

This works and doesn't look too bad to me, however, I believe there may be a better solution for this problem, especially since preprocessor macro uses are usually avoided and replaceable for constexpr a lot of the time.

1 Answers

The type of an expression can depend only on the types of its content and its non type template arguments.

The type of a raw string is an array of characters, including the null terminator.

You can create a non type template literal that contains the string. Then its return value can depend on the non type template argument's value.

auto pat = create_pattern<"some string here">();

where

template<string_literal lit>
constexpr auto create_pattern(){
  return Pattern<wordCount(lit)>(lit);
}

and

template<std::size_t N>
struct string_literal:std::array<char,N> {
  constexpr string_literal(const char (&str)[N]):
    std::array<char,N>{}
  {
    std::copy_n(str, N, data());
  }
};   

assuming no typos and you ensure std::arrays are accepted by the functions I used them on (or write more glue code).

This is .

Related