Icecream-CPP-это небольшая (одиночная заголовок) библиотека, чтобы помочь с отладкой печати в C ++ 11 и вперед.
Попробуйте это в компиляторе Explorer!
Содержимое
С IceCream, проверка выполнения:
auto my_function ( int i, double d) -> void
{
std::cout << " 1 " << std::endl;
if (condition)
std::cout << " 2 " << std::endl;
else
std::cout << " 3 " << std::endl;
}Вместо этого может быть закодирован:
auto my_function ( int i, double d) -> void
{
IC ();
if (condition)
IC ();
else
IC ();
}и напечатает что -то вроде:
ic| test.cpp:34 in "void my_function(int, double)"
ic| test.cpp:36 in "void my_function(int, double)"
Кроме того, любая переменная проверка, как:
std::cout << " a: " << a
<< " , b: " << b
<< " , sum(a, b): " << sum(a, b)
<< std::endl;может быть упрощено:
IC (a, b, sum(a, b));и печатать:
ic| a: 7, b: 2, sum(a, b): 9
Мы также можем осмотреть данные, проходящие через конвейер с видами диапазона (как диапазоны STL, так и диапазон V3), вставив функцию IC_V() в интересующей точке:
auto rv = std::vector< int >{ 1 , 0 , 2 , 3 , 0 , 4 , 5 }
| vws::split( 0 )
| IC_V()
| vws::enumerate; Так что, когда мы перечислим на rv , мы увидим печать:
ic| range_view_63:16[0]: [1]
ic| range_view_63:16[1]: [2, 3]
ic| range_view_63:16[2]: [4, 5]
Эта библиотека вдохновлена оригинальной библиотекой Python Icecream.
ICECREAM-CPP-это единый файл, библиотека только заголовка, имеющая STL в качестве единственной зависимости. Самый непосредственный способ его использования - это просто скопировать заголовок icecream.hpp в свой проект.
Чтобы правильно установить его систему, вместе с файлами проекта Cmake, запустите эти команды в каталоге Project-CPP Project-CPP:
mkdir build
cd build
cmake ..
cmake --install .При использовании NIX, ICECREAM-CPP может быть включен в качестве ввода хлопьев как
inputs . icecream-cpp . url = "github:renatoGarcia/icecream-cpp" ; Заклейка ICECREAM-CPP определяет наложение, так что его можно использовать при импорте nixpkgs :
import nixpkgs {
system = "x86_64-linux" ;
overlays = [
icecream-cpp . overlays . default
] ;
} Выполнение этого, вывод icecream-cpp будет добавлен в набор атрибутов nixpkgs .
Рабочий пример того, как использовать ICECREAM-CPP в проекте Flake, здесь.
Выпущенные версии также доступны на Конане:
conan install icecream-cpp/0.3.1@При использовании Cmake:
find_package (IcecreamCpp)
include_directories ( ${IcecreamCpp_INCLUDE_DIRS} )добавит установленного каталога в списке «Включить пути».
После включения заголовка icecream.hpp в исходный файл:
# include < icecream.hpp > Все функции библиотеки ICECREAM-CPP будут доступны функциями IC , IC_A и IC_V ; вместе с соответствующими аналогами IC_F , IC_FA и IC_FV ; Это ведут то же самое, но принимает строку форматирования вывода, как ее первый аргумент.
IC является самым простым из функций Icecream. При вызове без аргументов он печатает префикс, имя исходного файла, текущий номер строки и текущую подпись функции. Код:
auto my_function ( int foo, double bar) -> void
{
// ...
IC ();
// ...
}будет печатать:
ic| test.cpp:34 in "void my_function(int, double)"
При вызове с аргументами он печатает префикс, эти аргументы имена и его значения. Код:
auto v0 = std::vector< int >{ 1 , 2 , 3 };
auto s0 = std::string{ " bla " };
IC (v0, s0, 3.14 );будет печатать:
ic| v0: [1, 2, 3], s0: "bla", 3.14: 3.14
Вариант IC_F ведет себя так же, как и функция IC , но принимает строку форматирования вывода в качестве первого аргумента.
Чтобы распечатать данные, проходящие через конвейер просмотров диапазона (как диапазоны STL, так и диапазон V3), мы используем функцию IC_V , которая будет печатать любой вход, который он получает из предыдущего представления. Поскольку функция IC_V находится в пределах конвейера в диапазоне, печать будет выполнена лениво, в то время как каждый элемент генерируется. Например:
namespace vws = std::views;
auto v0 = vws::iota( ' a ' ) | vws::enumerate | IC_V() | vws::take( 3 );
for ( auto e : v0)
{
// ...
} В этом коде ничего не будет напечатано при создании v0 , как только итерация над ним. На каждой итерации в линии for петли будет напечатана, пока у нас не будет вывода:
ic| range_view_61:53[0]: (0, 'a')
ic| range_view_61:53[1]: (1, 'b')
ic| range_view_61:53[2]: (2, 'c')
Примечание
ICECREAM-CPP попытается обнаружить, будет ли установлена библиотека Range-V3, и если это так, поддержка будет автоматически включена. Однако при использовании C ++ 11 и C ++ 14 есть шанс получить диапазон V3 в системе, но Icecream не найдет его. Чтобы убедиться, что поддержка Range-V3 включена, просто определите Macro ICECREAM_RANGE_V3 прежде чем включать заголовок icecream.hpp
Функция IC_V имеет два необязательных параметра, IC_V(name, projection) .
Имя переменной, используемое для представления при печати. Перепечатка: <name>[<idx>]: <value> . Если параметр имени не используется, значение по умолчанию <name> is range_view_<source_location> .
Код:
vws::iota ( ' a ' ) | vws::enumerate | IC_V( " foo " ) | vws::take( 2 );Когда итерация будет печатать:
ic| foo[0]: (0, 'a')
ic| foo[1]: (1, 'b')
Вызов, который будет получать в качестве ввода элементов из предыдущего представления и должен вернуть фактический объект, который будет напечатан.
Код:
vws::iota ( ' a ' ) | vws::enumerate | IC_V([]( auto e){ return std::get< 1 >(e);}) | vws::take( 2 );Когда итерация будет печатать:
ic| range_view_61:53[0]: 'a'
ic| range_view_61:53[1]: 'b'
Примечание
Функция IC_V по -прежнему будет пересылать следующее представление о неизменном входном элементе, точно так же, как он был получен из предыдущего представления. Ни одно действие, выполненное функцией projection , не окажет на это какое -либо влияние.
Вариант IC_FV имеет то же поведение, что и функция IC_V , но принимает строку форматирования выходного сигнала в качестве своего первого аргумента.
За исключением случаев, когда вы вызываете ровно один аргумент, функция IC вернет кортеж со всеми его входными аргументами. При вызове с одним аргументом он вернет сам аргумент.
Это делается таким образом, чтобы вы могли использовать IC для проверки аргумента функции в вызове, без дальнейшего изменения кода. В коде:
my_function (IC(MyClass{})); Объект MyClass будет перенаправлен в my_function точно так же, как если бы функции IC не была там. my_function продолжит получать ссылку на RVALUE на объект MyClass .
Этот подход, однако, не настолько практичен, когда функция имеет много аргументов. На коде:
my_function (IC(a), IC(b), IC(c), IC(d)); Помимо написания функции IC , печатный вывод будет разделен на четыре различных линия. Что -то вроде:
ic| a: 1
ic| b: 2
ic| c: 3
ic| d: 4
К сожалению, просто не сработает все четыре аргументы в один вызов IC . Возвращенным значением будет std:::tuple с (a, b, c, d) , и my_function ожидает четыре аргумента.
Чтобы обойти это, есть функция IC_A . IC_A ведет себя точно так же, как функция IC , но получает в качестве первого аргумента возможность, и он будет называть ее, используя все следующие аргументы, печатая все их до этого. Этот предыдущий пример кода может быть переписан как:
IC_A (my_function, a, b, c, d);И на этот раз он будет печатать:
ic| a: 1, b: 2, c: 3, d: 4
Функция IC_A вернет то же значение, что и возвращение Callable. Код:
auto mc = std::make_unique<MyClass>();
auto r = IC_A(mc->my_function, a, b);ведет себя точно так же, как:
auto mc = std::make_unique<MyClass>();
auto r = mc-> my_function (a, b); но напечатает значения a и b
Вариант IC_FA ведет себя так же, как и функция IC_A , но принимает строку форматирования выходного сигнала в качестве первого аргумента, даже до вызова аргумента.
Можно настроить, как значение должно быть отформатировано во время печати. Следующий код:
auto a = int { 42 };
auto b = int { 20 };
IC_F ( " #X " , a, b);будет печатать:
ic| a: 0X2A, b: 0X14
При использовании варианта IC_F вместо простого IC Functio. Аналогичный результат будет получен при использовании IC_FA и IC_FV вместо IC_A и IC_V соответственно.
При использовании вариантов функции форматирования ( IC_F и IC_FA ) такая же строка форматирования будет применена по умолчанию ко всем аргументам. Это может быть проблемой, если мы хотим иметь аргументы с различным форматированием или если у аргументов есть несколько типов с не взаимно достоверными синтаксисами. Поэтому, чтобы установить отдельную строку форматирования в конкретный аргумент, мы можем обернуть ее функцией IC_ . Код:
auto a = int { 42 };
auto b = int { 20 };
IC_F ( " #X " , a, IC_( " d " , b));будет печатать:
ic| a: 0X2A, b: 20
Функция IC_ может использоваться в функции простого IC (или IC_A ) тоже:
auto a = int { 42 };
auto b = int { 20 };
IC (IC_( " #x " , a), b);будет печатать:
ic| a: 0x2a, b: 20
Последний аргумент в вызове функции IC_ - это тот, который будет напечатан, все остальные аргументы, которые предшествуют последним, будут преобразованы в строку с использованием функции to_string и объединены в качестве результирующей строки форматирования.
auto a = float { 1.234 };
auto width = int { 7 };
IC (IC_( " *< " ,width, " .3 " , a)); Будет иметь в результате форматирование строки "*<7.3" , и будет печатать:
ic| a: 1.23***
Просто для полноты в примерах, использование IC_FA и IC_FV было бы:
IC_FA ( " #x " , my_function, 10 , 20 );
auto rv0 = vws::iota( 0 ) | IC_FV( " [::2]:#x " , " bar " ) | vws::take( 5 );Это будет печатать:
ic| 10: 0xa, 20: 0x14
и когда итерация на rv0 :
ic| bar[0]: 0
ic| bar[2]: 0x2
ic| bar[4]: 0x4
От IC_F и IC_FA , синтаксическая спецификация строк форматирования зависит как от печатного типа T , так и в стратегии печати этого типа, используемой ICECREAM.
К IC_FV , синтаксис форматирования, если такой же, как строка формата диапазона.
Кодирование персонажа в C ++ грязно.
Строки char8_t , char16_t и char32_t хорошо определены. Они способны и держат единицы кода Unicode 8, 16 и 32 бит соответственно, и они кодируются в UTF-8, UTF-16 и UTF-32 также соответственно.
Строки char имеют четко определенный размер бита кода (заданный CHAR_BIT , обычно 8 бит), но нет никаких требований к его кодированию.
Строки wchar_t не имеют ни четко определенного размера кода, ни требования о его кодировании.
В таком коде:
auto const str = std::string{ " foo " };
std::cout << str; У нас будет три точки кодирования персонажа. В первом, перед компиляцией этот код будет находиться в исходном файле в неопределенном «кодировании источника». Во втором точке интереса, составленная бинарная строка будет сохранена в строке «Foo» в неопределенном «кодировании выполнения». Наконец, в третьем пункте байтовый поток «Foo», полученный std::cout в конечном итоге будет направлен в систему, который ожидает, что поток будет кодируется в также неопределенном «кодировании вывода».
Из этих трех интересующих точек кодирования символов как «кодирование выполнения», так и «выходное кодирование» оказывают влияние на внутреннюю работу ICECREAM-CPP, и нет никакого способа знать наверняка, каково используемое кодирование в обоих из них. В соответствии с этой неопределенностью, принятая стратегия предлагает разумную функцию транскодирования по умолчанию, которая попытается преобразовать данные в правильное кодирование и позволит пользователю использовать свою собственную реализацию при необходимости.
За исключением широких типов строк Unicode (обсуждается ниже), при печати любого другого типа у нас будет сериализованные текстовые данные в «Кодировках выполнения». Что «кодирование выполнения» может или не может быть таким же, как и «кодирование вывода», это кодирование, ожидаемое настроенным выходом. Из -за этого, прежде чем мы отправим эти данные на вывод, мы должны транслировать их, чтобы убедиться, что мы имеем их в «выходе». Для этого, перед доставкой текстовых данных на вывод, мы отправляем их в функцию настроенной output_transcoder, которые должны убедиться, что они кодируются в правильном «выводном кодировании».
При печати широких типов строк Unicode мы должны иметь еще один уровень транскодирования, потому что возможно, что текстовые данные находятся в отдельном кодировании символов из ожидаемого «кодирования выполнения». Из -за этого применяется дополнительная логика, чтобы убедиться, что строки находятся в «кодировании выполнения», прежде чем мы отправим их на вывод. Это далее обсуждается в широких строках и разделах строк Unicode.
Система конфигурации ICECREAM-CPP работает «Слоисленная по объему». На основе у нас есть глобальный объект IC_CONFIG . Этот глобальный экземпляр разделяется всей программой работы, как и следовало ожидать от глобальной переменной. Он создается со всеми параметрами конфигурации по значениям по умолчанию, и любое изменение легко видно по всей программе.
В любой точке кода мы можем создать новый конфигурационный слой на текущей области, создав создание новой переменной IC_CONFIG , вызывая макрос IC_CONFIG_SCOPE() . Все параметры конфигурации этого нового экземпляра будут находиться в состоянии «незаметного» по умолчанию, а любой запрос на значение опции еще не установлено, будет делегирован его родителям. Этот запрос будет подняться на родительскую цепочку до тех пор, пока первый, кто имеет эту опцию, не установит ответы.
Все параметры конфигурации устанавливаются с использованием методов доклада объекта IC_CONFIG , и их можно приколоть:
IC_CONFIG
.prefix( " ic: " )
.show_c_string( false )
.line_wrap_width( 70 ); IC_CONFIG - это просто обычная переменная с забавным именем, чтобы сделать столкновение чрезвычайно маловероятным. При вызове любого IC*(...) макроса он выберет экземпляр IC_CONFIG по объему, выполнив неквалифицированный поиск имени, используя те же правила, применяемые к любой другой обычной переменной.
Суммируйте все вышеперечисленное, в коде:
auto my_function () -> void
{
IC_CONFIG. line_wrap_width ( 20 );
IC_CONFIG_SCOPE ();
IC_CONFIG. context_delimiter ( " | " );
IC_CONFIG. show_c_string ( true );
{
IC_CONFIG_SCOPE ();
IC_CONFIG. show_c_string ( false );
// A
}
// B
} В строке A значение IC_CONFIG line_wrap_width , context_delimiter и show_c_string будут соответственно: 20 , "|" и false .
После закрытия внутреннего блока оцелев, в строке B значение IC_CONFIG 'S line_wrap_width , context_delimiter и show_c_string будет соответственно: 20 , "|" и true .
Операции чтения и письма на объектах IC_CONFIG безопасны.
Примечание
Любая модификация в IC_CONFIG , кроме глобального экземпляра, будет рассматриваться только в текущей области. Как следствие, эти модификации не будут распространяться на объем любой, называемой функции.
Включите или отключите вывод макроса IC(...) , включенный по умолчанию.
auto is_enabled () const -> bool; auto enable () -> Config&;
auto disable () -> Config&;Код:
IC ( 1 );
IC_CONFIG.disable();
IC ( 2 );
IC_CONFIG.enable();
IC ( 3 );будет печатать:
ic| 1: 1
ic| 3: 3
Набор, где будут напечатаны сериализованные текстовые данные. По умолчанию данные будут напечатаны на стандартном выходе ошибки, так же, как std::cerr .
auto output () const -> std::function<void(std::string const &)>; template < typename T>
auto output (T&& t) -> Config&; Где тип T может быть любым:
std::ostream .push_back(char) .*it = 'c'Например, код:
auto str = std::string{};
IC_CONFIG.output(str);
IC ( 1 , 2 ); Напечатает вывод "ic| 1: 1, 2: 2n" на строке str .
Предупреждение
ICECREAM-CPP не будет принять участие в аргументе t , поэтому пользователь должен проявлять осторожность, чтобы он был жив.
Функция, которая генерирует текст, который будет напечатан перед каждым выводом.
auto prefix () const -> std::function<std::string()>; template < typename ... Ts>
auto prefix (Ts&& ...values) -> Config&; Где типы Ts могут быть любыми:
T() -> U , где U имеет перегрузку operator<<(ostream&, U) .Печатный префикс будет объединением всех этих элементов.
Код:
IC_CONFIG.prefix( " icecream| " );
IC ( 1 );
IC_CONFIG.prefix([]{ return 42 ;}, " - " );
IC ( 2 );
IC_CONFIG.prefix( " thread " , std::this_thread::get_id, " | " );
IC ( 3 );будет печатать:
icecream| 1: 1
42- 2: 2
thread 1 | 3: 3
Управляет, если переменная char* следует интерпретировать как строка C ( true ) с нулевым, или указатель на char ( false ). Значение по умолчанию true .
auto show_c_string () const -> bool; auto show_c_string ( bool value) -> Config&;Код:
char const * flavor = " mango " ;
IC_CONFIG.show_c_string( true );
IC (flavor);
IC_CONFIG.show_c_string( false );
IC (flavor);будет печатать:
ic| flavor: "mango";
ic| flavor: 0x55587b6f5410
Функция, которая транкодирует строку wchar_t , из системы, определяемой кодировкой в строку char в системе «Кодирование выполнения».
auto wide_string_transcoder () const -> std::function<std::string( wchar_t const *, std:: size_t )>; auto wide_string_transcoder (std::function<std::string( wchar_t const *, std:: size_t )> transcoder) -> Config&;
auto wide_string_transcoder (std::function<std::string(std::wstring_view)> transcoder) -> Config&;Нет никакой гарантии, что входная строка будет заканчиваться на нулевом терминаторе (это фактическая семантика string_view), поэтому пользователь должен наблюдать за значением размера входной строки.
Реализация по умолчанию проверит, установлена ли локаль C другому значению, чем «C» или «Posix». Если да, это будет перенаправить вход в функцию std :: wcrtomb. В противном случае он будет предполагать, что вход кодируется Unicode (UTF-16 или UTF-32, соответственно, к размеру байта wchar_t ), и транкодировал его в UTF-8.
Функция, которая транкодирует строку char32_t , из UTF-32, кодирующейся в строку char в системе «Кодирование выполнения».
auto unicode_transcoder () const -> std::function<std::string( char32_t const *, std:: size_t )>; auto unicode_transcoder (std::function<std::string( char32_t const *, std:: size_t )> transcoder) -> Config&;
auto unicode_transcoder (std::function<std::string(std::u32string_view)> transcoder) -> Config&;Нет никакой гарантии, что входная строка будет заканчиваться на нулевом терминаторе (это фактическая семантика string_view), поэтому пользователь должен наблюдать за значением размера входной строки.
Реализация по умолчанию будет проверить, что локаль C установлена на другое значение, чем «C» или «posix». Если да, это будет перенаправить вход в функцию std :: c32rtomb. В противном случае это просто передаст его UTF-8.
Эта функция будет использоваться для транскода все строки char8_t , char16_t и char32_t . При транскодировании строк char8_t и char16_t они сначала будут преобразованы в строку char32_t , прежде чем отправлять в качестве входного в эту функцию.
Функция, которая транкодирует строку char , от системы «Кодирование выполнения» до char строки в системе «кодирование вывода», как и ожидалось, настройка вывода.
auto output_transcoder () const -> std::function<std::string( char const *, std:: size_t )>; auto output_transcoder (std::function<std::string( char const *, std:: size_t )> transcoder) -> Config&;
auto output_transcoder (std::function<std::string(std::string_view)> transcoder) -> Config&;Нет никакой гарантии, что входная строка будет заканчиваться на нулевом терминаторе (это фактическая семантика string_view), поэтому пользователь должен наблюдать за значением размера входной строки.
Реализация по умолчанию предполагает, что «кодирование выполнения» совпадает с «кодированием вывода», и просто вернет неизменную вход.
Максимальное количество символов до того, как вывод будет разбит на нескольких линиях. Значение по умолчанию 70 .
auto line_wrap_width () const -> std::size_t; auto line_wrap_width (std:: size_t value) -> Config&; Если контекст (имя источника, номер строки и имя функции) должен быть напечатан даже при печати переменных. Значение по умолчанию false .
auto include_context () const -> bool; auto include_context ( bool value) -> Config&; Строка, отделяющая текст контекста от значений переменных. Значение по умолчанию- "- " .
auto context_delimiter () const -> std::string; auto context_delimiter (std::string const & value) -> Config&; Чтобы быть печатным, тип T должен удовлетворить одну из стратегий, описанных в следующих разделах. Если это произойдет, что несколько стратегий удовлетворены, будет выбрана те, которые с более высоким приоритетом будет выбран.
Стратегия с самым высоким приоритетом заключается в использовании ввода/вывода на основе потока STL. Следовательно, при печати объекта типа T , если существует перегруженный operator<<(ostream&, T) , он будет использоваться.
C Строки неоднозначны. Должна ли переменная char* foo интерпретировать как указатель на единый char или как строка с нулевым концевым? Аналогичным образом, является ли переменная char bar[] массивом отдельных символов или строки с нулевым концевым? Является ли char baz[3] массив с тремя одиночными символами или это цепочка второго размера плюс A '