1

Is there a way in C++11 or C++14 to get a literal string into a variadic template's parameter list? For example, let's say I had:

template <char C, char... S>
void GrabFirst()
{
    printf("%c %zu\n", C, static_cast<size_t>(sizeof... S));
}

template <char... S>
void MyFunction()
{
    GrabFirst<S>();
}

I'd like to be able to get a literal string into MyFunction in some form. It would be nice if C++11 would allow this when "char" template parameters can accept it, but it doesn't:

MyFunction<"meow">();

The end goal is to be able to parse strings at compile-time.

Myria
  • 3,372
  • 1
  • 24
  • 42
  • [Yes, with a macro.](http://web.archive.org/web/20130930081424/http://cpp-next.com/archive/2012/10/using-strings-in-c-template-metaprograms/) – ghostofstandardspast Jun 17 '14 at 20:23
  • Does [Parsing strings at compile-time — Part I](http://akrzemi1.wordpress.com/2011/05/11/parsing-strings-at-compile-time-part-i/) do what you want? – Shafik Yaghmour Jun 17 '14 at 20:25
  • There are a bunch of duplicate questions and questions related to compile-time string parsing on SO already. Note that you don't even need a variadic template, typically `constexpr` functions are easier to use, see http://akrzemi1.wordpress.com/2011/05/11/parsing-strings-at-compile-time-part-i/ – dyp Jun 17 '14 at 20:26
  • 1
    Related/duplicate http://stackoverflow.com/q/4583022 http://stackoverflow.com/q/3718259 http://stackoverflow.com/q/15858141 It's so simple to find them on google. Go search yourself. – dyp Jun 17 '14 at 20:27
  • I guess the macro approach is more useful if you actually want a string you can change (e.g., push a character onto the back of). For parsing, `constexpr` really is easier, especially with C++14. – ghostofstandardspast Jun 17 '14 at 20:28
  • I was initially hoping this being the case for user defined literals, but then it turned out to be that only for integer literls, and not for string literals, which makes any nice printf-udl stuff moot – PlasmaHH Jun 17 '14 at 21:01
  • dyp: I read two of the three you linked before posting, and they didn't really match what I was doing. But the last one does. I didn't see that in a search. – Myria Jun 17 '14 at 21:21
  • `constexpr` would be easier, if it worked in Visual Studio. – Mooing Duck Jun 17 '14 at 23:07
  • Mooing Duck: Tell me about it =/ Herb Sutter is the "Convener" (chairman?) of the C++ standards committee, and yet it's his compiler that's the most behind of the three majors... Anyway, this is for code that is meant to catch errors, and isn't required for normal operation, so it won't break the Windows build to not have this. – Myria Jun 18 '14 at 01:30

1 Answers1

4

The best I can think of requires you to use something like:

(Update: Thanks to dyp's comment the usage became slightly less verbose but it now works at block scope!)

template< char... > struct foo {};

struct hallo { static constexpr const char* str() { return "Hallo"; } };
make< foo, hallo > m;

where make< foo, hallo > expands to foo< 'H', 'a', 'l', 'l', 'o' >. It's C++11 and works with recent versions of GCC and Clang (haven't tested others).

Live example

Here's the technique which, unlike other solutions you will find, does not require any macros and it does not have any limit on the length of the string.

It starts with the usual indices helper (this is already available in C++14):

template< std::size_t... Ns >
struct indices
{
  using next = indices< Ns..., sizeof...( Ns ) >;
};

template< std::size_t N >
struct make_indices
{
  using type = typename make_indices< N - 1 >::type::next;
};

template<>
struct make_indices< 0 >
{
  using type = indices<>;
};

and now the main technique:

constexpr std::size_t length( const char* str )
{
  return *str ? ( length( str + 1 ) + 1 ) : 0;
}

template< template< char... > class C,
          typename T,
          typename = typename make_indices< length( T::str() ) >::type >
struct make_impl;

template< template< char... > class C,
          typename T,
          std::size_t ... Is >
struct make_impl< C, T, indices< Is... > >
{
  using type = C< T::str()[ Is ]... >;
};

template< template< char... > class C, typename T >
using make = typename make_impl< C, T >::type;
Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • Looks similar to what I've done [here](http://stackoverflow.com/a/15863804/420683), although I've used a type rather than a variable. This allows defining new types in block scope, since (AFAIK), you cannot define variables with linkage in block scope. – dyp Jun 17 '14 at 23:34
  • @dyp Good point and nice idea. I combined it with my code and generalized it a bit. Thank you! – Daniel Frey Jun 18 '14 at 00:33