I've been using Boost.Hana to generate compile-time strings for use as template parameters with gcc:
using namespace boost::hana::literals;
#define GQL_STR(tok) decltype(#tok ## _s)
It relies on having the BOOST_HANA_CONFIG_ENABLE_STRING_UDL
define set. However my code is supposed to be portable and this relies on a gcc/clang extension. After reading this great answer, I wrote some code that mimics it using Boost.Mp11 for little extra ease:
#include <boost/mp11/list.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/hana/string.hpp>
#include <boost/preprocessor/config/limits.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <iostream>
namespace
{
template <int N>
constexpr char get(const char(&str)[N], int i)
{
return i < N ? str[i] : '\0';
}
struct is_null_char
{
template <typename T>
using fn = std::is_same<std::integral_constant<char, '\0'>, T>;
};
template <char... Cs>
constexpr auto list_to_string(boost::mp11::mp_list_c<char, Cs...>)
{
return boost::hana::string<Cs...>{};
}
template <char... Cs>
struct builder
{
using strip_null = boost::mp11::mp_remove_if_q<
boost::mp11::mp_list_c<char, Cs...>,
is_null_char
>;
using type = decltype(list_to_string(strip_null{}));
};
#define GQL_STR_CHAR(z, n, tok) \
BOOST_PP_COMMA_IF(n) get(tok, n)
#define GQL_STR_N(n, tok) \
typename builder<BOOST_PP_REPEAT(n, GQL_STR_CHAR, tok)>::type
}
#define GQL_STR(tok) GQL_STR_N(128, #tok)
int main()
{
using hello_s = GQL_STR(hello);
std::cout << hello_s{}.c_str() << std::endl;
return EXIT_SUCCESS;
}
However I don't like the idea that we're forcing the compiler to generate 128 char template parameters, only to then force it to remove all the superfluous ones when we know the string size at compile time. So I made a shorter one:
namespace
{
template <int N, int... I>
constexpr auto gql_str_impl(const char(&str)[N], std::integer_sequence<int, I...>)
{
return boost::hana::string<str[I]...>{};
}
template <int N>
constexpr auto gql_str(const char(&str)[N])
{
return gql_str_impl(str, std::make_integer_sequence<int, N-1>{});
}
}
#define GQL_STR(tok) \
decltype(gql_str(#tok))
This does not work. It does not work for a very good reason:
<source>:15:43: error: 'str' is not a constant expression
15 | return boost::hana::string<str[I]...>{};
Now what I don't understand, is why the first code sample works at all. The macro expands out to:
typename builder<get("hello", 0), get("hello", 1), ...>::type
Why the is str
in the get
function considered a constant expression here, but not in the second code sample?