a quick and dirty fizzbuzz attempt, with the goal of only one 'call' instruction at runtime (the write)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <type_traits> | |
#include <unistd.h> | |
/* | |
* std::string is not very friendly to constexpr, so we need to roll our own | |
* mechansim if we want the magic to work | |
*/ | |
template<size_t n> | |
struct ct_string | |
{ | |
char s[n]; | |
}; | |
/* | |
* "simple" concatenation | |
*/ | |
template<size_t n1, size_t n2> | |
static constexpr auto operator+(const ct_string<n1> &s1, const ct_string<n2> &s2) | |
{ | |
ct_string<n1 + n2> result = {{'\0'}}; | |
for(size_t i = 0; i < n1; i++) result.s[i] = s1.s[i]; | |
for(size_t i = 0; i < n2; i++) result.s[i + n1] = s2.s[i]; | |
return result; | |
} | |
/* | |
* need a bit of magic to convert integers to strings at compile time | |
* | |
* note: apologies for the copy-pasta; I'm sure there's a much better way to do | |
* this, but I hacked this together quickly | |
*/ | |
template<int n, | |
typename std::enable_if<n >= 10 && n < 100>::type* = nullptr> | |
static constexpr auto itoa() | |
{ | |
return ct_string<2>{n/10 + '0', n%10 + '0'}; | |
} | |
template<int n, | |
typename std::enable_if<n < 10>::type* = nullptr> | |
static constexpr auto itoa() | |
{ | |
return ct_string<1>{n + '0'}; | |
} | |
/* | |
* of course, we'll need a nice, minimalistic way to print our strings when | |
* they're done | |
*/ | |
template<size_t n> | |
static inline void write(const ct_string<n> &s) | |
{ | |
write(1, s.s, n); | |
} | |
/* | |
* pre-declare a few handy literals, since they're not very readable | |
*/ | |
static constexpr ct_string<4> fizz = {{'F', 'i', 'z', 'z'}}; | |
static constexpr ct_string<4> buzz = {{'B', 'u', 'z', 'z'}}; | |
static constexpr ct_string<1> newline = {{'\n'}}; | |
/* | |
* the business logic of how to display a number | |
*/ | |
template<int n, | |
typename std::enable_if<n % 15 == 0>::type* = nullptr> | |
static constexpr auto fizzle() | |
{ | |
return fizz + buzz; | |
} | |
template<int n, | |
typename std::enable_if<n % 5 == 0 && n % 15>::type* = nullptr> | |
static constexpr auto fizzle() | |
{ | |
return buzz; | |
} | |
template<int n, | |
typename std::enable_if<n % 3 == 0 && n % 15>::type* = nullptr> | |
static constexpr auto fizzle() | |
{ | |
return fizz; | |
} | |
template<int n, | |
typename std::enable_if<n % 3 && n % 5>::type* = nullptr> | |
static constexpr auto fizzle() | |
{ | |
return itoa<n>(); | |
} | |
/* | |
* a quick and dirty recursive version for doing the iteration | |
*/ | |
template<int i, int end, | |
typename std::enable_if<i >= end>::type* = nullptr> | |
static constexpr auto build_output() | |
{ | |
return ct_string<0>(); | |
} | |
template<int i, int end, | |
typename std::enable_if<i < end>::type* = nullptr> | |
static constexpr auto build_output() | |
{ | |
return fizzle<i>() + newline + build_output<i+1, end>(); | |
} | |
/* | |
* build the entire string at compile time | |
*/ | |
static constexpr auto output = build_output<1, 101>(); | |
int main() | |
{ | |
// runtime should just be a single call | |
write(output); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment