Last active
June 27, 2017 10:14
-
-
Save lo48576/dbdb5ac8296d05b3e7bc to your computer and use it in GitHub Desktop.
loligger printer
This file contains hidden or 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
| /* | |
| * Copyright 2014-2017 YOSHIOKA Takuma | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| * of this software and associated documentation files (the "Software"), to deal | |
| * in the Software without restriction, including without limitation the rights | |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| * copies of the Software, and to permit persons to whom the Software is | |
| * furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in | |
| * all copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| * SOFTWARE. | |
| */ | |
| /*! | |
| * \file lolipri.cpp | |
| * \brief | |
| * \author YOSHIOKA Takuma <[email protected]> | |
| * \date 2014/12/31 | |
| */ | |
| // Requires C++14 or above. | |
| #ifdef HAVE_CONFIG_H | |
| # include "config.h" | |
| #endif | |
| #include "lolipri.hpp" | |
| namespace loligger { | |
| namespace detail { | |
| template class TypenameTableBase<false>; | |
| template class TypenameTableBase<true>; | |
| /* | |
| * TypenameTable, TypenameTableBase | |
| */ | |
| template <> | |
| auto TypenameTable<false>::instance(void) noexcept | |
| -> TypenameTable & | |
| { | |
| /* | |
| * 結果をキャッシュしない。インスタンス生成のオーバーヘッドが小さくなる。 | |
| */ | |
| static TypenameTable i_; | |
| return i_; | |
| } | |
| template <> | |
| auto TypenameTable<true>::instance(void) | |
| -> TypenameTable & | |
| { | |
| /* | |
| * thread_local にすることで、排他制御を不要にする。 | |
| * 大量に作られ短期間で終了するようなスレッド内では | |
| * オーバーヘッドが大きくなるのでキャッシュ無し版を使うのが良い。 | |
| */ | |
| static thread_local TypenameTable i_; | |
| return i_; | |
| } | |
| std::string TypenameTableBase<false>::nameImpl(std::type_index ti) const | |
| { | |
| int status; | |
| std::unique_ptr<char, void(*)(void *)> buf(abi::__cxa_demangle(ti.name(), nullptr, nullptr, &status), std::free); | |
| // status==-1 : memory allocation failure | |
| // status==-2 : invalid mangled name | |
| // status==-3 : invalid arguments | |
| // status==0 : success | |
| switch(status) { | |
| case -1: | |
| throw std::runtime_error("TypenameTableBase::name(): memory allocation failure"); | |
| case -2: | |
| throw std::runtime_error("TypenameTableBase::name(): invalid mangled name: " + std::string(ti.name())); | |
| case -3: | |
| throw std::invalid_argument("TypenameTableBase::name(): invalid argument"); | |
| } // switch(status) | |
| return std::string(buf.get()); | |
| } | |
| std::string TypenameTableBase<true>::nameImpl(std::type_index ti) | |
| { | |
| auto it = m_map.find(ti); | |
| if(it == std::end(m_map)) { | |
| // No match. | |
| int status; | |
| std::unique_ptr<char, void(*)(void *)> buf(abi::__cxa_demangle(ti.name(), nullptr, nullptr, &status), std::free); | |
| // status==-1 : memory allocation failure | |
| // status==-2 : invalid mangled name | |
| // status==-3 : invalid arguments | |
| // status==0 : success | |
| switch(status) { | |
| case -1: | |
| throw std::runtime_error("TypenameTable::name(): memory allocation failure"); | |
| case -2: | |
| throw std::runtime_error("TypenameTable::name(): invalid mangled name: " + std::string(ti.name())); | |
| case -3: | |
| throw std::invalid_argument("TypenameTable::name(): invalid argument"); | |
| } // switch(status) | |
| it = m_map.emplace(ti, buf.get()).first; | |
| } | |
| return std::string(it->second); | |
| } | |
| // printEscapedString()内で使う専用。 | |
| void printEscapedStringStream(std::ostream &ost, std::stringstream &str) | |
| { | |
| const auto fmt = ost.flags(); | |
| ost << std::noshowbase << std::hex; | |
| // Use istreambuf_iterator, NOT istream_iterator !!! | |
| for(std::istreambuf_iterator<char> it(str), ite; it!=ite; ++it) { | |
| const unsigned char c = static_cast<unsigned char>(*it); | |
| /* | |
| * strはUTF-8等であるという前提で処理をする。 | |
| * (正確には、非ASCII文字を表現するバイト列中に0x80未満の | |
| * オクテットが現れないことが条件。) | |
| */ | |
| if( | |
| // [0x80..] : non-ASCII | |
| (c >= 0x80) | |
| // 0x20: blank | |
| // [0x21..0x7e] : printable | |
| || ((c >= 0x20) && (c <= 0x7e)) | |
| ) { | |
| ost << static_cast<char>(c); | |
| } else { | |
| // space and NUL | |
| switch(c) { | |
| case '\t': | |
| ost << "\\t"; | |
| break; | |
| case '\f': | |
| ost << "\\f"; | |
| break; | |
| case '\v': | |
| ost << "\\v"; | |
| break; | |
| case '\n': | |
| ost << "\\n"; | |
| break; | |
| case '\r': | |
| ost << "\\r"; | |
| break; | |
| case '\0': | |
| ost << "\\0"; | |
| break; | |
| default: | |
| ost << "\\x" << static_cast<int>(c); | |
| } // switch(c) | |
| } | |
| ost.unsetf(std::ios::hex); | |
| ost.setf(fmt); | |
| } | |
| } | |
| void printEscapedString(std::ostream &ost, const char *str, bool) | |
| { | |
| std::stringstream temp_stream; | |
| temp_stream << std::quoted(str); | |
| printEscapedStringStream(ost, temp_stream); | |
| } | |
| /* | |
| * EndianChecker | |
| */ | |
| const std::array<uint8_t, 4> &EndianChecker::byte32(void) noexcept | |
| { | |
| static const union { | |
| uint32_t all; | |
| std::array<uint8_t, 4> byte; | |
| } n = {0x0102'0304}; | |
| return n.byte; | |
| } | |
| // TODO: How to check existence of uint64_t (or uint128_t)? | |
| // I think it is impossible without using external tools | |
| // such as autoconf... | |
| #if 0 // If uint64_t exists | |
| static const std::array<uint8_t, 8> &EndianChecker::byte64(void) noexcept | |
| { | |
| static const union { | |
| uint64_t all; | |
| std::array<uint8_t, 8> byte; | |
| } n = {0x0102'0304'0506'0708}; | |
| return n.byte; | |
| } | |
| #endif | |
| #if 0 // If uint128_t exists | |
| static const std::array<uint8_t, 16> &EndianChecker::byte128(void) noexcept | |
| { | |
| static const union { | |
| uint128_t all; | |
| std::array<uint8_t, 16> byte; | |
| } n = {0x01020304'05060708'090a0b0c'0d0e0f10}; | |
| return n.byte; | |
| } | |
| #endif | |
| /* | |
| * demangler | |
| */ | |
| std::string demangle(const char *name) | |
| { | |
| int status; | |
| // void(*)(void *) : decltype(std::free)* | |
| std::unique_ptr<char, void(*)(void *)> buf(abi::__cxa_demangle(name, nullptr, nullptr, &status), std::free); | |
| return (status ? std::string(name) : std::string(buf.get())); | |
| } | |
| /* | |
| * dumpRawBytes | |
| */ | |
| void dumpRawBytes(std::ostream &ost, const void *ptr, size_t size, bool isLittleEndian) | |
| { | |
| const auto fmt = ost.flags(); | |
| const auto defaultWidth = ost.width(); | |
| const auto defaultFill = ost.fill(); | |
| ost << std::hex << std::setw(2) << std::setfill('0'); | |
| // Check endianness. | |
| if(isLittleEndian) { | |
| // Little endian. | |
| for(const uint8_t *it=static_cast<const uint8_t *>(ptr)+size-1, *ite=static_cast<const uint8_t *>(ptr)-1; | |
| it!=ite; --it) { | |
| ost << static_cast<int>(*it); | |
| } | |
| } else { | |
| // Big endian or unknown endian. | |
| for(const uint8_t *it=static_cast<const uint8_t *>(ptr), *ite=static_cast<const uint8_t *>(ptr)+size; | |
| it!=ite; ++it) { | |
| ost << static_cast<int>(*it); | |
| } | |
| } | |
| ost.fill(defaultFill); | |
| ost.width(defaultWidth); | |
| ost.unsetf(std::ios::hex); | |
| ost.setf(fmt); | |
| } | |
| } // namespace detail | |
| } // namespace loligger | |
| // vim: set noexpandtab tabstop=4 : |
This file contains hidden or 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
| /* | |
| * Copyright 2014-2017 YOSHIOKA Takuma | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| * of this software and associated documentation files (the "Software"), to deal | |
| * in the Software without restriction, including without limitation the rights | |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| * copies of the Software, and to permit persons to whom the Software is | |
| * furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in | |
| * all copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| * SOFTWARE. | |
| */ | |
| /*! | |
| * \file lolipri.hpp | |
| * \brief | |
| * \author YOSHIOKA Takuma <[email protected]> | |
| * \date 2014/12/31 | |
| */ | |
| // Requires C++14 or above. | |
| #pragma once | |
| #ifndef INCLUDED__LOLIGGER__LOLIPRI_HPP_ | |
| #define INCLUDED__LOLIGGER__LOLIPRI_HPP_ | |
| #ifndef INCLUDED__LOLIGGER__LOLIPRI_IMPL_HPP_ | |
| # include "lolipri_impl.hpp" | |
| #endif | |
| namespace loligger { | |
| namespace detail { | |
| extern template class TypenameTableBase<false>; | |
| extern template class TypenameTableBase<true>; | |
| } // namespace detail | |
| } // namespace loligger | |
| #endif // ifndef INCLUDED__LOLIGGER__LOLIPRI_HPP_ | |
| // vim: set noexpandtab tabstop=4 : |
This file contains hidden or 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
| /* | |
| * Copyright 2014-2017 YOSHIOKA Takuma | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| * of this software and associated documentation files (the "Software"), to deal | |
| * in the Software without restriction, including without limitation the rights | |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| * copies of the Software, and to permit persons to whom the Software is | |
| * furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in | |
| * all copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| * SOFTWARE. | |
| */ | |
| /*! | |
| * \file lolipri_impl.hpp | |
| * \brief | |
| * \author YOSHIOKA Takuma <[email protected]> | |
| * \date 2014/12/31 | |
| */ | |
| // Requires C++14 or above. | |
| #pragma once | |
| #ifndef INCLUDED__LOLIGGER__LOLIPRI_IMPL_HPP_ | |
| #define INCLUDED__LOLIGGER__LOLIPRI_IMPL_HPP_ | |
| #include <ostream> | |
| #include <string> | |
| // cxxabi.h: abi::__cxa_demangle() | |
| #include <cxxabi.h> | |
| // typeindex: type_index | |
| #include <typeindex> | |
| // stdexcept: runtime_error(), invalid_argument() | |
| #include <stdexcept> | |
| #include <unordered_map> | |
| // iomanip: quoted(), setiosflags() | |
| #include <iomanip> | |
| #include <array> | |
| /* | |
| * They declare (or define) types which is used to specialize types and | |
| * functions. | |
| * The types may be already defined indirectly in previously included headers, | |
| * but include some headers explicitly just to be sure. | |
| */ | |
| // memory may have been already included by <string>. | |
| #include <memory> | |
| // iterator may have been already included by <unordered_map>. | |
| // NOTE: clang-3.5.0: iterator<-string, gcc-4.9.2: iterator(partial)<-string | |
| // iterator: forward_iterator_tag, istreambuf_iterator | |
| #include <iterator> | |
| // tuple may have been already included by <memory>. | |
| // NOTE: clang-3.5.0: tuple<-memory, gcc-4.9.2: tuple<-functional<-memory | |
| #include <tuple> | |
| namespace loligger { | |
| template <typename T> | |
| struct PrintValue; | |
| /* | |
| * Manipulator | |
| */ | |
| struct Manipulator {}; | |
| template <typename Manip, typename =std::enable_if_t<std::is_base_of<Manipulator, Manip>::value>> | |
| std::ostream &operator<<(std::ostream &ost, const Manip &m) { | |
| return m(ost); | |
| } | |
| namespace detail { | |
| /* | |
| * Typename table | |
| */ | |
| template <bool cache_> | |
| class TypenameTableBase; | |
| template <> | |
| class TypenameTableBase<false> { | |
| protected: | |
| std::string nameImpl(std::type_index) const; | |
| protected: | |
| TypenameTableBase() = default; | |
| TypenameTableBase(const TypenameTableBase &) = default; | |
| TypenameTableBase(TypenameTableBase &&) = default; | |
| TypenameTableBase &operator=(const TypenameTableBase &) = default; | |
| TypenameTableBase &operator=(TypenameTableBase &&) = default; | |
| ~TypenameTableBase() = default; | |
| }; // class TypenameTableBase<false> | |
| template <> | |
| class TypenameTableBase<true> { | |
| private: | |
| std::unordered_map<std::type_index, std::string> m_map; | |
| protected: | |
| std::string nameImpl(std::type_index); | |
| protected: | |
| TypenameTableBase() = default; | |
| TypenameTableBase(const TypenameTableBase &) = delete; | |
| TypenameTableBase &operator=(const TypenameTableBase &) = delete; | |
| ~TypenameTableBase() = default; | |
| }; // class TypenameTableBase<true> | |
| template <bool cache_> | |
| class TypenameTable : protected TypenameTableBase<cache_> { | |
| public: | |
| static TypenameTable &instance(void) noexcept(!cache_); | |
| using Base = TypenameTableBase<cache_>; | |
| std::string name(std::type_index ti) | |
| { | |
| return Base::nameImpl(ti); | |
| } | |
| std::string name(const std::type_info &ti) | |
| { | |
| return Base::nameImpl(std::type_index(ti)); | |
| } | |
| template <typename T> | |
| std::string name(void) | |
| { | |
| return Base::nameImpl(std::type_index(typeid(T))); | |
| } | |
| //! Returns compile-time type name, not runtime type name. | |
| // If runtime type name is necessary, pass `typeid`. | |
| template <typename T> | |
| std::string name(const T &) | |
| { | |
| return Base::nameImpl(std::type_index(typeid(T))); | |
| } | |
| }; // class TypenameTable | |
| /* | |
| * demangler | |
| */ | |
| std::string demangle(const char *); | |
| /* | |
| */ | |
| template <typename T> | |
| using remove_cvr_t = std::remove_cv_t<std::remove_reference_t<T>>; | |
| template <typename T, bool cache_=true> | |
| struct PrintTypeAndValue : public Manipulator { | |
| using value_type = T; | |
| static constexpr bool typeCache = cache_; | |
| const value_type &obj; | |
| explicit PrintTypeAndValue(const value_type &o) | |
| :obj(o) | |
| {} | |
| PrintTypeAndValue(const PrintTypeAndValue &) = default; | |
| PrintTypeAndValue(PrintTypeAndValue &&) = default; | |
| ~PrintTypeAndValue() = default; | |
| std::ostream &operator()(std::ostream &ost) const | |
| { | |
| using cvr_removed_t = remove_cvr_t<T>; | |
| // TODO: It will be good if the string is compounded at compile time | |
| // (using sprout library or something). | |
| //auto &table = TypenameTable<typeCache>::instance(); | |
| auto &table = TypenameTable<true>::instance(); | |
| ost << table.name<cvr_removed_t>(); | |
| ost << '('; | |
| PrintValue<cvr_removed_t>::print(ost, obj, false); | |
| ost << ')'; | |
| return ost; | |
| } | |
| }; // struct PrintTypeAndValue | |
| } // namespace detail | |
| /* | |
| * Helper | |
| */ | |
| namespace detail { | |
| template <typename T, typename =void> | |
| struct OstreamPrintable : public std::false_type {}; | |
| template <typename T> | |
| struct OstreamPrintable< | |
| T, | |
| std::enable_if_t<decltype( | |
| std::declval<std::ostream &>()<<std::declval<T>(), | |
| std::true_type() | |
| )::value> | |
| > | |
| : public std::true_type | |
| {}; | |
| //template <typename T> | |
| //static constexpr bool OstreamPrintable_v = OstreamPrintable<T>::value; | |
| template <typename T, typename =void> | |
| struct ForwardIterable : public std::false_type {}; | |
| template <typename T> | |
| struct ForwardIterable< | |
| T, | |
| std::enable_if_t< | |
| decltype( | |
| std::end(std::declval<T>()), | |
| std::true_type() | |
| )::value | |
| && std::is_base_of< | |
| std::forward_iterator_tag, | |
| typename std::iterator_traits<decltype(std::begin(std::declval<T>()))>::iterator_category | |
| >::value | |
| > | |
| > | |
| : public std::true_type | |
| {}; | |
| //template <typename T> | |
| //static constexpr bool ForwardIterable_v = ForwardIterable<T>::value; | |
| /* | |
| * Not restricted to forward iterator to accept any input iterator. | |
| * Callers can pass objects even if they are not input iterator, but they | |
| * shouldn't do that. | |
| * Callers should care about it. | |
| */ | |
| template <typename InputIterator> | |
| inline void printCommaSeparatedValues(InputIterator it, InputIterator ite, std::ostream &ost, bool paren=true) | |
| { | |
| using value_type = detail::remove_cvr_t<decltype(*it)>; | |
| bool outputComma = false; | |
| if(paren) { | |
| ost << '{'; | |
| } | |
| for(; it!=ite; ++it) { | |
| if(std::exchange(outputComma, true)) { | |
| ost << ", "; | |
| } | |
| PrintValue<value_type>::print(ost, *it); | |
| } | |
| if(paren) { | |
| ost << '}'; | |
| } | |
| } | |
| template <size_t I=0, typename... Types> | |
| static inline auto printTupleElements(std::ostream &, const std::tuple<Types...> &) | |
| -> std::enable_if_t<I==sizeof...(Types)> | |
| {} | |
| template <size_t I=0, typename... Types> | |
| static inline auto printTupleElements(std::ostream &ost, const std::tuple<Types...> &t) | |
| -> std::enable_if_t<(I!=0)&&(I<sizeof...(Types))> | |
| { | |
| ost << ", "; | |
| PrintValue<typename detail::remove_cvr_t<decltype(std::get<I>(t))>>::print(ost, std::get<I>(t)); | |
| printTupleElements<I+1, Types...>(ost, t); | |
| } | |
| template <size_t I=0, typename... Types> | |
| static inline auto printTupleElements(std::ostream &ost, const std::tuple<Types...> &t) | |
| -> std::enable_if_t<I==0> | |
| { | |
| PrintValue<typename detail::remove_cvr_t<decltype(std::get<I>(t))>>::print(ost, std::get<I>(t)); | |
| printTupleElements<1, Types...>(ost, t); | |
| } | |
| // printEscapedString()内で使う専用。 | |
| void printEscapedStringStream(std::ostream &, std::stringstream &); | |
| template <typename... Args> | |
| void printEscapedString(std::ostream &ost, const std::basic_string<char, Args...> &str, bool =true) | |
| { | |
| std::stringstream temp_stream; | |
| temp_stream << std::quoted(str); | |
| printEscapedStringStream(ost, temp_stream); | |
| } | |
| void printEscapedString(std::ostream &ost, const char *str, bool =true); | |
| void dumpRawBytes(std::ostream &ost, const void *ptr, size_t size, bool isLittleEndian=false); | |
| struct EndianChecker { | |
| static bool isLittleEndian(void) noexcept | |
| { | |
| // check if byte32() == {4, 3, 2, 1} | |
| return (byte32()[0] == 0x04); | |
| } | |
| static bool isBigEndian(void) noexcept | |
| { | |
| // check if byte32() == {1, 2, 3, 4} | |
| return (byte32()[0] == 0x01); | |
| } | |
| static const std::array<uint8_t, 4> &byte32(void) noexcept; | |
| // TODO: How to check existence of uint64_t (or uint128_t)? | |
| // I think it is impossible without using external tools | |
| // such as autoconf... | |
| #if 0 // If uint64_t exists | |
| static const std::array<uint8_t, 8> &byte64(void) noexcept; | |
| #endif | |
| #if 0 // If uint64_t exists | |
| static const std::array<uint8_t, 16> &byte128(void) noexcept; | |
| #endif | |
| }; // struct EndianChecker | |
| } // namespcae detail | |
| template <typename T> | |
| struct DumpRaw: public Manipulator { | |
| using value_type = T; | |
| const value_type &obj; | |
| explicit DumpRaw(const value_type &o) | |
| :obj(o) | |
| {} | |
| DumpRaw(const DumpRaw &) = default; | |
| DumpRaw(DumpRaw &&) = default; | |
| ~DumpRaw() = default; | |
| std::ostream &operator()(std::ostream &ost) const | |
| { | |
| // Care about endian only when the type is fundamental. | |
| detail::dumpRawBytes(ost, &obj, sizeof(T), std::is_fundamental<T>::value && detail::EndianChecker::isLittleEndian()); | |
| return ost; | |
| } | |
| }; // struct DumpRaw | |
| namespace detail { | |
| template <typename U> | |
| using is_char = std::integral_constant<bool, | |
| std::is_same<U, char>::value | |
| || std::is_same<U, unsigned char>::value | |
| || std::is_same<U, signed char>::value | |
| >; | |
| } // namespace detail | |
| /* | |
| * PrintValue | |
| */ | |
| // Defaults | |
| template <typename T> | |
| struct PrintValue { | |
| enum class Enabler {_}; | |
| // Integer except character. | |
| template < | |
| typename U, | |
| std::enable_if_t< | |
| std::is_integral<U>::value | |
| && !detail::is_char<U>::value | |
| , Enabler | |
| > = Enabler::_ | |
| > | |
| static void print(std::ostream &ost, U num, bool =true) | |
| { | |
| const auto fmt = ost.flags(); | |
| ost << std::showbase; | |
| if(fmt & std::ios::hex) { | |
| ost << num; | |
| } else { | |
| ost << num << '(' << std::hex << num << std::setiosflags(fmt) << ')'; | |
| } | |
| ost.unsetf(std::ios::showbase | std::ios::hex); | |
| ost.setf(fmt); | |
| } | |
| // 1-byte character types. | |
| template < | |
| typename U, | |
| std::enable_if_t< | |
| detail::is_char<U>::value | |
| , Enabler | |
| > = Enabler::_ | |
| > | |
| static void print(std::ostream &ost, U c, bool =true) | |
| { | |
| ost << '\'' << c << '\''; | |
| } | |
| // Floating-point. | |
| template < | |
| typename U, | |
| std::enable_if_t< | |
| detail::OstreamPrintable<U>::value | |
| && std::is_floating_point<U>::value | |
| , Enabler | |
| > = Enabler::_ | |
| > | |
| static void print(std::ostream &ost, U num, bool =true) | |
| { | |
| ost << num << '(' << DumpRaw<U>(num) << ')'; | |
| } | |
| // Not arithmetic, not pointer, and operator<<(std::ostream &, U) is available. | |
| template < | |
| typename U, | |
| std::enable_if_t< | |
| detail::OstreamPrintable<U>::value | |
| && !std::is_arithmetic<U>::value | |
| && !std::is_pointer<U>::value | |
| , Enabler | |
| > = Enabler::_ | |
| > | |
| static void print(std::ostream &ost, const U &obj, bool =true) | |
| { | |
| ost << '{' << obj << '}'; | |
| } | |
| // Container. | |
| template < | |
| typename U, | |
| std::enable_if_t< | |
| !detail::OstreamPrintable<U>::value | |
| && detail::ForwardIterable<U>::value | |
| , Enabler | |
| > = Enabler::_ | |
| > | |
| static void print(std::ostream &ost, const U &obj, bool paren=true) | |
| { | |
| detail::printCommaSeparatedValues(obj.begin(), obj.end(), ost, paren); | |
| } | |
| // std::pair<> . | |
| /* | |
| * Define default implementation here, because `std::pair` has been already | |
| * available through `unordered_map` header. | |
| */ | |
| template <typename T1, typename T2> | |
| static void print(std::ostream &ost, const std::pair<T1, T2> &obj, bool paren=true) | |
| { | |
| if(paren) { | |
| ost << '{'; | |
| } | |
| PrintValue<typename detail::remove_cvr_t<T1>>::print(ost, obj.first); | |
| ost << ", "; | |
| PrintValue<typename detail::remove_cvr_t<T2>>::print(ost, obj.second); | |
| if(paren) { | |
| ost << '}'; | |
| } | |
| } | |
| template <typename... Args> | |
| static void print(std::ostream &ost, const std::tuple<Args...> &obj, bool paren=true) | |
| { | |
| if(paren) { | |
| ost << '{'; | |
| } | |
| detail::printTupleElements(ost, obj); | |
| if(paren) { | |
| ost << '}'; | |
| } | |
| } | |
| // String (std::string). | |
| template <typename... Args> | |
| static void print(std::ostream &ost, const std::basic_string<char, Args...> &str, bool =true) | |
| { | |
| detail::printEscapedString(ost, str); | |
| } | |
| // String (const char *). | |
| static void print(std::ostream &ost, const char *str, bool =true) | |
| { | |
| detail::printEscapedString(ost, str); | |
| } | |
| // Raw pointer. | |
| template <typename U> | |
| static void print(std::ostream &ost, U *ptr, bool =true) | |
| { | |
| ost << static_cast<void *>(ptr); | |
| } | |
| // std::unique_ptr<> . | |
| template <typename... Args> | |
| static void print(std::ostream &ost, const std::unique_ptr<Args...> &ptr, bool =true) | |
| { | |
| ost << static_cast<void *>(ptr.get()); | |
| } | |
| // std::unique_ptr<> . | |
| template <typename... Args> | |
| static void print(std::ostream &ost, const std::shared_ptr<Args...> &ptr, bool =true) | |
| { | |
| ost << static_cast<void *>(ptr.get()) << "[use:" << ptr.use_count() << ']'; | |
| } | |
| // std::weak_ptr<> . | |
| template <typename... Args> | |
| static void print(std::ostream &ost, const std::weak_ptr<Args...> &ptr, bool =true) | |
| { | |
| ost << "weak_ptr[use:" << ptr.use_count() << ']'; | |
| } | |
| // nullptr. | |
| static void print(std::ostream &ost, std::nullptr_t, bool =true) | |
| { | |
| ost << "nullptr"; | |
| } | |
| // Unprintable. | |
| template < | |
| typename U, | |
| std::enable_if_t< | |
| !detail::OstreamPrintable<U>::value | |
| && !detail::ForwardIterable<U>::value | |
| , Enabler | |
| > = Enabler::_ | |
| > | |
| static void print(std::ostream &ost, const U &, bool =true) | |
| { | |
| ost << "(unknown)"; | |
| } | |
| }; // struct PrintValue | |
| /* | |
| * Print, PrintNoCache | |
| */ | |
| template <typename T, bool cache_=true> | |
| detail::PrintTypeAndValue<T, cache_> Print(const T &obj) | |
| { | |
| return detail::PrintTypeAndValue<T, cache_>(obj); | |
| } | |
| template <typename T> | |
| detail::PrintTypeAndValue<T, false> PrintNoCache(const T &obj) | |
| { | |
| return detail::PrintTypeAndValue<T, false>(obj); | |
| } | |
| /* | |
| * Specialize sample | |
| */ | |
| /* | |
| * `PrintValue` implementation above is default and customizable. | |
| * To change its behavior, specialize `PrintValue` class for types. | |
| * Commented out code below is a sample which customize serialization of | |
| * `std::basic_string`. | |
| */ | |
| #if 0 | |
| /* | |
| template <typename... Args> | |
| struct PrintValue<std::basic_string<Args...>> { | |
| static void print(std::ostream &ost, const std::basic_string<Args...> &str, bool =true) | |
| { | |
| ost << "[string: " << std::quoted(str) << ']'; | |
| } | |
| }; // struct PrintValue | |
| */ | |
| #endif | |
| } // namespace loligger | |
| #endif // ifndef INCLUDED__LOLIGGER__LOLIPRI_IMPL_HPP_ | |
| // vim: set noexpandtab tabstop=4 : |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment