Skip to content

Instantly share code, notes, and snippets.

@lo48576
Last active June 27, 2017 10:14
Show Gist options
  • Select an option

  • Save lo48576/dbdb5ac8296d05b3e7bc to your computer and use it in GitHub Desktop.

Select an option

Save lo48576/dbdb5ac8296d05b3e7bc to your computer and use it in GitHub Desktop.
loligger printer
/*
* 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 :
/*
* 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 :
/*
* 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 *);
/*
* Print
*/
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