はじめに
Pythonみたいなprint関数をC++で書きたい!
上の記事に触発されて自分でも書いてみた話。
方針
-
operator<<
が使える場合は優先して使う - Range, Set, Map, Tuple っぽいものが現れたらいい感じに表示する
- Python の
print
らしくするために、キーワード引数 (sep, end, file, flush) を実装する
実装
operator<<
が使えるかを知るために、今回はクラステンプレートと std::void_t
による detection idiom を用いる。
template <class Stream, class T, class Void = void>
struct is_insertable : std::false_type {};
template <class Stream, class T>
struct is_insertable<Stream, T,
std::void_t<decltype(std::declval<Stream>() << std::declval<T>())>
> : std::true_type {};
template <class Stream, class T>
constexpr bool is_insertable_v = is_insertable<Stream, T>::value;
static_assert(is_insertable_v<std::ostream &, int>);
Range などの検出も同様に行う。コンセプトがあれば楽なのかもしれない。
キーワード引数は、まあ愚直に実装する。予期せぬ代入を避けるため、名前の頭にアンダースコアをつけるのがよいと思われる (参考)。
できたもの
pyprint.hpp
C++17 必須。GCC 9.1.0、Clang 8.0.1、Visual C++ 14.2 でたぶん動いている。
例
// ... 標準ライブラリのインクルード ...
#include "./pyprint.hpp"
int main() {
using namespace std;
using namespace pyprint;
// 基本
print(42, 3.5, "Hello, world!");
// 42 3.5 Hello, world!
// Range
print(vector<vector<string>>{{"foo"s, "bar"s,}, {}, {"baz"s, "qux"s}});
// [[foo, bar], [], [baz, qux]]
// Set と Map
print(set{42, 23, 50}, unordered_map<string, int>{{"hoge"s, 15}, {"fuga"s, 21}});
// {23, 42, 50} {fuga: 21, hoge: 15}
// Tuple
print(pair{'A', "bcd"sv}, tuple{list{1, 2}, set{'A', 'B'}});
// (A, bcd) ([1, 2], {A, B})
// キーワード引数
print(42, L"Hello, error!", _file = wcerr, _sep = L'\n', _end = L"\n-- END --\n");
// 42
// Hello, error!
// -- END --
}
Q&A
Q. {fmt} とかでよいのでは?
A. せやな