LoginSignup
7
2

More than 5 years have passed since last update.

MSVCでもCPPでFizzBuzzしてみた

Posted at

前回の記事でC言語のプリプロセッサでFizzBuzzする方法を書いたのですが、GCCとかでしか動かなくてちょっとアレだなぁ、と思ったので がんばって MSVCとかTCCとかでも動くようにしてみました。
がんばって ってところがポイントです。がんばったんです!

で、どんなコードになったの?

こんなコードになりました。

fizzbuzz.c
#include "counter.h"

#ifndef FIZZBUZZ_MAX
#  define FIZZBUZZ_MAX 100
#endif

#if   COUNTER % 15 == 0
FizzBuzz
#elif COUNTER % 3  == 0
Fizz
#elif COUNTER % 5  == 0
Buzz
#else
COUNTER
#endif

#if COUNTER < FIZZBUZZ_MAX
#  include __FILE__
#endif

このコード自体は前回の__INCLUDE_LEVEL__を使ったものとそう変わらないような気がします。
しかし、このコードでは__INCLUDE_LEVEL__の代わりにCOUNTERというマクロが使われています。

このCOUNTERというマクロはもちろん、コンパイラによって組み込まれたものではなく、僕が定義したものです。そして、その定義はcounter.hの中にあります。
このcounter.hに秘密がありそうですね。

counter.h。あるいはCPPで状態を扱うということ

前回の記事で「CPPでは変数のようにマクロの更新ができない」と書きました。
それはつまり、

#define COUNTER 0

というマクロがあったとして、

#define COUNTER (COUNTER + 1)

のようにもう一度定義しなおしても、展開結果は‘(COUNTER + 1)‘のようになってしまう、ということです。
しかも、仮に(0 + 1)と展開されたとしても、CPPではそこからさらに計算を行なって1という数値にしてくれるわけではないので、とてもFizzBuzzの出力には使えそうにありません。

前回の記事ではタウンページよろしく「そんなときに__INCLUDE_LEVEL__が便利なんですよ」と言ったわけなのですが、今回は__INCLUDE_LEVEL__の無い環境でも動くようにしなければいけないので、使うことはできません。

そこでどのようにするのかと言うと‥‥。

「御託はいいからさっさとコードを見せろ!」

はい。

いやまあ、言う程すごいことはしてないんですよ。
強いて言えばちょっとがんばっただけです。

それではcounter.hのコード。

counter.h
#ifndef COUNTER_H_
#define COUNTER_H_

/*    1の位の値 */
#define COUNTER_VALUE_1 0
/*   10の位の値 */
#define COUNTER_VALUE_2 0
/*  100の位の値 */
#define COUNTER_VALUE_3 0
/* 1000の位の値 */
#define COUNTER_VALUE_4 0

#define COUNTER 0

/* 引数として与えられたものを、展開してから結合するマクロ */
#define JOIN1(x) x
#define JOIN2(x, y) JOIN2_(x, y)
#define JOIN2_(x, y) x ## y
#define JOIN3(x, y, z) JOIN3_(x, y, z)
#define JOIN3_(x, y, z) x ## y ## z
#define JOIN4(x, y, z, w) JOIN4_(x, y, z, w)
#define JOIN4_(x, y, z, w) x ## y ## z ## w

#endif /* COUNTER_H_ */

/*  1の位から処理 */
#if   COUNTER_VALUE_1 == 0
#  undef COUNTER_VALUE_1
#  define COUNTER_VALUE_1 1
#elif COUNTER_VALUE_1 == 1
#  undef COUNTER_VALUE_1
#  define COUNTER_VALUE_1 2
#elif COUNTER_VALUE_1 == 2
#  undef COUNTER_VALUE_1
#  define COUNTER_VALUE_1 3
#elif COUNTER_VALUE_1 == 3
#  undef COUNTER_VALUE_1
#  define COUNTER_VALUE_1 4
#elif COUNTER_VALUE_1 == 4
#  undef COUNTER_VALUE_1
#  define COUNTER_VALUE_1 5
#elif COUNTER_VALUE_1 == 5
#  undef COUNTER_VALUE_1
#  define COUNTER_VALUE_1 6
#elif COUNTER_VALUE_1 == 6
#  undef COUNTER_VALUE_1
#  define COUNTER_VALUE_1 7
#elif COUNTER_VALUE_1 == 7
#  undef COUNTER_VALUE_1
#  define COUNTER_VALUE_1 8
#elif COUNTER_VALUE_1 == 8
#  undef COUNTER_VALUE_1
#  define COUNTER_VALUE_1 9
#elif COUNTER_VALUE_1 == 9
#  undef COUNTER_VALUE_1
/* 10位に繰り上がる場合 */
#  define COUNTER_VALUE_1 0
#  if   COUNTER_VALUE_2 == 0
#    undef COUNTER_VALUE_2
#    define COUNTER_VALUE_2 1
#  elif COUNTER_VALUE_2 == 1
#    undef COUNTER_VALUE_2
#    define COUNTER_VALUE_2 2
#  elif COUNTER_VALUE_2 == 2
#    undef COUNTER_VALUE_2
#    define COUNTER_VALUE_2 3
#  elif COUNTER_VALUE_2 == 3
#    undef COUNTER_VALUE_2
#    define COUNTER_VALUE_2 4
#  elif COUNTER_VALUE_2 == 4
#    undef COUNTER_VALUE_2
#    define COUNTER_VALUE_2 5
#  elif COUNTER_VALUE_2 == 5
#    undef COUNTER_VALUE_2
#    define COUNTER_VALUE_2 6
#  elif COUNTER_VALUE_2 == 6
#    undef COUNTER_VALUE_2
#    define COUNTER_VALUE_2 7
#  elif COUNTER_VALUE_2 == 7
#    undef COUNTER_VALUE_2
#    define COUNTER_VALUE_2 8
#  elif COUNTER_VALUE_2 == 8
#    undef COUNTER_VALUE_2
#    define COUNTER_VALUE_2 9
#  elif COUNTER_VALUE_2 == 9
#    undef COUNTER_VALUE_2
#    define COUNTER_VALUE_2 0
/* 100の位に繰り上がる場合 */
#    if   COUNTER_VALUE_3 == 0
#      undef COUNTER_VALUE_3
#      define COUNTER_VALUE_3 1
#    elif COUNTER_VALUE_3 == 1
#      undef COUNTER_VALUE_3
#      define COUNTER_VALUE_3 2
#    elif COUNTER_VALUE_3 == 2
#      undef COUNTER_VALUE_3
#      define COUNTER_VALUE_3 3
#    elif COUNTER_VALUE_3 == 3
#      undef COUNTER_VALUE_3
#      define COUNTER_VALUE_3 4
#    elif COUNTER_VALUE_3 == 4
#      undef COUNTER_VALUE_3
#      define COUNTER_VALUE_3 5
#    elif COUNTER_VALUE_3 == 5
#      undef COUNTER_VALUE_3
#      define COUNTER_VALUE_3 6
#    elif COUNTER_VALUE_3 == 6
#      undef COUNTER_VALUE_3
#      define COUNTER_VALUE_3 7
#    elif COUNTER_VALUE_3 == 7
#      undef COUNTER_VALUE_3
#      define COUNTER_VALUE_3 8
#    elif COUNTER_VALUE_3 == 8
#      undef COUNTER_VALUE_3
#      define COUNTER_VALUE_3 9
#    elif COUNTER_VALUE_3 == 9
#      undef COUNTER_VALUE_3
#      define COUNTER_VALUE_3 0
/* さすがに1000の位まで対応しなくていいよね? */
#      define COUNTER_VALUE_4 1
#    endif
#  endif
#endif

/* 桁の数に合わせて結合するようにする */
#if   COUNTER_VALUE_2 == 0 && COUNTER_VALUE_3 == 0 && COUNTER_VALUE_4 == 0
#  undef COUNTER
#  define COUNTER JOIN1(COUNTER_VALUE_1)
#elif COUNTER_VALUE_3 == 0 && COUNTER_VALUE_4 == 0
#  undef COUNTER
#  define COUNTER JOIN2(COUNTER_VALUE_2, COUNTER_VALUE_1)
#elif COUNTER_VALUE_4 == 0
#  undef COUNTER
#  define COUNTER JOIN3(COUNTER_VALUE_3, COUNTER_VALUE_2, COUNTER_VALUE_1)
#else
#  undef COUNTER
#  define COUNTER JOIN4(COUNTER_VALUE_4, COUNTER_VALUE_3, COUNTER_VALUE_2, COUNTER_VALUE_1)
#endif

長いです。
しかも、長いわりに中身がないという、プログラマの三大美徳にひっかかりそうものになっています。

読めば分かりますが、展開結果にCOUNTERが出てこないようにするためと計算の無いCPPのために、10進数に数値を分解してインクリメントを自力で実装しています。
状態を表現するのが難しいCPPとはいえ、何だかとても残念なコードなのでした。

MSVCでの実行

Visual Studio立ち上げるの面倒だったので、clコマンドの使えるプロンプトを起動して、fizzbuzz.ccounter.hの保存されたディレクトリに移動して、

$ cl /EP fizzbuzz.c | grep -v -e "#" -v -e "^$"

でFizzBuzzが見れます。ちなみにMSVCでは#includeの深さが1024までいけるっぽいので、

$ cl /EP /DFIZZBUZZ_MAX=1000 fizzbuzz.c | grep -v -e "#" -v -e "^$"

のようにして、1000までFizzBuzzできます。

MSVCたんいじめるの楽しい! ✌(’ω’✌ )三✌(’ω’)✌三( ✌’ω’)✌

また、TCCでは再帰のレベルが32までしかサポートされてないっぽいので、

$ tcc -E -w -DFIZZBUZZ_MAX=30 fizzbuzz.c | grep -v -e "#" -v -e "^ $" -v -e "^$"

とするといいかと思います。

最後に

コンパイラいじめダメゼッタイ

7
2
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
7
2