Last active
July 29, 2023 03:03
-
-
Save shouth/a201b294c9767269d1a3c1b5e148441d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <optional> | |
#include <tuple> | |
#include <type_traits> | |
#include <utility> | |
namespace shouth | |
{ | |
namespace detail | |
{ | |
template <typename T, typename Enabler = void> | |
struct dumper; | |
} | |
namespace internal | |
{ | |
template <typename T> | |
using detect_dump_t = std::void_t<decltype(detail::dumper<T>())>; | |
template <typename T, typename Enabler = void> | |
inline constexpr bool has_dump_v = false; | |
template <typename T> | |
inline constexpr bool has_dump_v<T, detect_dump_t<T>> = true; | |
} // namespace internal | |
template <typename T> | |
auto dump(std::ostream &os, const T &value) | |
-> std::enable_if_t<internal::has_dump_v<T>> | |
{ | |
detail::dumper<T>::do_dump(os, value); | |
} | |
namespace internal | |
{ | |
template <typename T> | |
struct Dump | |
{ | |
const T &value; | |
}; | |
template <typename T> | |
auto operator<<(std::ostream &os, const Dump<T> &value) -> std::ostream & | |
{ | |
dump(os, value.value); | |
return os; | |
} | |
} // namespace internal | |
template <typename T> | |
auto dump(const T &value) -> internal::Dump<T> | |
{ | |
return { value }; | |
} | |
namespace dumpop | |
{ | |
template <class T> | |
auto operator<<(std::ostream &os, const T &value) | |
-> std::enable_if_t<internal::has_dump_v<T>, std::ostream &> | |
{ | |
dump(os, value); | |
return os; | |
} | |
} // namespace dumpop | |
namespace internal | |
{ | |
template <typename T> | |
using detect_ostream_operator_t = std::void_t<decltype(std::declval<std::ostream &>() << std::declval<T>())>; | |
template <typename T, typename Enabler = void> | |
inline constexpr bool has_ostream_operator_v = false; | |
template <typename T> | |
inline constexpr bool has_ostream_operator_v<T, detect_ostream_operator_t<T>> = true; | |
using std::begin, std::end; | |
template <typename T> | |
using detect_begin_end_t = std::void_t<decltype(begin(std::declval<T &>()), end(std::declval<T &>()))>; | |
template <typename T, typename Enabler = void> | |
inline constexpr bool is_iterable_v = false; | |
template <typename T> | |
inline constexpr bool is_iterable_v<T, detect_begin_end_t<T>> = true; | |
template <typename T> | |
using detect_tuple_size_t = std::void_t<decltype(std::tuple_size<T>())>; | |
template <typename T, typename Enabler = void> | |
inline constexpr bool is_tuple_like_v = false; | |
template <typename T> | |
inline constexpr bool is_tuple_like_v<T, detect_tuple_size_t<T>> = true; | |
} // namespace internal | |
namespace detail | |
{ | |
template <typename T> | |
struct dumper<T, std::enable_if_t<internal::has_ostream_operator_v<T>>> | |
{ | |
static auto do_dump(std::ostream &os, const T &value) -> void | |
{ | |
os << value; | |
} | |
}; | |
template <typename T> | |
struct dumper<std::optional<T>> | |
{ | |
static auto do_dump(std::ostream &os, const std::optional<T> &value) -> void | |
{ | |
if (value) { | |
os << "!<"; | |
dump(os, *value); | |
os << ">"; | |
} else { | |
os << "?<>"; | |
} | |
} | |
}; | |
template <typename T> | |
struct dumper<T, std::enable_if_t<not internal::has_ostream_operator_v<T> and internal::is_iterable_v<T>>> | |
{ | |
static auto do_dump(std::ostream &os, const T &iterable) -> void | |
{ | |
os << "["; | |
auto separator = ""; | |
for (const auto &value : iterable) { | |
os << std::exchange(separator, ", "); | |
dump(os, value); | |
} | |
os << "]"; | |
} | |
}; | |
template <typename T> | |
struct dumper<T, std::enable_if_t< | |
not internal::has_ostream_operator_v<T> and not internal::is_iterable_v<T> and internal::is_tuple_like_v<T>>> | |
{ | |
template <std::size_t Index> | |
static auto do_dump_each(std::ostream &os, const T &tuplelike) -> void | |
{ | |
using std::get; | |
if constexpr (Index != 0) os << ", "; | |
dump(os, get<Index>(tuplelike)); | |
} | |
template <std::size_t ...Indices> | |
static auto do_dump_all(std::ostream &os, const T &tuplelike, std::index_sequence<Indices...>) -> void | |
{ | |
os << "("; | |
(do_dump_each<Indices>(os, tuplelike), ...); | |
os << ")"; | |
} | |
static auto do_dump(std::ostream &os, const T &tuplelike) -> void | |
{ | |
do_dump_all(os, tuplelike, std::make_index_sequence<std::tuple_size_v<T>>()); | |
} | |
}; | |
} // namespace detail | |
} // namespace shouth | |
#include <deque> | |
#include <list> | |
#include <map> | |
#include <utility> | |
#include <vector> | |
auto main() -> int | |
{ | |
auto a = std::vector<int> { 3, 1, 4, 1, 5 }; | |
auto b = std::list<std::deque<int>> { { 3 }, { 1, 4 }, { 1, 5, 9, 2 } }; | |
auto m = std::map<std::string, int> { { "aaa", 111 }, { "bbb", 222 }, { "ccc", 333 } }; | |
auto o = std::map<int, std::optional<int>> { { 1, std::nullopt }, { 2, 4 }, { 3, std::nullopt }, { 4, 9 } }; | |
std::cout << shouth::dump(a) << std::endl; // [3, 1, 4, 1, 5] | |
std::cout << shouth::dump(b) << std::endl; // [[3], [1, 4], [1, 5, 9, 2]] | |
std::cout << shouth::dump(m) << std::endl; // [(aaa, 111), (bbb, 222), (ccc, 333)] | |
std::cout << shouth::dump(o) << std::endl; // [(1, ?<>), (2, !<4>), (3, ?<>), (4, !<9>)] | |
{ | |
using namespace shouth::dumpop; | |
std::cout << a << std::endl; // [3, 1, 4, 1, 5] | |
std::cout << b << std::endl; // [[3], [1, 4], [1, 5, 9, 2]] | |
std::cout << m << std::endl; // [(aaa, 111), (bbb, 222), (ccc, 333)] | |
std::cout << o << std::endl; // [(1, ?<>), (2, !<4>), (3, ?<>), (4, !<9>)] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment