C++ 编译时的 Fizz-Buzz
Fizz-Buzz at C++ compile time
我们今天谈到了“fizz buzz”编程测试,我考虑过用 C++ 实现它,但使用元编程。理想情况下,它会在编译期间生成输出。
我当前的代码使用模板,但仍然需要执行它才能生成输出。在 Ideone.
上查看
我仍然使用 std::cout <<
以便在屏幕上打印输出。 FizzBuzz<i>::value
将给出 i
、-1
(Fizz)、-2
(Buzz) 或 -3
(FizzBuzz)。然后 word<value>
被定义为负数并给出 char const *
:
template <int i>
struct Print {
static void print() {
Print<i - 1>::print();
auto const value = FizzBuzz<i>::value;
if (value < 0) {
std::cout << word<value>() << std::endl;
} else {
std::cout << value << std::endl;
}
}
};
由于递归,循环消失了,Print<0>
停止了它。
有什么方法可以在编译时打印出word<value>()
或value
,这些在编译时是已知的吗?这可能不适用于每个编译器,所以我很高兴有一个适用于 GCC and/or Clang 的解决方案。
虽然通过一些模板技巧可以在编译时生成字符串,但在编译时打印它似乎并不合理。
这是生成字符串的代码:
#include <iostream>
#include <type_traits>
#include <utility>
// Compile time string
template <char ...C> struct str_lit
{
static constexpr char value[] {C..., '[=10=]'};
};
// Integer to str_lit
constexpr std::size_t cexpr_pow(std::size_t x, std::size_t y)
{
std::size_t ret = 1;
while (y--)
ret *= x;
return ret;
}
template <std::size_t N, std::size_t X, typename = std::make_index_sequence<X>> struct num_to_sl_impl;
template <std::size_t N, std::size_t X, std::size_t ...Seq> struct num_to_sl_impl<N, X, std::index_sequence<Seq...>>
{
static constexpr auto func()
{
if constexpr (N >= cexpr_pow(10,X))
return num_to_sl_impl<N, X+1>::func();
else
return str_lit<(N / cexpr_pow(10,X-1-Seq) % 10 + '0')...>{};
}
};
template <std::size_t N> using num_to_sl = decltype(num_to_sl_impl<N,1>::func());
// str_lit concatenation
template <typename F, typename ...P> struct sl_cat_impl {using type = typename sl_cat_impl<F, typename sl_cat_impl<P...>::type>::type;};
template <char ...C1, char ...C2> struct sl_cat_impl<str_lit<C1...>,str_lit<C2...>> {using type = str_lit<C1..., C2...>;};
template <typename F, typename ...P> using sl_cat = typename sl_cat_impl<F, P...>::type;
// The FizzBuzz
template <std::size_t N> struct fizzbuzz_impl
{
using fizz = std::conditional_t<N % 3 == 0,
str_lit<'f','i','z','z'>,
str_lit<>>;
using buzz = std::conditional_t<N % 5 == 0,
str_lit<'b','u','z','z'>,
str_lit<>>;
using type = sl_cat<typename fizzbuzz_impl<N-1>::type,
std::conditional_t<N % 3 == 0 || N % 5 == 0,
sl_cat<fizz, buzz>,
num_to_sl<N>>,
str_lit<'\n'>>;
};
template <> struct fizzbuzz_impl<0>
{
using type = str_lit<>;
};
template <std::size_t N> using fizzbuzz = typename fizzbuzz_impl<N>::type;
用法示例:
int main()
{
std::cout << fizzbuzz<15>::value;
}
输出:
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
我们今天谈到了“fizz buzz”编程测试,我考虑过用 C++ 实现它,但使用元编程。理想情况下,它会在编译期间生成输出。
我当前的代码使用模板,但仍然需要执行它才能生成输出。在 Ideone.
上查看我仍然使用 std::cout <<
以便在屏幕上打印输出。 FizzBuzz<i>::value
将给出 i
、-1
(Fizz)、-2
(Buzz) 或 -3
(FizzBuzz)。然后 word<value>
被定义为负数并给出 char const *
:
template <int i>
struct Print {
static void print() {
Print<i - 1>::print();
auto const value = FizzBuzz<i>::value;
if (value < 0) {
std::cout << word<value>() << std::endl;
} else {
std::cout << value << std::endl;
}
}
};
由于递归,循环消失了,Print<0>
停止了它。
有什么方法可以在编译时打印出word<value>()
或value
,这些在编译时是已知的吗?这可能不适用于每个编译器,所以我很高兴有一个适用于 GCC and/or Clang 的解决方案。
虽然通过一些模板技巧可以在编译时生成字符串,但在编译时打印它似乎并不合理。
这是生成字符串的代码:
#include <iostream>
#include <type_traits>
#include <utility>
// Compile time string
template <char ...C> struct str_lit
{
static constexpr char value[] {C..., '[=10=]'};
};
// Integer to str_lit
constexpr std::size_t cexpr_pow(std::size_t x, std::size_t y)
{
std::size_t ret = 1;
while (y--)
ret *= x;
return ret;
}
template <std::size_t N, std::size_t X, typename = std::make_index_sequence<X>> struct num_to_sl_impl;
template <std::size_t N, std::size_t X, std::size_t ...Seq> struct num_to_sl_impl<N, X, std::index_sequence<Seq...>>
{
static constexpr auto func()
{
if constexpr (N >= cexpr_pow(10,X))
return num_to_sl_impl<N, X+1>::func();
else
return str_lit<(N / cexpr_pow(10,X-1-Seq) % 10 + '0')...>{};
}
};
template <std::size_t N> using num_to_sl = decltype(num_to_sl_impl<N,1>::func());
// str_lit concatenation
template <typename F, typename ...P> struct sl_cat_impl {using type = typename sl_cat_impl<F, typename sl_cat_impl<P...>::type>::type;};
template <char ...C1, char ...C2> struct sl_cat_impl<str_lit<C1...>,str_lit<C2...>> {using type = str_lit<C1..., C2...>;};
template <typename F, typename ...P> using sl_cat = typename sl_cat_impl<F, P...>::type;
// The FizzBuzz
template <std::size_t N> struct fizzbuzz_impl
{
using fizz = std::conditional_t<N % 3 == 0,
str_lit<'f','i','z','z'>,
str_lit<>>;
using buzz = std::conditional_t<N % 5 == 0,
str_lit<'b','u','z','z'>,
str_lit<>>;
using type = sl_cat<typename fizzbuzz_impl<N-1>::type,
std::conditional_t<N % 3 == 0 || N % 5 == 0,
sl_cat<fizz, buzz>,
num_to_sl<N>>,
str_lit<'\n'>>;
};
template <> struct fizzbuzz_impl<0>
{
using type = str_lit<>;
};
template <std::size_t N> using fizzbuzz = typename fizzbuzz_impl<N>::type;
用法示例:
int main()
{
std::cout << fizzbuzz<15>::value;
}
输出:
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz