LoginSignup
1
1

More than 5 years have passed since last update.

Templateのお勉強がてらFizzBuzz

Last updated at Posted at 2015-01-10
int main() { return 0; }

#include <iostream>

#pragma mark Definition: Boolean

struct True { };
struct False { };

#pragma mark Definition: Equality

namespace {
    template <typename, typename>
    struct eq_impl { using type = False; };

    template <typename A>
    struct eq_impl<A, A> { using type = True; };
};

template <typename A, typename B>
using Equal = typename eq_impl<A, B>::type;

#pragma mark Definition: Natural Number

struct Zero {};
template <typename>
struct S {};

template <typename>
struct Eval;

template <>
struct Eval<Zero> {
    static constexpr int value = 0;
};
template <typename Num>
struct Eval<S<Num>> {
    static constexpr int value = 1 + Eval<Num>::value;
};

std::ostream &operator<<(std::ostream &os, const Zero &) {
    os << Eval<Zero>::value;
    return os;
}

template <typename Num>
std::ostream &operator<<(std::ostream &os, const S<Num> &) {
    os << Eval<S<Num>>::value;
    return os;
}

#pragma mark Definition: If

namespace {
    template <typename Condition, typename Then, typename Else>
    struct if_impl;

    template <typename Then, typename Else>
    struct if_impl<True, Then, Else> {
        using type = Then;
    };

    template <typename Then, typename Else>
    struct if_impl<False, Then, Else> {
        using type = Else;
    };
};

template <typename Condition, typename Then, typename Else>
using If = typename if_impl<Condition, Then, Else>::type;

#pragma mark Definition: Or

namespace {
    template <typename, typename>
    struct or_impl {
        using type = True;
    };

    template <>
    struct or_impl<False, False> {
        using type = False;
    };
};

template <typename P, typename Q>
using Or = typename or_impl<P, Q>::type;

#pragma mark Utility

namespace {
    template <int n>
    struct make_num_impl {
        using type = S<typename make_num_impl<n - 1>::type>;
    };

    template <>
    struct make_num_impl<0> {
        using type = Zero;
    };
};

template <int n>
using MakeNum = typename make_num_impl<n>::type;

struct Stop {};

struct Nothing {};
std::ostream &operator<<(std::ostream &os, const Nothing *) { return os; };

struct LineBreak { LineBreak() { std::cout << std::endl; } };

template <typename X, typename Y, typename T>
using Enable = If<Equal<X, Y>, Stop, T>;

#pragma mark Implementation of Fizz / Buzz

#define DefineFizzBuzz(Name, ...)\
template <typename Num, typename C = Zero> struct Name;\
template <typename Num, typename C> struct Name<S<Num>, C> : public Name<Num, If<Equal<C, __VA_ARGS__>, Zero, S<C>>> {};\
template <typename C> struct Name<Zero, C> { using is_enable = Equal<C, Zero>;\
    Name() { std::cout << static_cast<If<is_enable, Name, Nothing> *>(nullptr); };\
};\
template <typename Num, typename C> std::ostream &operator<<(std::ostream &os, const Name<Num, C> *) {\
    os << #Name;\
    return os;\
};

DefineFizzBuzz(Fizz, S<S<Zero>>);
DefineFizzBuzz(Buzz, S<S<S<S<Zero>>>>);

#pragma mark Implementation of Number

template <typename> struct number_impl;

template <typename Num>
struct number_impl {
    number_impl() { std::cout << Num(); }
};

template <typename Num>
using Number = If<Or<typename Fizz<Num>::is_enable, typename Buzz<Num>::is_enable>, Nothing, number_impl<Num>>;

#pragma mark FizzBuzz Composition

template <typename Num, typename Max>
struct fizz_buzz_impl : public
  Fizz<Num>
, Buzz<Num>
, Number<Num>
, LineBreak
, Enable<Num, Max, fizz_buzz_impl<S<Num>, Max>> { };

template <int n>
using FizzBuzz = fizz_buzz_impl<S<Zero>, MakeNum<n>>;

FizzBuzz<100> fizzbuzz;

wandbox

(ideoneのC++4.9.2とかC++14だと動かない!)
ideone
(FizzBuzz<100> だと動かないけど FizzBuzz<20> くらいなら動いた)

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1