C++20でRustのdbg!っぽく

# はじめに
> この記事は、
> Siv3D Advent Calendar 2022
> https://qiita.com/advent-calendar/2022/siv3d
> 2日目の参加記事です。

Siv3Dで、ある値とその"ソースコード上の位置"を出力する関数を作ります。

<details><summary>Rustのdbg!って何</summary><div>

[Rust言語のマクロ](https://doc.rust-lang.org/std/macro.dbg.html)です。こういうコードを書くと、
[Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7e1df826e258b75ec8ee2688b4a9ac31)
```Rust:main.rs
fn f() -> i32 {
    1225
}
fn main(){
    let xmas = (12, 25);
    dbg!(xmas);
    dbg!(f());
}
```
こういう標準エラー出力をしてくれます。
(この記事ではRustについてはこれ以上触れません)
```:stderr
[src/main.rs:7] xmas = (
    12,
    25,
)
[src/main.rs:8] f() = 1225
```

</div></details>

# タネと仕掛け
`std::source_location`というクラスがC++20で標準ライブラリに追加されました。
何らかの関数`F`のデフォルト引数に`source_location::current()`を指定すると、
**`F`を呼び出した**位置を表す`source_location`オブジェクトが得られるようになっています。

参考:
* source_location - cpprefjp C++日本語リファレンス
https://cpprefjp.github.io/reference/source_location/source_location.html
* source_location::current - cpprefjp C++日本語リファレンス
https://cpprefjp.github.io/reference/source_location/source_location/current.html

# 環境
* OpenSiv3D v0.6.5 (Win)
* OpenSiv3D v0.6.6 (Win)
* (未確認ですがv0.6以降なら大丈夫な気がします)
  * 結局`std::source_location`が今回の本質なので

# code (& sample)
```C++:sample.cpp
# include <Siv3D.hpp> // OpenSiv3D v0.6.5

# include "dbg.hpp"

void fnA()
{
	dbg(U"dbg test");
	dbg(Scene::Time());
}

void Main()
{
	int sum = 0;

	while (System::Update())
	{
		if (KeyA.down())
		{
			fnA();
			sum += dbg(Random(5));
			dbg(sum);
		}

		if (KeyC.down())
		{
			dbg(U"Console test", Console);
		}
	}
}

```
```C++:dbg.hpp
# pragma once

# include <source_location>

template<class Output = decltype(s3d::Print)>
auto dbg(
	Output output = s3d::Print,
	std::source_location loc = std::source_location::current()
) {
	auto text = U"[{}:{}:{} {}] "_fmt(
		FileSystem::RelativePath(Unicode::Widen(loc.file_name())),
		loc.line(),
		loc.column(),
		Unicode::Widen(loc.function_name())
	);
	return std::move(output << text);
}

template<class Output = decltype(s3d::Print), s3d::Concept::Formattable Formattable>
auto&& dbg(
	Formattable&& item,
	Output output = s3d::Print,
	std::source_location loc = std::source_location::current()
) {
	dbg(output, loc) << item;
	return item;
}
```

スクリーンショット(Aキーを3回押した):![20221113-152123-516.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273953/573fc67a-b72f-7cc4-2674-f6fd4a4aa0b1.png)


## コメント
* tupleを使えば複数アイテムをdbg()1回で出力できます
* outputにはSiv3Dの各種出力の使用を想定しています
   * `Print`(画面出力)
   * `Console`(コンソール出力)
   * `Logger`(ログ出力)
   * (`Say`(音声読み上げ))
   * チュートリアル 01 | Siv3D の基本｜Siv3D リファレンス v0.6.5
1.10 Print 以外の出力
https://zenn.dev/reputeless/books/siv3d-documentation/viewer/tutorial-basic#1.10-print-%E4%BB%A5%E5%A4%96%E3%81%AE%E5%87%BA%E5%8A%9B
* 他のテンプレート引数と併用したかったので`SIV3D_CONCEPT_FORMATTABLE`ではなく`s3d::Concept::Formattable`を使用していますが
  どうせ`source_location`がC++20からなので問題ないと思っています
 
# おわりに
色々なオブジェクトの出力関数が初めから定義されているということでSiv3Dベースで進めてきましたが、
`std::cout, cerr`等にも応用できるはずです。

というか標準エラー向けなら高機能なライブラリがgithubにありました。
> sharkdp/dbg-macro: A dbg(…) macro for C++
> https://github.com/sharkdp/dbg-macro

# 参考文献
* source_location - cpprefjp C++日本語リファレンス
https://cpprefjp.github.io/reference/source_location/source_location.html
* source_location::current - cpprefjp C++日本語リファレンス
https://cpprefjp.github.io/reference/source_location/source_location/current.html
* チュートリアル 01 | Siv3D の基本｜Siv3D リファレンス v0.6.5
  1.10 Print 以外の出力
https://zenn.dev/reputeless/books/siv3d-documentation/viewer/tutorial-basic#1.10-print-%E4%BB%A5%E5%A4%96%E3%81%AE%E5%87%BA%E5%8A%9B