Reputation: 21
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
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