Код уже несколько раз оказывался удобным, и я чувствую, что затраты на настройку очень низки, поскольку использование довольно низкое. Таким образом, я решил выпустить его под лицензией MIT и предоставить репозиторий GitHub, куда можно загрузить заголовок и небольшой файл примера.
«украшение» в терминах этого ответа представляет собой набор строки префикса, строки-разделителя и строки-постфикса. Где строка префикса вставляется в поток до, а строка постфикса после значений контейнера (см. 2. Целевые контейнеры). Строка разделителя вставляется между значениями соответствующего контейнера.
Примечание. На самом деле этот ответ не рассматривает вопрос до 100%, поскольку декорация не является строго скомпилированной постоянной времени, поскольку для проверки того, было ли применено нестандартное декорирование к текущему потоку, требуются проверки во время выполнения. Тем не менее, я думаю, что он имеет некоторые приличные особенности.
Примечание 2: могут иметь незначительные ошибки, так как он еще не был хорошо протестирован.
[Тысяча сто шестьдесят один] 1. Общая идея / использование
Это должно быть так же просто, как
#include
#include "pretty.h"
int main()
{
std::cout << std::vector{1,2,3,4,5}; // prints 1, 2, 3, 4, 5
return 0;
}
... относительно конкретного объекта потока
#include
#include "pretty.h"
int main()
{
// set decoration for std::vector for cout object
std::cout << pretty::decoration>("(", ",", ")");
std::cout << std::vector{1,2,3,4,5}; // prints (1,2,3,4,5)
return 0;
}
или относительно всех потоков:
#include
#include "pretty.h"
// set decoration for std::vector for all ostream objects
PRETTY_DEFAULT_DECORATION(std::vector, "{", ", ", "}")
int main()
{
std::cout << std::vector{1,2,3,4,5}; // prints {1, 2, 3, 4, 5}
std::cout << pretty::decoration>("(", ",", ")");
std::cout << std::vector{1,2,3,4,5}; // prints (1,2,3,4,5)
return 0;
}
ios_base
с использованием xalloc
/ pword
для сохранения указателя на объект pretty::decor
, конкретно украшающий определенный тип в определенном потоке. Если объект pretty::decor
для этого потока не был установлен явно, вызывается pretty::defaulted
для получения декорации по умолчанию для данного типа. Класс pretty::defaulted
должен быть специализирован для настройки декораций по умолчанию.
Целевые объекты obj
для «красивого украшения» этого кода - это объекты с перегрузками
std::begin
и std::end
. ] определено (включает массивы в стиле C), begin(obj)
и end(obj)
, доступные через ADL, std::tuple
std::pair
. Код включает в себя признак для идентификации классов с особенностями диапазона (begin
/ end
). (Хотя проверка не включена, является ли begin(obj) == end(obj)
допустимым выражением.)
Код предоставляет operator<<
в глобальном пространстве имен, которые применяются только к классам, не имеющим более специализированной версии operator<<
имеется в наличии. Поэтому, например, std::string
не печатается с использованием оператора в этом коде, хотя имеет действительную пару begin
/ end
.
Декорации могут быть наложены отдельно для каждого типа (кроме разных tuple
) и потока (не тип потока!). (Т.е. std::vector
может иметь разные декорации для разных потоковых объектов.)
Префикс по умолчанию - ""
(ничего), как и постфикс по умолчанию, в то время как разделитель по умолчанию - ", "
(запятая + пробел).
pretty::defaulted
У struct defaulted
есть статическая функция-член decoration()
, возвращающая объект decor
, который включает значения по умолчанию для данный тип.
Настройка печати массива по умолчанию:
namespace pretty
{
template
struct defaulted
{
static decor decoration()
{
return{ { "(" }, { ":" }, { ")" } };
}
};
}
Печать массива массивов:
float e[5] = { 3.4f, 4.3f, 5.2f, 1.1f, 22.2f };
std::cout << e << '\n'; // prints (3.4:4.3:5.2:1.1:22.2)
PRETTY_DEFAULT_DECORATION(TYPE, PREFIX, DELIM, POSTFIX, ...)
для char
streams Макрос расширяется до
namespace pretty {
template< __VA_ARGS__ >
struct defaulted< TYPE > {
static decor< TYPE > decoration() {
return { PREFIX, DELIM, POSTFIX };
}
};
}
, что позволяет переписать вышеуказанную частичную специализацию в
PRETTY_DEFAULT_DECORATION(T[N], "", ";", "", class T, std::size_t N)
или вставить полную специализацию, например
PRETTY_DEFAULT_DECORATION(std::vector, "(", ", ", ")")
Включен еще один макрос для wchar_t
потоков: PRETTY_DEFAULT_WDECORATION
.
[1 171] С) Наложение декораций на потоки
Функция pretty::decoration
используется для наложения декораций на определенный поток. Существуют перегрузки, принимающие либо один строковый аргумент, являющийся разделителем (принимающий префикс и постфикс из класса по умолчанию), либо три строковых аргумента, собирающих полное оформление
float e[3] = { 3.4f, 4.3f, 5.2f };
std::stringstream u;
// add { ; } decoration to u
u << pretty::decoration("{", "; ", "}");
// use { ; } decoration
u << e << '\n'; // prints {3.4; 4.3; 5.2}
// uses decoration returned by defaulted::decoration()
std::cout << e; // prints 3.4, 4.3, 5.2
PRETTY_DEFAULT_DECORATION(float[3], "{{{", ",", "}}}")
std::stringstream v;
v << e; // prints {{{3.4,4.3,5.2}}}
v << pretty::decoration(":");
v << e; // prints {{{3.4:4.3:5.2}}}
v << pretty::decoration("((", "=", "))");
v << e; // prints ((3.4=4.3=5.2))
std::tuple
Вместо того, чтобы разрешать специализацию для каждого возможного типа кортежа, этот код применяет любое украшение, доступное для std::tuple
, ко всем видам std::tuple<...>
с.
[1 165] 5. Удалить пользовательское оформление из потока
Чтобы вернуться к стандартному оформлению для данного типа, используйте шаблон функции pretty::clear
в потоке s
.
s << pretty::clear>();
Печать "в виде матрицы" с разделителем новой строки
std::vector> m{ {1,2,3}, {4,5,6}, {7,8,9} };
std::cout << pretty::decoration>>("\n");
std::cout << m;
Печать
1, 2, 3
4, 5, 6
7, 8, 9
#ifndef pretty_print_0x57547_sa4884X_0_1_h_guard_
#define pretty_print_0x57547_sa4884X_0_1_h_guard_
#include
#include
#include
#include
#include
#define PRETTY_DEFAULT_DECORATION(TYPE, PREFIX, DELIM, POSTFIX, ...) \
namespace pretty { template< __VA_ARGS__ >\
struct defaulted< TYPE > {\
static decor< TYPE > decoration(){\
return { PREFIX, DELIM, POSTFIX };\
} /*decoration*/ }; /*defaulted*/} /*pretty*/
#define PRETTY_DEFAULT_WDECORATION(TYPE, PREFIX, DELIM, POSTFIX, ...) \
namespace pretty { template< __VA_ARGS__ >\
struct defaulted< TYPE, wchar_t, std::char_traits > {\
static decor< TYPE, wchar_t, std::char_traits > decoration(){\
return { PREFIX, DELIM, POSTFIX };\
} /*decoration*/ }; /*defaulted*/} /*pretty*/
namespace pretty
{
namespace detail
{
// drag in begin and end overloads
using std::begin;
using std::end;
// helper template
template using _ol = std::integral_constant*;
// SFINAE check whether T is a range with begin/end
template
class is_range
{
// helper function declarations using expression sfinae
template = nullptr>
static std::false_type b(...);
template = nullptr>
static auto b(U &v) -> decltype(begin(v), std::true_type());
template = nullptr>
static std::false_type e(...);
template = nullptr>
static auto e(U &v) -> decltype(end(v), std::true_type());
// return types
using b_return = decltype(b(std::declval()));
using e_return = decltype(e(std::declval()));
public:
static const bool value = b_return::value && e_return::value;
};
}
// holder class for data
template>
struct decor
{
static const int xindex;
std::basic_string prefix, delimiter, postfix;
decor(std::basic_string const & pre = "",
std::basic_string const & delim = "",
std::basic_string const & post = "")
: prefix(pre), delimiter(delim), postfix(post) {}
};
template
int const decor::xindex = std::ios_base::xalloc();
namespace detail
{
template
void manage_decor(std::ios_base::event evt, std::ios_base &s, int const idx)
{
using deco_type = decor;
if (evt == std::ios_base::erase_event)
{ // erase deco
void const * const p = s.pword(idx);
if (p)
{
delete static_cast(p);
s.pword(idx) = nullptr;
}
}
else if (evt == std::ios_base::copyfmt_event)
{ // copy deco
void const * const p = s.pword(idx);
if (p)
{
auto np = new deco_type{ *static_cast(p) };
s.pword(idx) = static_cast(np);
}
}
}
template struct clearer {};
template
std::basic_ostream& operator<< (
std::basic_ostream &s, clearer const &)
{
using deco_type = decor;
void const * const p = s.pword(deco_type::xindex);
if (p)
{ // delete if set
delete static_cast(p);
s.pword(deco_type::xindex) = nullptr;
}
return s;
}
template
struct default_data { static const CharT * decor[3]; };
template <>
const char * default_data::decor[3] = { "", ", ", "" };
template <>
const wchar_t * default_data::decor[3] = { L"", L", ", L"" };
}
// Clear decoration for T
template
detail::clearer clear() { return{}; }
template
void clear(std::basic_ostream &s) { s << detail::clearer{}; }
// impose decoration on ostream
template
std::basic_ostream& operator<<(
std::basic_ostream &s, decor && h)
{
using deco_type = decor;
void const * const p = s.pword(deco_type::xindex);
// delete if already set
if (p) delete static_cast(p);
s.pword(deco_type::xindex) = static_cast(new deco_type{ std::move(h) });
// check whether we alread have a callback registered
if (s.iword(deco_type::xindex) == 0)
{ // if this is not the case register callback and set iword
s.register_callback(detail::manage_decor, deco_type::xindex);
s.iword(deco_type::xindex) = 1;
}
return s;
}
template>
struct defaulted
{
static inline decor decoration()
{
return{ detail::default_data::decor[0],
detail::default_data::decor[1],
detail::default_data::decor[2] };
}
};
template>
decor decoration(
std::basic_string const & prefix,
std::basic_string const & delimiter,
std::basic_string const & postfix)
{
return{ prefix, delimiter, postfix };
}
template>
decor decoration(
std::basic_string const & delimiter)
{
using str_type = std::basic_string;
return{ defaulted::decoration().prefix,
delimiter, defaulted::decoration().postfix };
}
template>
decor decoration(CharT const * const prefix,
CharT const * const delimiter, CharT const * const postfix)
{
using str_type = std::basic_string;
return{ str_type{ prefix }, str_type{ delimiter }, str_type{ postfix } };
}
template>
decor decoration(CharT const * const delimiter)
{
using str_type = std::basic_string;
return{ defaulted::decoration().prefix,
str_type{ delimiter }, defaulted::decoration().postfix };
}
template
struct tuple
{
template
static void print(std::basic_ostream& s, T const & value,
std::basic_string const &delimiter)
{
s << std::get(value) << delimiter;
tuple::print(s, value, delimiter);
}
};
template
struct tuple
{
template
static void print(std::basic_ostream& s, T const & value,
std::basic_string const &) {
s << std::get(value);
}
};
}
template
std::basic_ostream & operator<< (
std::basic_ostream &s, std::tuple<> const & v)
{
using deco_type = pretty::decor, CharT, TraitT>;
using defaulted_type = pretty::defaulted, CharT, TraitT>;
void const * const p = s.pword(deco_type::xindex);
auto const d = static_cast(p);
s << (d ? d->prefix : defaulted_type::decoration().prefix);
s << (d ? d->postfix : defaulted_type::decoration().postfix);
return s;
}
template
std::basic_ostream & operator<< (
std::basic_ostream &s, std::tuple const & v)
{
using deco_type = pretty::decor, CharT, TraitT>;
using defaulted_type = pretty::defaulted, CharT, TraitT>;
using pretty_tuple = pretty::tuple, 0U, sizeof...(T)-1U>;
void const * const p = s.pword(deco_type::xindex);
auto const d = static_cast(p);
s << (d ? d->prefix : defaulted_type::decoration().prefix);
pretty_tuple::print(s, v, d ? d->delimiter :
defaulted_type::decoration().delimiter);
s << (d ? d->postfix : defaulted_type::decoration().postfix);
return s;
}
template
std::basic_ostream & operator<< (
std::basic_ostream &s, std::pair const & v)
{
using deco_type = pretty::decor, CharT, TraitT>;
using defaulted_type = pretty::defaulted, CharT, TraitT>;
void const * const p = s.pword(deco_type::xindex);
auto const d = static_cast(p);
s << (d ? d->prefix : defaulted_type::decoration().prefix);
s << v.first;
s << (d ? d->delimiter : defaulted_type::decoration().delimiter);
s << v.second;
s << (d ? d->postfix : defaulted_type::decoration().postfix);
return s;
}
template>
typename std::enable_if < pretty::detail::is_range::value,
std::basic_ostream < CharT, TraitT >> ::type & operator<< (
std::basic_ostream &s, T const & v)
{
bool first(true);
using deco_type = pretty::decor;
using default_type = pretty::defaulted;
void const * const p = s.pword(deco_type::xindex);
auto d = static_cast const * const>(p);
s << (d ? d->prefix : default_type::decoration().prefix);
for (auto const & e : v)
{ // v is range thus range based for works
if (!first) s << (d ? d->delimiter : default_type::decoration().delimiter);
s << e;
first = false;
}
s << (d ? d->postfix : default_type::decoration().postfix);
return s;
}
#endif // pretty_print_0x57547_sa4884X_0_1_h_guard_
Попробуйте эту команду на терминале,
sudo dpkg -i --force-overwrite /var/cache/apt/archives/linux-firmware_1.79.9_all.deb