Roza Rozmarin
Roza Rozmarin

Reputation: 21

Compile time check if sizeof...(args) matches result from constexpr function

I have constexpr function which counts number of placeholders https://godbolt.org/g/JcxSiu,

e.g: "Hello %1" returns 1, and "Hello %1, time is %2" returns 2.

Then I would like to make a function which does not compile if number of arguments does not equal to number of placeholders.

template <typename... Args>
 inline std::string make(const char* text, Args&&... args) {
   constexpr static unsigned count = sizeof...(args);

   // TODO how to compile time check if count == count_placeholders(text)    
  // constexpr static auto np = count_placeholders(text);

  //static_assert(count == np;, "Wrong number of arguments in make");

  return std::to_string(count);
};

so that make("Hello %1", "World"); compiles and

make("Hello %1 %2", "World"); or make("Hello %1", "World", "John"); does not.

I think it can be done, I just don`t know how. Maybe with some template magick :)

EDIT

I almost get what I want. https://godbolt.org/g/Y3q2f8

Now aborts in debug mode. It is possible to make compile time error?

Upvotes: 1

Views: 1044

Answers (1)

max66
max66

Reputation: 66210

My first idea was to enable/disable make() using SFINAE; something like

template <typename... Args>
auto make(const char* text, Args&&... args)
   -> std::enable_if_t<sizeof...(args) == count_placeholders(text),
                       std::string>
 {
    // ... 
 }

Unfortunately this didn't compile because text can't be used in a constexpr.

But, if you accept that text is a template argument (so known at compile time), you can do something as

template <char const * const text, typename ... Args>
auto makeS (Args && ... args)
   -> std::enable_if_t<sizeof...(args) == count_plc(text), std::string>
 { return std::to_string(sizeof...(args)); }

The following is a full working example

#include <string>
#include <type_traits>

constexpr std::size_t count_plc (char const * s,
                                 std::size_t index = 0U,
                                 std::size_t count = 0U)
 {
   if ( s[index] == '\0' )
      return count;
   else if (    (s[index] == '%')  && (s[index+1U] != '\0')
             && (s[index+1U] > '0') && (s[index+1U] <= '9') )
      return count_plc(s, index + 1U, count+1U);
   else
      return count_plc(s, index + 1U, count); 
 }

template <char const * const text, typename ... Args>
auto makeS (Args && ... args)
   -> std::enable_if_t<sizeof...(args) == count_plc(text), std::string>
 { return std::to_string(sizeof...(args)); }

constexpr char const h1[] { "Hello %1" };
constexpr char const h2[] { "Hello %1 %2" };

int main ()
 {
   makeS<h1>("World"); // compile
   //makeS<h2>("World"); // compilation error
   //makeS<h1>("World", "John"); // compilation error
 }

Upvotes: 2

Related Questions