C++17のif constexprがあると嬉しかったシーンを具体的に

# はじめに

最近全然記事を書いていないので小ネタでも記事を書こうと思い筆を取るなどした。

if constexprについては、cpprefjpにて
[if constexpr 文 - cpprefjp C++日本語リファレンス](https://cpprefjp.github.io/lang/cpp17/if_constexpr.html)
を書いたが、未だにC++14時代にも対応したライブラリを作っているのでいまいち使う機会に恵まれなかった。

が、Minecraftというゲームにおいて、鉱石を採掘する際の手法としてチャンク依拠風車型ブランチマイニングというのを[@KizahashiLuca](https://twitter.com/KizahashiLuca)氏が提唱しているのだが
[チャンク依拠風車型ブランチマイニングの計算](https://kizahashiluca.github.io/chunk_dependent_windmill_like_branch_mining_calcurator/)
この手法のさらなる研究のためにMinecraftの生成済みワールドのセーブデータであるNBTを読み取ってパースして仮想的に自動採掘する必要ができている。でいりぃ氏という方がJavaでとりあえず作ったのだが(非公開)C++で書いてみたい。
[yumetodo / branchminingsimulator — Bitbucket](https://bitbucket.org/yumetodo/branchminingsimulator/src/master/)
しかし文字列を操作する必要がある。それも`std::basic_string_view`を使ってメモリーアロケーションを可能な限り排除して。

というわけで、
[C++でPStade.Oven(pipeline)風味なstringのsplitを作ってみた](https://qiita.com/yumetodo/items/bf2bc5c1d49d5aec3efa)
で紹介した文字列分割ライブラリを`std::basic_string_view`に対応させる必要が出てきた。
@kazatsuyu 氏にコメントで指摘されていた件に重い腰を上げて動き始めたわけだ。

マクロで環境を判定して自動で`std::basic_string_view`が使えれば対応するように目下製作中だが
[Feat: support string_view by yumetodo · Pull Request #5 · yumetodo/string_split](https://github.com/yumetodo/string_split/pull/5)
`std::basic_string_view`が使えるならそういえばif constexprが使える。ということはテストケースが少しだけシンプルに書ける。そんなことがあった。

# 状況

C++で単体テストを書いていると

```cpp:例はiutestをテストフレームワークに使用
template<typename T>
struct StringViewSplit : public ::iutest::Test {};
IUTEST_TYPED_TEST_CASE(StringViewSplit, ::iutest::Types<char, wchar_t, char16_t, char32_t>);
```

型リストを使ったテストを書くこともある。基本的にはこの型リスト全てについてテストが通るように書くのだが、処理系依存な処理を避けるために一時的に一部の型を除外したい時がある。

# C++14時代の対処法

```cpp:C++14
namespace StringSplitLvalue_SplitByStlStr {
	template<typename CharType, std::enable_if_t<!std::is_same<CharType, char>::value, std::nullptr_t> = nullptr>
	void without_char()
	{
		using char_type = CharType;
		const std::basic_string<char_type> s2 = constant::arikitarina_sekai_wspace<char_type>();
		const std::basic_string<char_type> re2_1[] = { constant::arikitarina<char_type>(), constant::sekai<char_type>() };
		const auto re2_2 = s2 | split(std::basic_string<char_type>(constant::wspace<char_type>()));
		IUTEST_ASSERT_TRUE(std::equal(std::begin(re2_1), std::end(re2_1), re2_2.begin(), re2_2.end()));
	}
	template<typename CharType, std::enable_if_t<std::is_same<CharType, char>::value, std::nullptr_t> = nullptr>
	void without_char() {}
}
IUTEST_TYPED_TEST(StringSplitLvalue, SplitByStlStr)
{
	using char_type = TypeParam;
	const std::basic_string<char_type> s1 = constant::arikitari_na_world_underscore<char_type>();
	const std::basic_string<char_type> re1_1[] = { constant::arikitari<char_type>(), constant::na<char_type>(), constant::world<char_type>() };
	const auto re1_2 = s1 | split(std::basic_string<char_type>(constant::space_underscore<char_type>()));
	IUTEST_ASSERT_TRUE(std::equal(std::begin(re1_1), std::end(re1_1), re1_2.begin(), re1_2.end()));
	StringSplitLvalue_SplitByStlStr::without_char<char_type>();
}
```

SFINAEで場合分けするヘルパー関数を作ることになる。

いや、今回の例ならtemplate関数の完全特殊化でもタグディスパッチでもいいけど。

いずれにせよテストケースの外に書かないといけなくて辛い。

# C++17時代の対処法

```c++:C++17
IUTEST_TYPED_TEST(StringViewSplit, SplitByStlStr)
{
	using char_type = TypeParam;
	const std::basic_string_view<char_type> s1 = constant::arikitari_na_world_underscore<char_type>();
	const std::basic_string_view<char_type> re1_1[] = { constant::arikitari<char_type>(), constant::na<char_type>(), constant::world<char_type>() };
	const auto re1_2 = s1 | split(std::basic_string<char_type>(constant::space_underscore<char_type>()));
	IUTEST_ASSERT_TRUE(std::equal(std::begin(re1_1), std::end(re1_1), re1_2.begin(), re1_2.end()));
	if constexpr(!std::is_same_v<char_type, char>) {
		const std::basic_string_view<char_type> s2 = constant::arikitarina_sekai_wspace<char_type>();
		const std::basic_string_view<char_type> re2_1[] = { constant::arikitarina<char_type>(), constant::sekai<char_type>() };
		const auto re2_2 = s2 | split(std::basic_string<char_type>(constant::wspace<char_type>()));
		IUTEST_ASSERT_TRUE(std::equal(std::begin(re2_1), std::end(re2_1), re2_2.begin(), re2_2.end()));
	}
}
```

すっきり。ついでに`std::is_same_v`も使える。

# 余談

それはそうと文字列リテラルにtemplateパラメータがほしい。

```cpp
"arikitari na sekai"<char16_t>
```

みたいなやつ。これがないと予めすべての文字列型について文字列リテラルをどこかに書いておいて参照するみたいな迂遠なことしないといけない。

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