ICECREAM-CPP เป็นไลบรารี (ส่วนหัวเดี่ยว) เล็กน้อยเพื่อช่วยในการดีบักการพิมพ์ใน C ++ 11 และไปข้างหน้า
ลองใช้ที่ Compiler 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 และ Range-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 ให้เรียกใช้คำสั่งเหล่านี้ในไดเรกทอรีรูทของโครงการ ICECREAM-CPP:
mkdir build
cd build
cmake ..
cmake --install .หากใช้ Nix, Icecream-CPP สามารถรวมเป็นอินพุต Flakes เป็น
inputs . icecream-cpp . url = "github:renatoGarcia/icecream-cpp" ; Flake 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 เป็นฟังก์ชั่นไอศครีมที่ง่ายที่สุด หากเรียกโดยไม่มีอาร์กิวเมนต์มันจะพิมพ์คำนำหน้าชื่อไฟล์ต้นฉบับหมายเลขบรรทัดปัจจุบันและลายเซ็นฟังก์ชันปัจจุบัน รหัส:
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 และ Range-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 ซ้ำแต่ละครั้งในวง One Line จะถูกพิมพ์จนกว่าเราจะมีเอาต์พุต:
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 อย่างไรก็ตามมีโอกาสที่จะมี Range-V3 ในระบบ แต่ Icecream ไม่พบมัน เพื่อให้แน่ใจว่าเปิดใช้งานการรองรับ Range-V3 เพียงแค่กำหนด Macro ICECREAM_RANGE_V3 ก่อนที่จะรวมส่วนหัว icecream.hpp
ฟังก์ชั่น IC_V มีพารามิเตอร์เสริมสองตัวคือ IC_V(name, projection)
ชื่อตัวแปรที่ใช้ในการดูเมื่อพิมพ์ เค้าโครงการพิมพ์คือ: <name>[<idx>]: <value> หากไม่มีการใช้พารามิเตอร์ชื่อค่าเริ่มต้นเป็น <name> คือ 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')
callable ที่จะได้รับเป็นอินพุตองค์ประกอบจากมุมมองก่อนหน้าและต้องส่งคืนวัตถุจริงที่จะพิมพ์
รหัส:
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 จะส่งคืน tuple ด้วยอาร์กิวเมนต์อินพุตทั้งหมด หากเรียกด้วยอาร์กิวเมนต์หนึ่งมันจะส่งคืนอาร์กิวเมนต์เอง
สิ่งนี้ทำด้วยวิธีนี้เพื่อให้คุณสามารถใช้ 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 แต่ได้รับ callable เป็นอาร์กิวเมนต์แรกและจะเรียกมันว่าโดยใช้อาร์กิวเมนต์ถัดไปทั้งหมดพิมพ์ทั้งหมดก่อนหน้านั้น รหัสตัวอย่างก่อนหน้านี้สามารถเขียนใหม่เป็น:
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 ใหม่โดยเรียก Macro IC_CONFIG_SCOPE() ตัวเลือกการกำหนดค่าทั้งหมดของอินสแตนซ์ใหม่นี้จะอยู่ในสถานะ "UNSET" โดยค่าเริ่มต้นและคำขอใด ๆ ไปยังค่าตัวเลือกที่ยังไม่ได้ตั้งค่าจะถูกมอบหมายให้กับพาเรนต์ คำขอนั้นจะขึ้นไปบนห่วงโซ่หลักจนกว่าจะมีคำตอบชุดตัวเลือกแรก
ตัวเลือกการกำหนดค่าทั้งหมดถูกตั้งค่าโดยใช้วิธี accessor ของวัตถุ 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 ค่าของ line_wrap_width ของ IC_CONFIG , context_delimiter และ show_c_string จะเป็นไปตามลำดับ: 20 , "|" และ false
หลังจากปิดบล็อกขอบเขตด้านในสุดที่บรรทัด B ค่าของ IC_CONFIG ของ 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&; ที่ Type T สามารถเป็นของ:
std::ostreampush_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 จะต้องเป็นไปตามกลยุทธ์ที่อธิบายไว้ในส่วนถัดไป หากมันเกิดขึ้นที่มีหลายกลยุทธ์ที่พึงพอใจจะเลือกกลยุทธ์ที่มีความสำคัญสูงกว่า
กลยุทธ์ที่มีความสำคัญสูงสุดคือการใช้ I/O-based STL-based ดังนั้นเมื่อพิมพ์วัตถุประเภท T หากมี operator<<(ostream&, T) จะใช้
C สตริงมีความคลุมเครือ ตัวแปร char* foo ควรตีความว่าเป็นตัวชี้ไปยัง char เดียวหรือเป็นสตริงที่สิ้นสุดแบบโมฆะ? ในทำนองเดียวกันตัวแปร char bar[] เป็นอาร์เรย์ของอักขระเดี่ยวหรือสตริงที่สิ้นสุดโมฆะหรือไม่? char baz[3] อาร์เรย์ที่มีสามอักขระเดี่ยวหรือเป็นสตริงที่มีขนาดสองบวกกับ '