Skip to content

Instantly share code, notes, and snippets.

@shouth
Last active July 29, 2023 03:03
Show Gist options
  • Save shouth/a201b294c9767269d1a3c1b5e148441d to your computer and use it in GitHub Desktop.
Save shouth/a201b294c9767269d1a3c1b5e148441d to your computer and use it in GitHub Desktop.
#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