我正在尝试使以下代码工作:
#include <string>
#include <tuple>
#include <print>
template<char Specifier>
struct ParseSpecifier;
template<>
struct ParseSpecifier<'s'>
{
static void parse()
{
std::println("string");
}
};
template<>
struct ParseSpecifier<'i'>
{
static void parse()
{
std::println("integer");
}
};
template<std::size_t Index>
void parse_format(const std::string_view format)
{
if (Index == format.size())
return;
ParseSpecifier<format[Index]>::parse();
parse_format<Index + 1>(format);
}
int main()
{
parse_format<0>("iiisss");
}
我知道存在可以编译时间验证格式字符串的库(如 fmt),因此这必须以某种方式实现。我应该注意,最终目标不是打印“字符串”和“整数”,而是根据提供的字符串中的
i
和 s
创建 std::tuple。该字符串在编译时始终是已知的。
我从小处开始,因为我刚刚学习模板元编程。我怎样才能使这样的代码工作?我知道格式不是 constexpr,因为函数参数不能是 constexpr。此外,std::string_view 不能是模板参数。如果我使用
template<char... Chars>
那么我必须调用像 parse_format<0, 'i', 's', 's', 'i', 's'>();
这样的函数,这太可怕了。
有什么方法可以让我尝试的代码正常运行吗?如果不是,字符串编译时验证的最佳策略是什么?
重要提示,最终函数将使用一个编译时已知参数和一个实时参数(很像 fmt::format)来调用。
libfmt(和
std::format
)的方式是传递自定义类而不是 std::string_view
,并使用接受字符串的 consteval
构造函数。所述构造函数在编译时运行并且可以例如抛出(或做其他事情)导致编译时错误。
但是这种方法不允许你这样做:
根据提供的字符串中的
和std::tuple
创建i
s
这需要将字符串作为模板参数传递:
template <std::size_t N>
struct ConstString
{
char value[N]{};
constexpr ConstString() {}
constexpr ConstString(const char (&str)[N])
{
std::copy_n(str, N, value);
}
[[nodiscard]] constexpr std::string_view view() const
{
return std::string_view(value, value + N - 1);
}
};
template <ConstString S>
void foo();
int main()
{
foo<"blah">();
}
或者使用稍微不同的调用语法:
template <ConstString>
struct ConstStringTag {};
template <ConstString S>
[[nodiscard]] ConstStringTag<S> constexpr operator""_c() {return {};}
template <ConstString S>
void foo(ConstStringTag<S>);
int main()
{
foo("blah"_c);
}