shogo-library

競技プログラミング用の C++ ライブラリ

View the Project on GitHub shogo314/shogo-library

:heavy_check_mark: test/unittest/repr.test.cpp

Depends on

Code

#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A"

// repr.hpp の文字列化を検証する
// CHECK_REPR(値, 式) は repr(値) が「式」の文字列と一致し、さらにその式を評価すると値と等しくなることを確かめる
// (式はこのファイルで C++ としてコンパイルされるので、repr の出力が C++ の式として正しいことも確かめられる)
// 問題の入力は使わず、すべて一致したら Hello World を出力する

#include <array>
#include <iostream>
#include <map>
#include <random>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

#include "../../src/base/range.hpp"
#include "../../src/base/repr.hpp"

bool ok = true;
void check(bool cond, const std::string& what) {
    if (!cond) {
        if (ok) std::cerr << "failed: " << what << std::endl;
        ok = false;
    }
}

#define CHECK_REPR(value, ...)                                                         \
    do {                                                                               \
        const auto& value_ = (value);                                                  \
        check(repr(value_) == #__VA_ARGS__, "repr(" #value ") == " #__VA_ARGS__ " (got " + repr(value_) + ")"); \
        check(value_ == (__VA_ARGS__), "evaluating " #__VA_ARGS__);                    \
    } while (0)

// repr などのメンバ関数を持たない型
struct Plain {};
// initializer_str だけを持つ型
struct OnlyInit {
    int x;
    std::string initializer_str() const { return "{" + std::to_string(x) + "}"; }
};

// char の期待値 (愚直に作る)
std::string naive_char_repr(char c) {
    unsigned char u = static_cast<unsigned char>(c);
    switch (c) {
        case '\0': return "'\\0'";
        case '\a': return "'\\a'";
        case '\b': return "'\\b'";
        case '\f': return "'\\f'";
        case '\r': return "'\\r'";
        case '\n': return "'\\n'";
        case '\v': return "'\\v'";
        case '\'': return "'\\''";
        case '\\': return "'\\\\'";
    }
    if (32 <= u && u <= 126) return std::string("'") + c + "'";
    return "'\\" + std::to_string(u / 64) + std::to_string(u / 8 % 8) + std::to_string(u % 8) + "'";
}

int main() {
    std::mt19937 rng(314);

    // 整数・浮動小数点数
    CHECK_REPR(5, 5);
    CHECK_REPR(-3, -3);
    CHECK_REPR(5L, 5l);
    CHECK_REPR(-7LL, -7ll);
    CHECK_REPR(5U, 5u);
    CHECK_REPR(5UL, 5ul);
    CHECK_REPR(18446744073709551615ULL, 18446744073709551615ull);
    CHECK_REPR(1.5f, 1.500000f);
    CHECK_REPR(-0.25, -0.250000);
    CHECK_REPR(2.5L, 2.500000l);
    check(type_str(5) == "int" && type_str(5L) == "long" && type_str(5LL) == "long long", "type_str (signed)");
    check(type_str(5U) == "unsigned int" && type_str(5UL) == "unsigned long" && type_str(5ULL) == "unsigned long long", "type_str (unsigned)");
    check(type_str(1.0f) == "float" && type_str(1.0) == "double" && type_str(1.0L) == "long double", "type_str (floating)");
    check(initializer_str(5LL) == "5ll" && initializer_str(1.5f) == "1.500000f", "initializer_str (number)");

    // char
    CHECK_REPR('a', 'a');
    CHECK_REPR('\n', '\n');
    CHECK_REPR('\0', '\0');
    CHECK_REPR('\'', '\'');
    CHECK_REPR('\\', '\\');
    CHECK_REPR(char(1), '\001');
    CHECK_REPR(char(127), '\177');
    CHECK_REPR(char(-61), '\303');  // UTF-8 の文字のバイトなど、負の値の char
    check(type_str('a') == "char" && initializer_str('a') == "'a'", "type_str / initializer_str (char)");
    for (int i = -128; i <= 127; i++) {
        char c = static_cast<char>(i);
        check(repr(c) == naive_char_repr(c), "repr(char) for all values");
    }

    // 文字列
    {
        const char* s = "abc";
        check(repr(s) == "\"abc\"" && type_str(s) == "char*" && initializer_str(s) == "\"abc\"", "const char*");
    }
    CHECK_REPR(std::string("abc"), std::string{"abc"});
    CHECK_REPR(std::string(), std::string{""});
    check(type_str(std::string()) == "std::string" && initializer_str(std::string("x")) == "\"x\"", "type_str / initializer_str (string)");

    // コンテナ
    CHECK_REPR((std::vector<int>{1, 2, 3}), std::vector<int>{1, 2, 3});
    CHECK_REPR(std::vector<int>(), std::vector<int>{});
    CHECK_REPR((std::vector<std::vector<long long>>{{1, 2}, {}}), std::vector<std::vector<long long>>{{1ll, 2ll}, {}});
    CHECK_REPR((std::set<int>{3, 1, 2}), std::set<int>{1, 2, 3});
    CHECK_REPR((std::pair<int, std::string>{1, "a"}), std::pair<int, std::string>{1, "a"});
    CHECK_REPR((std::pair<char, double>{'x', 0.5}), std::pair<char, double>{'x', 0.500000});
    CHECK_REPR((std::map<int, char>{{2, 'b'}, {1, 'a'}}), std::map<int, char>{{1, 'a'}, {2, 'b'}});
    CHECK_REPR((std::map<std::string, std::vector<int>>{{"k", {1}}}), std::map<std::string, std::vector<int>>{{"k", {1}}});
    CHECK_REPR((std::array<int, 3>{1, 2, 3}), std::array<int, 3>{1, 2, 3});
    CHECK_REPR((std::vector<std::pair<int, unsigned>>{{1, 2u}}), std::vector<std::pair<int, unsigned int>>{{1, 2u}});
    check(type_str(std::vector<std::set<long long>>()) == "std::vector<std::set<long long>>", "type_str (nested)");
    check(initializer_str(std::vector<int>{1, 2}) == "{1, 2}", "initializer_str (vector)");

    // repr / type_str / initializer_str をメンバ関数に持つ型 (Range)
    CHECK_REPR(Range(0, 10, 2), Range(0, 10, 2));
    CHECK_REPR((std::vector<Range>{Range(0, 3, 1)}), std::vector<Range>{{0, 3, 1}});
    check(type_str(Range(5)) == "Range" && initializer_str(Range(1, 5, 2)) == "{1, 5, 2}", "Range");

    // メンバ関数を持たない型は "T" と "{}"
    check(type_str(Plain{}) == "T" && initializer_str(Plain{}) == "{}" && repr(Plain{}) == "T{}", "Plain");
    // initializer_str だけを持つ型
    check(repr(OnlyInit{7}) == "T{7}" && initializer_str(std::vector<OnlyInit>{{1}, {2}}) == "{{1}, {2}}", "OnlyInit");

    // ランダム: 整数の vector
    for (int rep = 0; rep < 1000 && ok; rep++) {
        int n = rng() % 5;
        std::vector<long long> v(n);
        std::string expect = "std::vector<long long>{";
        for (int i = 0; i < n; i++) {
            v[i] = (long long)(rng() % 2001) - 1000;
            expect += (i ? ", " : "") + std::to_string(v[i]) + "ll";
        }
        expect += "}";
        check(repr(v) == expect, "repr(vector<long long>) (random)");
    }

    // DEBUG(x) は std::cerr に "x = repr(x)" を出力する
    {
        std::ostringstream buf;
        std::streambuf* old = std::cerr.rdbuf(buf.rdbuf());
        std::vector<int> xs{1, 2};
        DEBUG(xs);
        std::cerr.rdbuf(old);
        check(buf.str() == "xs = std::vector<int>{1, 2}\n", "DEBUG");
    }

    if (ok) std::cout << "Hello World" << std::endl;
}
#line 1 "test/unittest/repr.test.cpp"
#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A"

// repr.hpp の文字列化を検証する
// CHECK_REPR(値, 式) は repr(値) が「式」の文字列と一致し、さらにその式を評価すると値と等しくなることを確かめる
// (式はこのファイルで C++ としてコンパイルされるので、repr の出力が C++ の式として正しいことも確かめられる)
// 問題の入力は使わず、すべて一致したら Hello World を出力する

#include <array>
#include <iostream>
#include <map>
#include <random>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

#line 2 "src/base/range.hpp"

/**
 * @file range.hpp
 * @brief Python の range のような整数列
 */

#include <cassert>
#include <cstddef>
#include <iterator>
#line 12 "src/base/range.hpp"
#include <type_traits>
#line 15 "src/base/range.hpp"

struct Range {
   public:
    using value_type = long long;
    using const_reference = const value_type&;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;

    const value_type start;
    const value_type stop;
    const value_type step;

   private:
    const value_type norm_stop;

    constexpr static value_type normalize(value_type _start, value_type _stop, value_type _step) {
        assert(_step != 0);
        if (_step > 0) {
            if (_start >= _stop) {
                return _start;
            } else {
                return _start + (_stop - _start + _step - 1) / _step * _step;
            }
        } else {
            if (_start <= _stop) {
                return _start;
            } else {
                return _start - (_start - _stop - _step - 1) / _step * _step;
            }
        }
    }

    constexpr Range(value_type _start, value_type _stop, value_type _step, value_type _norm_stop)
        : start(_start), stop(_stop), step(_step), norm_stop(_norm_stop) {
    }

   public:
    /**
     * @brief start から stop の手前まで step ずつ進む整数列
     * @pre step != 0
     */
    constexpr Range(value_type _start, value_type _stop, value_type _step)
        : start(_start), stop(_stop), step(_step), norm_stop(normalize(_start, _stop, _step)) {
    }
    constexpr Range(value_type _start, value_type _stop)
        : start(_start), stop(_stop), step(1), norm_stop(normalize(_start, _stop, 1)) {}
    constexpr Range(value_type _stop)
        : start(0), stop(_stop), step(1), norm_stop(normalize(0, _stop, 1)) {}
    constexpr Range()
        : start(0), stop(0), step(1), norm_stop(0) {
    }
    constexpr Range(const Range& o) = default;
    Range& operator=(const Range& o) = delete;

    struct const_iterator {
       public:
        using difference_type = Range::difference_type;
        using value_type = Range::value_type;
        using pointer = const Range::value_type*;
        using reference = const Range::value_type&;
        using iterator_category = std::random_access_iterator_tag;

       private:
        const Range& range;
        value_type value;

        constexpr bool same_range(const const_iterator& o) const noexcept {
            return range.start == o.range.start and range.stop == o.range.stop and range.step == o.range.step;
        }

       public:
        constexpr const_iterator(const Range& _range, value_type _value) noexcept
            : range(_range), value(_value) {}
        constexpr const_iterator(const const_iterator& o) noexcept
            : range(o.range), value(o.value) {}
        constexpr reference base() const noexcept {
            return value;
        }
        constexpr const_iterator& operator=(const const_iterator& o) {
            assert(same_range(o));
            value = o.value;
            return *this;
        };
        constexpr const_iterator& operator++() noexcept {
            value += range.step;
            return *this;
        }
        constexpr const_iterator operator++(int) noexcept {
            const_iterator ret(*this);
            value += range.step;
            return ret;
        }
        constexpr const_iterator& operator+=(difference_type _n) noexcept {
            value += _n * range.step;
            return *this;
        }
        constexpr friend const_iterator operator+(const const_iterator& i, difference_type _n) noexcept {
            const_iterator ret(i);
            ret += _n;
            return ret;
        }
        constexpr friend const_iterator operator+(difference_type _n, const const_iterator& i) noexcept {
            const_iterator ret(i);
            ret += _n;
            return ret;
        }
        constexpr const_iterator& operator--() noexcept {
            value -= range.step;
            return *this;
        }
        constexpr const_iterator operator--(int) noexcept {
            const_iterator ret(*this);
            value -= range.step;
            return ret;
        }
        constexpr const_iterator& operator-=(difference_type _n) noexcept {
            value -= _n * range.step;
            return *this;
        }
        constexpr friend const_iterator operator-(const const_iterator& i, difference_type _n) noexcept {
            const_iterator ret(i);
            ret -= _n;
            return ret;
        }
        constexpr reference operator*() const noexcept {
            return value;
        }
        constexpr friend bool operator!=(const const_iterator& a, const const_iterator& b) noexcept {
            return !a.same_range(b) or a.value != b.value;
        }
        constexpr friend bool operator==(const const_iterator& a, const const_iterator& b) noexcept {
            return !(a != b);
        }
        constexpr friend difference_type operator-(const const_iterator& a, const const_iterator& b) {
            assert(a.same_range(b));
            if (a.range.step > 0) {
                return (a.value - b.value) / a.range.step;
            } else {
                return (b.value - a.value) / (-a.range.step);
            }
        }
    };

    constexpr const_iterator begin() const noexcept {
        return {*this, start};
    }
    constexpr const_iterator end() const noexcept {
        return {*this, norm_stop};
    }
    /**
     * @brief x の個数 (0 か 1)
     */
    constexpr difference_type count(value_type x) const noexcept {
        if (step > 0) {
            if (start <= x and x < stop) {
                if ((x - start) % step == 0) {
                    return 1;
                }
            }
        } else {
            if (stop < x and x <= start) {
                if ((start - x) % (-step) == 0) {
                    return 1;
                }
            }
        }
        return 0;
    }
    /**
     * @brief x が含まれるか
     */
    constexpr bool contains(value_type x) const noexcept {
        return count(x) != 0;
    }
    /**
     * @brief x のインデックス
     * x が含まれないとエラー
     */
    constexpr size_type index(value_type x) const {
        assert(contains(x));
        return (x - start) / step;
    }
    /**
     * @brief 要素が x のイテレータ
     * x が含まれないと end() を返す
     */
    constexpr const_iterator find(value_type x) const noexcept {
        if (contains(x)) {
            return {*this, x};
        } else {
            return {*this, norm_stop};
        }
    }
    /**
     * @brief 要素が x 以上の最小のイテレータ
     * step > 0 じゃないとエラー
     */
    constexpr const_iterator lower_bound(value_type x) const {
        assert(step > 0);
        if (norm_stop <= x) {
            return end();
        } else if (x < start) {
            return begin();
        } else {
            return {*this, start + (x - start + step - 1) / step * step};
        }
    }
    /**
     * @brief 要素が x より大きい最小のイテレータ
     * step > 0 じゃないとエラー
     */
    constexpr const_iterator upper_bound(value_type x) const {
        assert(step > 0);
        if (norm_stop <= x) {
            return end();
        } else if (x < start) {
            return begin();
        } else {
            return {*this, start + (x - start + step) / step * step};
        }
    }
    /**
     * @brief コンテナの要素数
     */
    constexpr size_type size() const noexcept {
        if (step > 0) {
            return (norm_stop - start) / step;
        } else {
            return (start - norm_stop) / (-step);
        }
    }
    constexpr value_type at(size_type i) const {
        assert(i < size());
        return start + i * step;
    }
    constexpr value_type operator[](size_type i) const {
        return at(i);
    }
    constexpr bool empty() const noexcept {
        return start == norm_stop;
    }
    constexpr value_type sum() const noexcept {
        return static_cast<Range::value_type>(size()) * (norm_stop - step + start) / 2;
    }
    /**
     * @brief 最小値
     * 空だとエラー
     */
    constexpr value_type min() const {
        assert(not empty());
        if (step > 0) {
            return start;
        } else {
            return norm_stop - step;
        }
    }
    /**
     * @brief 最大値
     * 空だとエラー
     */
    constexpr value_type max() const {
        assert(not empty());
        if (step > 0) {
            return norm_stop - step;
        } else {
            return start;
        }
    }
    /**
     * @brief 総 xor
     * 負を含むとエラー
     */
    constexpr value_type product_xor() const noexcept {
        if (empty()) return 0;
        assert(min() >= 0);
        Range sorted_range(sorted());
        value_type res = 0;
        std::make_unsigned_t<value_type> t = 1;
        while (t <= static_cast<std::make_unsigned_t<value_type>>(max())) {
            std::make_unsigned_t<value_type> tmp = 0, n = size(), m = t, a = sorted_range.step, b = sorted_range.start;
            while (true) {
                if (a >= m) {
                    tmp += n * (n - 1) / 2 * (a / m);
                    a %= m;
                }
                if (b >= m) {
                    tmp += n * (b / m);
                    b %= m;
                }
                std::make_unsigned_t<value_type> y_max = a * n + b;
                if (y_max < m) break;
                n = y_max / m;
                b = y_max % m;
                std::swap(m, a);
            }
            if (tmp % 2 == 1) {
                res += t;
            }
            t <<= 1;
        }
        return res;
    }
    /**
     * @brief 逆順にしたもの
     */
    constexpr Range reversed() const noexcept {
        return {norm_stop - step, start - step, -step, start - step};
    }
    /**
     * @brief step > 0 だとそのまま step < 0 だと逆順にしたもの
     */
    constexpr Range sorted() const noexcept {
        if (step > 0) {
            return {start, norm_stop, step, norm_stop};
        } else {
            return {norm_stop - step, start - step, -step, start - step};
        }
    }
    /**
     * @brief 要素が [l,r] である範囲を返す
     */
    constexpr Range clipped(value_type l, value_type r) const {
        if (l > r) return {};
        if (step > 0) {
            value_type _start = [&] {
                if (norm_stop <= l) {
                    return norm_stop;
                } else if (l < start) {
                    return start;
                } else {
                    return start + (l - start + step - 1) / step * step;
                }
            }();
            value_type _stop = [&] {
                if (norm_stop <= r) {
                    return norm_stop;
                } else if (r < start) {
                    return start;
                } else {
                    return start + (r - start + step) / step * step;
                }
            }();
            return {_start, _stop, step, _stop};
        } else {
            value_type _start = [&] {
                if (start - step <= r) {
                    return start;
                } else if (r < norm_stop - step) {
                    return norm_stop;
                } else {
                    return norm_stop + (r - norm_stop) / step * step;
                }
            }();
            value_type _stop = [&] {
                if (start - step <= l) {
                    return start;
                } else if (l < norm_stop - step) {
                    return norm_stop;
                } else {
                    return norm_stop + (l - norm_stop - 1) / step * step;
                }
            }();
            return {_start, _stop, step, _stop};
        }
    }
    /**
     * @brief 要素を Range で指定したコンテナ
     */
    constexpr Range slice(const Range& r) const {
        if (r.empty()) return {};
        if (r.max() < 0) return {};
        if (r.min() >= static_cast<Range::value_type>(size())) return {};
        Range trimmed_range(r.clipped(0, static_cast<Range::value_type>(size()) - 1));
        if (trimmed_range.empty()) return {};
        value_type _step = step * trimmed_range.step;
        value_type _start = at(trimmed_range.start);
        value_type _stop = _start + _step * static_cast<Range::value_type>(trimmed_range.size());
        return {_start, _stop, _step, _stop};
    }

    static std::string type_str() {
        return "Range";
    }
    std::string initializer_str() const {
        return "{" + std::to_string(start) + ", " + std::to_string(stop) + ", " + std::to_string(step) + "}";
    }
    std::string repr() const {
        return "Range(" + std::to_string(start) + ", " + std::to_string(stop) + ", " + std::to_string(step) + ")";
    }

    std::vector<value_type> to_vector() const {
        std::vector<value_type> ret;
        ret.reserve(size());
        if (step > 0) {
            for (value_type x = start; x < stop; x += step) {
                ret.push_back(x);
            }
        } else {
            for (value_type x = start; x > stop; x += step) {
                ret.push_back(x);
            }
        }
        return ret;
    }
    constexpr friend bool operator==(const Range& a, const Range& b) noexcept {
        if (a.empty() and b.empty()) return true;
        if (a.size() != b.size()) return false;
        if (a.start != b.start) return false;
        if (a.size() == 1) return true;
        if (a.step != b.step) return false;
        return true;
    }
    constexpr friend bool operator!=(const Range& a, const Range& b) noexcept {
        return !(a == b);
    }
};
#line 2 "src/base/repr.hpp"

/**
 * @file repr.hpp
 * @brief 出力をそのままコンストラクタに使える文字列化 (repr)
 */

#line 2 "src/base/traits.hpp"

/**
 * @file traits.hpp
 * @brief 型特性
 */

#line 13 "src/base/traits.hpp"

namespace detail {
using std::begin, std::end;

template <class T, class = void>
struct is_range_impl : std::false_type {};
template <class T>
struct is_range_impl<T, std::void_t<decltype(begin(std::declval<T&>()), end(std::declval<T&>()))>> : std::true_type {};
}  // namespace detail

template <class T>
struct is_range : detail::is_range_impl<T>::type {};
template <class T>
inline constexpr bool is_range_v = is_range<T>::value;

#define HAS_METHOD(func_name)                                                              \
    namespace detail {                                                                     \
    template <class T, class = void>                                                       \
    struct has_##func_name##_impl : std::false_type {};                                    \
    template <class T>                                                                     \
    struct has_##func_name##_impl<T, std::void_t<decltype(std::declval<T>().func_name())>> \
        : std::true_type {};                                                               \
    }                                                                                      \
    template <class T>                                                                     \
    struct has_##func_name : detail::has_##func_name##_impl<T>::type {};                   \
    template <class T>                                                                     \
    inline constexpr bool has_##func_name##_v = has_##func_name<T>::value;

#define HAS_METHOD_ARG(func_name)                                                                              \
    namespace detail {                                                                                         \
    template <class T, typename U, class = void>                                                               \
    struct has_##func_name##_impl : std::false_type {};                                                        \
    template <class T, typename U>                                                                             \
    struct has_##func_name##_impl<T, U, std::void_t<decltype(std::declval<T>().func_name(std::declval<U>()))>> \
        : std::true_type {};                                                                                   \
    }                                                                                                          \
    template <class T, typename U>                                                                             \
    struct has_##func_name : detail::has_##func_name##_impl<T, U>::type {};                                    \
    template <class T, typename U>                                                                             \
    inline constexpr bool has_##func_name##_v = has_##func_name<T, U>::value;

HAS_METHOD(repr)
HAS_METHOD(type_str)
HAS_METHOD(initializer_str)
HAS_METHOD(max)
HAS_METHOD(min)
HAS_METHOD(reversed)
HAS_METHOD(sorted)
HAS_METHOD(sum)
HAS_METHOD(product)
HAS_METHOD(product_xor)
HAS_METHOD_ARG(count)
HAS_METHOD_ARG(find)
HAS_METHOD_ARG(lower_bound)
HAS_METHOD_ARG(upper_bound)

#define ENABLE_IF_T_IMPL(expr) std::enable_if_t<expr, std::nullptr_t> = nullptr
#define ENABLE_IF_T(...) ENABLE_IF_T_IMPL((__VA_ARGS__))

template <class C>
using mem_value_type = typename C::value_type;
template <class C>
using mem_difference_type = typename C::difference_type;

namespace detail {
template <class T, class = void>
struct is_sorted_container_impl : std::false_type {};
template <class T>
struct is_sorted_container_impl<std::set<T>> : std::true_type {};
template <class T>
struct is_sorted_container_impl<std::multiset<T>> : std::true_type {};
}  // namespace detail
template <class T>
struct is_sorted_container : detail::is_sorted_container_impl<T>::type {};
template <class T>
inline constexpr bool is_sorted_container_v = is_sorted_container<T>::value;
#line 17 "src/base/repr.hpp"

namespace detail {

///////////////////////////////////////////////////////////////////
/////////////////////////// declaration ///////////////////////////
///////////////////////////////////////////////////////////////////

inline std::string type_str(const char*);
inline std::string initializer_str(const char*);
inline std::string repr(const char*);

inline std::string type_str(const char&);
inline std::string initializer_str(const char&);
inline std::string repr(const char&);

inline std::string type_str(const int&);
inline std::string initializer_str(const int&);
inline std::string repr(const int&);

inline std::string type_str(const long&);
inline std::string initializer_str(const long&);
inline std::string repr(const long&);

inline std::string type_str(const long long&);
inline std::string initializer_str(const long long&);
inline std::string repr(const long long&);

inline std::string type_str(const unsigned int&);
inline std::string initializer_str(const unsigned int&);
inline std::string repr(const unsigned int&);

inline std::string type_str(const unsigned long&);
inline std::string initializer_str(const unsigned long&);
inline std::string repr(const unsigned long&);

inline std::string type_str(const unsigned long long&);
inline std::string initializer_str(const unsigned long long&);
inline std::string repr(const unsigned long long&);

inline std::string type_str(const std::string&);
inline std::string initializer_str(const std::string&);
inline std::string repr(const std::string&);

inline std::string type_str(const float&);
inline std::string initializer_str(const float&);
inline std::string repr(const float&);

inline std::string type_str(const double&);
inline std::string initializer_str(const double&);
inline std::string repr(const double&);

inline std::string type_str(const long double&);
inline std::string initializer_str(const long double&);
inline std::string repr(const long double&);

template <typename T>
inline std::string type_str(const std::vector<T>&);
template <typename T>
inline std::string initializer_str(const std::vector<T>&);
template <typename T>
inline std::string repr(const std::vector<T>&);

template <typename T>
inline std::string type_str(const std::set<T>&);
template <typename T>
inline std::string initializer_str(const std::set<T>&);
template <typename T>
inline std::string repr(const std::set<T>&);

template <typename T1, typename T2>
inline std::string type_str(const std::pair<T1, T2>&);
template <typename T1, typename T2>
inline std::string initializer_str(const std::pair<T1, T2>&);
template <typename T1, typename T2>
inline std::string repr(const std::pair<T1, T2>&);

template <typename T1, typename T2>
inline std::string type_str(const std::map<T1, T2>&);
template <typename T1, typename T2>
inline std::string initializer_str(const std::map<T1, T2>&);
template <typename T1, typename T2>
inline std::string repr(const std::map<T1, T2>&);

template <typename T, size_t N>
inline std::string type_str(const std::array<T, N>&);
template <typename T, size_t N>
inline std::string initializer_str(const std::array<T, N>&);
template <typename T, size_t N>
inline std::string repr(const std::array<T, N>&);

///////////////////////////////////////////////////////////////////
/////////////////////////// definition ////////////////////////////
///////////////////////////////////////////////////////////////////

template <typename T, std::enable_if_t<not has_type_str_v<T>, std::nullptr_t> = nullptr>
inline std::string type_str(const T& t) {
    return "T";
}
template <typename T, std::enable_if_t<has_type_str_v<T>, std::nullptr_t> = nullptr>
inline std::string type_str(const T& t) {
    return t.type_str();
}
template <typename T, std::enable_if_t<not has_initializer_str_v<T>, std::nullptr_t> = nullptr>
inline std::string initializer_str(const T& t) {
    return "{}";
}
template <typename T, std::enable_if_t<has_initializer_str_v<T>, std::nullptr_t> = nullptr>
inline std::string initializer_str(const T& t) {
    return t.initializer_str();
}
template <typename T, std::enable_if_t<not has_repr_v<T>, std::nullptr_t> = nullptr>
inline std::string repr(const T& t) {
    return detail::type_str(t) + detail::initializer_str(t);
}
template <typename T, std::enable_if_t<has_repr_v<T>, std::nullptr_t> = nullptr>
inline std::string repr(const T& t) {
    return t.repr();
}

inline std::string type_str(const char*) {
    return "char*";
}
inline std::string initializer_str(const char* t) {
    return "\"" + std::string(t) + "\"";
}
inline std::string repr(const char* t) {
    return detail::initializer_str(t);
}

inline std::string type_str(const char&) {
    return "char";
}
inline std::string initializer_str(const char& t) {
    // char が符号付きでも添字が負にならないように unsigned char で扱う (UTF-8 のバイトなど)
    const unsigned char u = static_cast<unsigned char>(t);
    std::vector<std::string> v(128, "");
    v['\0'] = "\\0";
    v['\a'] = "\\a";
    v['\b'] = "\\b";
    v['\f'] = "\\f";
    v['\r'] = "\\r";
    v['\n'] = "\\n";
    v['\v'] = "\\v";
    v['\''] = "\\'";
    v['\\'] = "\\\\";
    if (u < 128 and !v[u].empty())
        return "'" + v[u] + "'";
    if (32 <= u and u <= 126)
        return "'" + std::string{t} + "'";
    return "'\\" + std::to_string(u / 64) + std::to_string(u / 8 % 8) + std::to_string(u % 8) + "'";
}
inline std::string repr(const char& t) {
    return detail::initializer_str(t);
}

inline std::string type_str(const int&) {
    return "int";
}
inline std::string initializer_str(const int& t) {
    return std::to_string(t);
}
inline std::string repr(const int& t) {
    return detail::initializer_str(t);
}

inline std::string type_str(const long&) {
    return "long";
}
inline std::string initializer_str(const long& t) {
    return std::to_string(t) + "l";
}
inline std::string repr(const long& t) {
    return detail::initializer_str(t);
}

inline std::string type_str(const long long&) {
    return "long long";
}
inline std::string initializer_str(const long long& t) {
    return std::to_string(t) + "ll";
}
inline std::string repr(const long long& t) {
    return detail::initializer_str(t);
}

inline std::string type_str(const unsigned int&) {
    return "unsigned int";
}
inline std::string initializer_str(const unsigned int& t) {
    return std::to_string(t) + "u";
}
inline std::string repr(const unsigned int& t) {
    return detail::initializer_str(t);
}

inline std::string type_str(const unsigned long&) {
    return "unsigned long";
}
inline std::string initializer_str(const unsigned long& t) {
    return std::to_string(t) + "ul";
}
inline std::string repr(const unsigned long& t) {
    return detail::initializer_str(t);
}

inline std::string type_str(const unsigned long long&) {
    return "unsigned long long";
}
inline std::string initializer_str(const unsigned long long& t) {
    return std::to_string(t) + "ull";
}
inline std::string repr(const unsigned long long& t) {
    return detail::initializer_str(t);
}

inline std::string type_str(const std::string&) {
    return "std::string";
}
inline std::string initializer_str(const std::string& t) {
    return "\"" + t + "\"";
}
inline std::string repr(const std::string& t) {
    return detail::type_str(t) + "{" + detail::initializer_str(t) + "}";
}

inline std::string type_str(const float&) {
    return "float";
}
inline std::string initializer_str(const float& t) {
    return std::to_string(t) + "f";
}
inline std::string repr(const float& t) {
    return detail::initializer_str(t);
}

inline std::string type_str(const double&) {
    return "double";
}
inline std::string initializer_str(const double& t) {
    return std::to_string(t);
}
inline std::string repr(const double& t) {
    return detail::initializer_str(t);
}

inline std::string type_str(const long double&) {
    return "long double";
}
inline std::string initializer_str(const long double& t) {
    return std::to_string(t) + "l";
}
inline std::string repr(const long double& t) {
    return detail::initializer_str(t);
}

template <typename T>
inline std::string type_str(const std::vector<T>&) {
    T t;
    return "std::vector<" + detail::type_str(t) + ">";
}
template <typename T>
inline std::string initializer_str(const std::vector<T>& t) {
    std::string ret;
    ret += "{";
    for (auto itr = t.begin(); itr != t.end(); itr++) {
        if (itr != t.begin()) ret += ", ";
        ret += detail::initializer_str(*itr);
    }
    ret += "}";
    return ret;
}
template <typename T>
inline std::string repr(const std::vector<T>& t) {
    return detail::type_str(t) + detail::initializer_str(t);
}

template <typename T>
inline std::string type_str(const std::set<T>&) {
    T t;
    return "std::set<" + detail::type_str(t) + ">";
}
template <typename T>
inline std::string initializer_str(const std::set<T>& t) {
    std::string ret;
    ret += "{";
    for (auto itr = t.begin(); itr != t.end(); itr++) {
        if (itr != t.begin()) ret += ", ";
        ret += detail::initializer_str(*itr);
    }
    ret += "}";
    return ret;
}
template <typename T>
inline std::string repr(const std::set<T>& t) {
    return detail::type_str(t) + detail::initializer_str(t);
}

template <typename T1, typename T2>
inline std::string type_str(const std::pair<T1, T2>&) {
    T1 t1;
    T2 t2;
    return "std::pair<" + detail::type_str(t1) + ", " + detail::type_str(t2) + ">";
}
template <typename T1, typename T2>
inline std::string initializer_str(const std::pair<T1, T2>& t) {
    return "{" + detail::initializer_str(t.first) + ", " + detail::initializer_str(t.second) + "}";
}
template <typename T1, typename T2>
inline std::string repr(const std::pair<T1, T2>& t) {
    return detail::type_str(t) + detail::initializer_str(t);
}

template <typename T1, typename T2>
inline std::string type_str(const std::map<T1, T2>&) {
    T1 t1;
    T2 t2;
    return "std::map<" + detail::type_str(t1) + ", " + detail::type_str(t2) + ">";
}
template <typename T1, typename T2>
inline std::string initializer_str(const std::map<T1, T2>& t) {
    std::string ret;
    ret += "{";
    for (auto itr = t.begin(); itr != t.end(); itr++) {
        if (itr != t.begin()) ret += ", ";
        ret += "{";
        ret += detail::initializer_str(itr->first);
        ret += ", ";
        ret += detail::initializer_str(itr->second);
        ret += "}";
    }
    ret += "}";
    return ret;
}
template <typename T1, typename T2>
inline std::string repr(const std::map<T1, T2>& t) {
    return detail::type_str(t) + detail::initializer_str(t);
}

template <typename T, size_t N>
inline std::string type_str(const std::array<T, N>&) {
    T t;
    return "std::array<" + detail::type_str(t) + ", " + std::to_string(N) + ">";
}
template <typename T, size_t N>
inline std::string initializer_str(const std::array<T, N>& t) {
    std::string ret;
    ret += "{";
    for (auto itr = t.begin(); itr != t.end(); itr++) {
        if (itr != t.begin()) ret += ", ";
        ret += detail::initializer_str(*itr);
    }
    ret += "}";
    return ret;
}
template <typename T, size_t N>
inline std::string repr(const std::array<T, N>& t) {
    return detail::type_str(t) + detail::initializer_str(t);
}

}  // namespace detail

template <typename T>
inline std::string type_str(const T& t) {
    return detail::type_str(t);
}
template <typename T>
inline std::string initializer_str(const T& t) {
    return detail::initializer_str(t);
}
template <typename T>
inline std::string repr(const T& t) {
    return detail::repr(t);
}

#define DEBUG(x) { std::cerr << #x << " = " << repr(x) << std::endl; }
#line 20 "test/unittest/repr.test.cpp"

bool ok = true;
void check(bool cond, const std::string& what) {
    if (!cond) {
        if (ok) std::cerr << "failed: " << what << std::endl;
        ok = false;
    }
}

#define CHECK_REPR(value, ...)                                                         \
    do {                                                                               \
        const auto& value_ = (value);                                                  \
        check(repr(value_) == #__VA_ARGS__, "repr(" #value ") == " #__VA_ARGS__ " (got " + repr(value_) + ")"); \
        check(value_ == (__VA_ARGS__), "evaluating " #__VA_ARGS__);                    \
    } while (0)

// repr などのメンバ関数を持たない型
struct Plain {};
// initializer_str だけを持つ型
struct OnlyInit {
    int x;
    std::string initializer_str() const { return "{" + std::to_string(x) + "}"; }
};

// char の期待値 (愚直に作る)
std::string naive_char_repr(char c) {
    unsigned char u = static_cast<unsigned char>(c);
    switch (c) {
        case '\0': return "'\\0'";
        case '\a': return "'\\a'";
        case '\b': return "'\\b'";
        case '\f': return "'\\f'";
        case '\r': return "'\\r'";
        case '\n': return "'\\n'";
        case '\v': return "'\\v'";
        case '\'': return "'\\''";
        case '\\': return "'\\\\'";
    }
    if (32 <= u && u <= 126) return std::string("'") + c + "'";
    return "'\\" + std::to_string(u / 64) + std::to_string(u / 8 % 8) + std::to_string(u % 8) + "'";
}

int main() {
    std::mt19937 rng(314);

    // 整数・浮動小数点数
    CHECK_REPR(5, 5);
    CHECK_REPR(-3, -3);
    CHECK_REPR(5L, 5l);
    CHECK_REPR(-7LL, -7ll);
    CHECK_REPR(5U, 5u);
    CHECK_REPR(5UL, 5ul);
    CHECK_REPR(18446744073709551615ULL, 18446744073709551615ull);
    CHECK_REPR(1.5f, 1.500000f);
    CHECK_REPR(-0.25, -0.250000);
    CHECK_REPR(2.5L, 2.500000l);
    check(type_str(5) == "int" && type_str(5L) == "long" && type_str(5LL) == "long long", "type_str (signed)");
    check(type_str(5U) == "unsigned int" && type_str(5UL) == "unsigned long" && type_str(5ULL) == "unsigned long long", "type_str (unsigned)");
    check(type_str(1.0f) == "float" && type_str(1.0) == "double" && type_str(1.0L) == "long double", "type_str (floating)");
    check(initializer_str(5LL) == "5ll" && initializer_str(1.5f) == "1.500000f", "initializer_str (number)");

    // char
    CHECK_REPR('a', 'a');
    CHECK_REPR('\n', '\n');
    CHECK_REPR('\0', '\0');
    CHECK_REPR('\'', '\'');
    CHECK_REPR('\\', '\\');
    CHECK_REPR(char(1), '\001');
    CHECK_REPR(char(127), '\177');
    CHECK_REPR(char(-61), '\303');  // UTF-8 の文字のバイトなど、負の値の char
    check(type_str('a') == "char" && initializer_str('a') == "'a'", "type_str / initializer_str (char)");
    for (int i = -128; i <= 127; i++) {
        char c = static_cast<char>(i);
        check(repr(c) == naive_char_repr(c), "repr(char) for all values");
    }

    // 文字列
    {
        const char* s = "abc";
        check(repr(s) == "\"abc\"" && type_str(s) == "char*" && initializer_str(s) == "\"abc\"", "const char*");
    }
    CHECK_REPR(std::string("abc"), std::string{"abc"});
    CHECK_REPR(std::string(), std::string{""});
    check(type_str(std::string()) == "std::string" && initializer_str(std::string("x")) == "\"x\"", "type_str / initializer_str (string)");

    // コンテナ
    CHECK_REPR((std::vector<int>{1, 2, 3}), std::vector<int>{1, 2, 3});
    CHECK_REPR(std::vector<int>(), std::vector<int>{});
    CHECK_REPR((std::vector<std::vector<long long>>{{1, 2}, {}}), std::vector<std::vector<long long>>{{1ll, 2ll}, {}});
    CHECK_REPR((std::set<int>{3, 1, 2}), std::set<int>{1, 2, 3});
    CHECK_REPR((std::pair<int, std::string>{1, "a"}), std::pair<int, std::string>{1, "a"});
    CHECK_REPR((std::pair<char, double>{'x', 0.5}), std::pair<char, double>{'x', 0.500000});
    CHECK_REPR((std::map<int, char>{{2, 'b'}, {1, 'a'}}), std::map<int, char>{{1, 'a'}, {2, 'b'}});
    CHECK_REPR((std::map<std::string, std::vector<int>>{{"k", {1}}}), std::map<std::string, std::vector<int>>{{"k", {1}}});
    CHECK_REPR((std::array<int, 3>{1, 2, 3}), std::array<int, 3>{1, 2, 3});
    CHECK_REPR((std::vector<std::pair<int, unsigned>>{{1, 2u}}), std::vector<std::pair<int, unsigned int>>{{1, 2u}});
    check(type_str(std::vector<std::set<long long>>()) == "std::vector<std::set<long long>>", "type_str (nested)");
    check(initializer_str(std::vector<int>{1, 2}) == "{1, 2}", "initializer_str (vector)");

    // repr / type_str / initializer_str をメンバ関数に持つ型 (Range)
    CHECK_REPR(Range(0, 10, 2), Range(0, 10, 2));
    CHECK_REPR((std::vector<Range>{Range(0, 3, 1)}), std::vector<Range>{{0, 3, 1}});
    check(type_str(Range(5)) == "Range" && initializer_str(Range(1, 5, 2)) == "{1, 5, 2}", "Range");

    // メンバ関数を持たない型は "T" と "{}"
    check(type_str(Plain{}) == "T" && initializer_str(Plain{}) == "{}" && repr(Plain{}) == "T{}", "Plain");
    // initializer_str だけを持つ型
    check(repr(OnlyInit{7}) == "T{7}" && initializer_str(std::vector<OnlyInit>{{1}, {2}}) == "{{1}, {2}}", "OnlyInit");

    // ランダム: 整数の vector
    for (int rep = 0; rep < 1000 && ok; rep++) {
        int n = rng() % 5;
        std::vector<long long> v(n);
        std::string expect = "std::vector<long long>{";
        for (int i = 0; i < n; i++) {
            v[i] = (long long)(rng() % 2001) - 1000;
            expect += (i ? ", " : "") + std::to_string(v[i]) + "ll";
        }
        expect += "}";
        check(repr(v) == expect, "repr(vector<long long>) (random)");
    }

    // DEBUG(x) は std::cerr に "x = repr(x)" を出力する
    {
        std::ostringstream buf;
        std::streambuf* old = std::cerr.rdbuf(buf.rdbuf());
        std::vector<int> xs{1, 2};
        DEBUG(xs);
        std::cerr.rdbuf(old);
        check(buf.str() == "xs = std::vector<int>{1, 2}\n", "DEBUG");
    }

    if (ok) std::cout << "Hello World" << std::endl;
}
Back to top page