Я использую Boost.Hana для генерации строк времени компиляции для использования в качестве параметров шаблона с gcc:
using namespace boost::hana::literals;
#define GQL_STR(tok) decltype(#tok ## _s)
Это зависит от наличия набора определения BOOST_HANA_CONFIG_ENABLE_STRING_UDL
. Однако мой код должен быть переносимым, и это зависит от расширения gcc / clang. Прочитав этот замечательный ответ , я написал некоторый код , который имитирует его с помощью Boost.Mp11 для дополнительной простоты:
#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;
}
Однако мне не нравится идея, что мы заставляем компилятор генерировать 128 параметров шаблона char, только чтобы затем принудительно удалить все лишние, когда мы знаем размер строки во время компиляции. Итак, я сделал более короткий:
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))
Это не работает. Это не работает по очень веской причине:
<source>:15:43: error: 'str' is not a constant expression
15 | return boost::hana::string<str[I]...>{};
Теперь я не понимаю, почему первый пример кода работает вообще. Макрос расширяется до:
typename builder<get("hello", 0), get("hello", 1), ...>::type
Почему str
в функции get
считается здесь константным выражением, а не во втором примере кода?