Re:強いエンジニアにHelloWorldさせてみた(縛りあり)(縛り追加)

# 初心者C++Advent Calendar 2017

この記事は初心者C++Advent Calendar 2017 10日目の記事です

[<< 9日目|他言語経験者はどのように C++ を学び始めるか](https://qiita.com/euxn23/items/671266aa23bc63e16111) || [11日目|autoの推論まとめ、あるいはautoは忖度などしない件 >>](https://qiita.com/sumomoneko/items/678a05fad9e7b3be359f)

本来もっとあとに記事を出す予定でしたがなんか空いているのとネタが手に入ったのでゆるふわ記事を出します。ガチな記事は12/15までお待ち下さい。

# 元ネタ

[強いエンジニアにHelloWorldさせてみた(縛りあり)](https://qiita.com/imishinist/items/1883b5eef836aeee93ff)

すでにC++でやっている人もいて

[「強いエンジニアにHelloWorldさせてみた」をC++でやってみました。](https://qiita.com/zmtx_pp/items/55a13d75d324eccc2f84)

ですね。

# 既存のルール

- ソースコードにダブルクォート、シングルクォート、数字を書くこと無く、「LIFULL」と出力して下さい。
- 最後に改行を付けること。
- 言語は問いません。

# 追加の縛り

- C++11で、C++14ではない
- 標準ライブラリのみ使用可、外部ライブラリ(boostとか)不可
- マクロ使用禁止
- `sizeof`演算子使用禁止
- `std::array`使用禁止
- enum/enum class使用禁止
- コンパイル時に文字列を組み立てる
- 文字コードがASCII互換(UTF-8とかCP932とか)、1byte=8bitな環境で動く

# 基本戦略

まあまず文字/文字列リテラルが書けず、数字も書けないのがもとの縛りです。この時点で普通のHelloWorldじゃない。

コンパイル時に文字列を組み立てる制約を追加したので実行時引数から文字列を渡すのはできません。

マクロは禁じたので先行例みたいに`__LINE__`マクロは使えません。また`sizeof`演算子も禁じたので配列の大きさから数字を作ることもできません。

幸いC++11縛りなので市民の義務であるconstexprが使えます。

というわけで愚直にASCIIの数値列を作ってやればいいことになります。

# 詳細

## 目的の文字列はASCIIでどういう数値列か

お題は`LIFULL`なので`76, 73, 70, 85, 76, 76, 0`となります。C/C++の文字列はNULL終端することをお忘れなく。

## 0と1をどう作るか

究極0と1さえ作れればあとはこねくり回してどうにかできます。

C++で0と1・・・といえば`bool`型ですね！C++は`bool`型から整数型に暗黙変換できる言語なので遠慮なく使いましょう！

```cpp
constexpr char zero = false;
constexpr char one = true;
```

遠回りする手段としては

```cpp
#include <type_traits>
constexpr char zero = std::true_type::value;
constexpr char one = std::false_type::value;
```

という手法やもっと遠回りしてC++14でなら

```cpp:C++14
#include <limits>
constexpr char zero = std::numeric_limits<unsigned char>::min();
constexpr char one_impl()
{
    char t = zero;
    ++t;
    return t;
}
constexpr char one = one_impl();
```

という手もありますが、冗長なので素直に`bool`を使います。

## 大きい数をどうやって作るか

まあひたすら足し算や掛け算で作ってもいいんですが、流石にだるいので`std::numeric_limits`の力を借りましょう。

例えば63という数字は1byte=8bitを仮定できるので

```cpp
constexpr char two = one + one;
constexpr sixty_three = std::numeric_limits<unsigned char>::max() >> two;
```

と表せます。

別解としては
[PythonでHelloWorld(縛り)](https://qiita.com/snowdrop/items/a5306a5a4720be6c4110)
の記事でされているように、`76=2*2*19=2*2*(3*3*2+1)`を目指す方法もあります。

# 答え

あとは足し算やら引き算やら掛け算するだけですね。変数`lifull`がコンパイル時に組み上げられた文字列が格納されています。

```cpp
#include <iostream>
#include <limits>
constexpr char zero = false;
constexpr char one = true;
constexpr char two = one + one;
constexpr char seventy = (std::numeric_limits<unsigned char>::max() >> two) + (two * two * two) - one;
constexpr char seventy_three = seventy + two + one;
constexpr char seventy_six = seventy_three + two + one;
constexpr char eighty_five = seventy_six + two * two * two + one;
constexpr const char lifull[] = { seventy_six, seventy_three, seventy, eighty_five, seventy_six, seventy_six, zero };
int main()
{
   std::cout << lifull << std::endl;
}
```

https://wandbox.org/permlink/1Eb0hHAG8aV3RGyh

# ありきたりな世界

個人的にHelloWorldと聞くとありきたりな世界なのでやってみましょう。

目指すのは`arikitari_na_sekai`をデコードして`97 114 105 107 105 116 97 114 105 95 110 97 95 115 101 107 97 105 0`です。

100を超える数字がいくつも並んでいます。流石にヘルパー関数がほしいところです。

```cpp
constexpr char zero = false;
constexpr char one = true;
constexpr char two = one + one;
constexpr char three = two + one;
constexpr char four = two + two;
constexpr char five = four + one;
constexpr char six = four + two;
constexpr char seven = six + one;
constexpr char eight = four * two;
constexpr char nine = three * three;
constexpr char ten = eight + two;
constexpr char fmt(char n) { return n; }
constexpr char fmt(char n1, char n2) { return n1 * ten + n2; }
constexpr char fmt(char n1, char n2, char n3) { return fmt(n1 * ten + n2, n3); }
```

というわけでこんなものを。`char`型なのでsigned/unsignedどちらにしろ4桁以上はないので可変長テンプレートを持ち出すまでもありません。

あとはよく出てくる`a`と`i`は別に作っておくと可読性が上がりそうです。

```cpp
constexpr char a = fmt(nine, seven);
constexpr char i = fmt(one, zero, five);
```

というわけで完成品

```cpp
#include <iostream>
constexpr char zero = false;
constexpr char one = true;
constexpr char two = one + one;
constexpr char three = two + one;
constexpr char four = two + two;
constexpr char five = four + one;
constexpr char six = four + two;
constexpr char seven = six + one;
constexpr char eight = four * two;
constexpr char nine = three * three;
constexpr char ten = eight + two;
constexpr char fmt(char n) { return n; }
constexpr char fmt(char n1, char n2) { return n1 * ten + n2; }
constexpr char fmt(char n1, char n2, char n3) { return fmt(n1 * ten + n2, n3); }

constexpr char a = fmt(nine, seven);
constexpr char i = fmt(one, zero, five);
constexpr const char arikitari_na_sekai[] = {
  a, fmt(one, one, four), i, fmt(one, zero, seven), i, fmt(one, one, six), a, fmt(one, one, four), i,
  fmt(nine, five),
  fmt(one, one, zero), a,
  fmt(nine, five),
  fmt(one, one, five), fmt(one, zero, one), fmt(one, zero, seven), a, i,
  zero
};
int main()
{
  std::cout << arikitari_na_sekai << std::endl;
}
```

https://wandbox.org/permlink/gNpNllj8PHkOhHtR

# License

[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.ja)

<a href="https://commons.wikimedia.org/wiki/File:CC-BY_icon.svg#/media/File:CC-BY_icon.svg"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/16/CC-BY_icon.svg/1200px-CC-BY_icon.svg.png" alt="CC-BY icon.svg" width="88px"></a>