Repository: Apress/beginning-cpp20 Branch: main Commit: 8e99a6cee493 Files: 1522 Total size: 1.7 MB Directory structure: gitextract_vz438qf9/ ├── .gitmodules ├── Contributing.md ├── Examples/ │ ├── Modules/ │ │ ├── Chapter 01/ │ │ │ ├── Ex1_01.cpp │ │ │ └── Ex1_02.cpp │ │ ├── Chapter 02/ │ │ │ ├── Ex2_01.cpp │ │ │ ├── Ex2_02.cpp │ │ │ ├── Ex2_03.cpp │ │ │ ├── Ex2_03A.cpp │ │ │ ├── Ex2_03B.cpp │ │ │ ├── Ex2_03C.cpp │ │ │ ├── Ex2_03D.cpp │ │ │ ├── Ex2_04.cpp │ │ │ ├── Ex2_05.cpp │ │ │ ├── Ex2_06.cpp │ │ │ ├── Ex2_06B.cpp │ │ │ └── Ex2_07.cpp │ │ ├── Chapter 03/ │ │ │ ├── Ex3_01.cpp │ │ │ ├── Ex3_02.cpp │ │ │ └── Ex3_03.cpp │ │ ├── Chapter 04/ │ │ │ ├── Ex4_01.cpp │ │ │ ├── Ex4_01A.cpp │ │ │ ├── Ex4_02.cpp │ │ │ ├── Ex4_02A.cpp │ │ │ ├── Ex4_03.cpp │ │ │ ├── Ex4_04.cpp │ │ │ ├── Ex4_04A.cpp │ │ │ ├── Ex4_05.cpp │ │ │ ├── Ex4_06.cpp │ │ │ ├── Ex4_07.cpp │ │ │ ├── Ex4_08.cpp │ │ │ ├── Ex4_09.cpp │ │ │ └── Ex4_09A.cpp │ │ ├── Chapter 05/ │ │ │ ├── Ex5_01.cpp │ │ │ ├── Ex5_02.cpp │ │ │ ├── Ex5_03.cpp │ │ │ ├── Ex5_03A.cpp │ │ │ ├── Ex5_04.cpp │ │ │ ├── Ex5_04A.cpp │ │ │ ├── Ex5_05.cpp │ │ │ ├── Ex5_06.cpp │ │ │ ├── Ex5_07.cpp │ │ │ ├── Ex5_07A.cpp │ │ │ ├── Ex5_08.cpp │ │ │ ├── Ex5_09.cpp │ │ │ ├── Ex5_10.cpp │ │ │ ├── Ex5_11.cpp │ │ │ ├── Ex5_12.cpp │ │ │ ├── Ex5_12A.cpp │ │ │ ├── Ex5_13.cpp │ │ │ ├── Ex5_14.cpp │ │ │ └── Ex5_15.cpp │ │ ├── Chapter 06/ │ │ │ ├── Ex6_01.cpp │ │ │ ├── Ex6_02.cpp │ │ │ ├── Ex6_03.cpp │ │ │ ├── Ex6_04.cpp │ │ │ ├── Ex6_05.cpp │ │ │ ├── Ex6_06.cpp │ │ │ └── Ex6_07.cpp │ │ ├── Chapter 08/ │ │ │ ├── Ex8_01.cpp │ │ │ ├── Ex8_02.cpp │ │ │ ├── Ex8_03.cpp │ │ │ ├── Ex8_04.cpp │ │ │ ├── Ex8_05.cpp │ │ │ ├── Ex8_05A.cpp │ │ │ ├── Ex8_06.cpp │ │ │ ├── Ex8_06A.cpp │ │ │ ├── Ex8_07.cpp │ │ │ ├── Ex8_08.cpp │ │ │ ├── Ex8_09A.cpp │ │ │ ├── Ex8_09B.cpp │ │ │ ├── Ex8_09C.cpp │ │ │ ├── Ex8_10.cpp │ │ │ ├── Ex8_11.cpp │ │ │ ├── Ex8_12.cpp │ │ │ ├── Ex8_13.cpp │ │ │ ├── Ex8_14.cpp │ │ │ ├── Ex8_15.cpp │ │ │ ├── Ex8_16.cpp │ │ │ └── Ex8_17.cpp │ │ ├── Chapter 09/ │ │ │ ├── Ex9_01.cpp │ │ │ ├── Ex9_02.cpp │ │ │ ├── Ex9_03.cpp │ │ │ └── Ex9_03A.cpp │ │ ├── Chapter 10/ │ │ │ ├── Ex10_01.cpp │ │ │ ├── Ex10_02.cpp │ │ │ ├── Ex10_03.cpp │ │ │ ├── Ex10_03A.cpp │ │ │ └── Ex10_04.cpp │ │ ├── Chapter 11/ │ │ │ ├── Ex11_01/ │ │ │ │ ├── Ex11_01.cpp │ │ │ │ └── math.cppm │ │ │ ├── Ex11_01A/ │ │ │ │ ├── Ex11_01A.cpp │ │ │ │ └── math.cppm │ │ │ ├── Ex11_01B/ │ │ │ │ ├── Ex11_01B.cpp │ │ │ │ └── math.cppm │ │ │ ├── Ex11_02/ │ │ │ │ ├── Ex11_02.cpp │ │ │ │ ├── from_roman.cpp │ │ │ │ ├── roman.cppm │ │ │ │ └── to_roman.cpp │ │ │ ├── Ex11_03/ │ │ │ │ ├── Ex11_03.cpp │ │ │ │ ├── from_roman.cpp │ │ │ │ ├── roman.cppm │ │ │ │ └── to_roman.cpp │ │ │ ├── Ex11_04/ │ │ │ │ ├── Ex11_04.cpp │ │ │ │ ├── from_roman.cpp │ │ │ │ ├── roman-internals.cpp │ │ │ │ ├── roman.cppm │ │ │ │ └── to_roman.cpp │ │ │ ├── Ex11_05/ │ │ │ │ ├── Ex11_05.cpp │ │ │ │ ├── from_roman.cpp │ │ │ │ ├── roman-from.cppm │ │ │ │ ├── roman-internals.cpp │ │ │ │ ├── roman-to.cppm │ │ │ │ └── roman.cppm │ │ │ ├── Ex11_06/ │ │ │ │ └── Ex11_06.cpp │ │ │ ├── Ex11_06A/ │ │ │ │ ├── Ex11_06A.cpp │ │ │ │ └── squaring.cppm │ │ │ ├── Ex11_06B/ │ │ │ │ ├── Ex11_06B.cpp │ │ │ │ └── squaring.cppm │ │ │ ├── Ex11_07/ │ │ │ │ ├── Ex11_07.cpp │ │ │ │ ├── math.cpp │ │ │ │ └── math.cppm │ │ │ └── Ex11_08/ │ │ │ ├── Ex11_08.cpp │ │ │ └── squaring.cppm │ │ ├── Chapter 12/ │ │ │ ├── Ex12_01/ │ │ │ │ └── Ex12_01.cpp │ │ │ ├── Ex12_01A/ │ │ │ │ └── Ex12_01A.cpp │ │ │ ├── Ex12_02/ │ │ │ │ └── Ex12_02.cpp │ │ │ ├── Ex12_03/ │ │ │ │ └── Ex12_03.cpp │ │ │ ├── Ex12_04/ │ │ │ │ └── Ex12_04.cpp │ │ │ ├── Ex12_05/ │ │ │ │ └── Ex12_05.cpp │ │ │ ├── Ex12_05A/ │ │ │ │ └── Ex12_05A.cpp │ │ │ ├── Ex12_06/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_06.cpp │ │ │ ├── Ex12_06A/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_06A.cpp │ │ │ ├── Ex12_06B/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_06B.cpp │ │ │ ├── Ex12_07/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_07.cpp │ │ │ ├── Ex12_08/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_08.cpp │ │ │ ├── Ex12_09/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_09.cpp │ │ │ ├── Ex12_10/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_10.cpp │ │ │ ├── Ex12_11/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_11.cpp │ │ │ ├── Ex12_12/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_12.cpp │ │ │ ├── Ex12_13/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_13.cpp │ │ │ ├── Ex12_14/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_14.cpp │ │ │ ├── Ex12_15/ │ │ │ │ ├── CylindricalBox.cpp │ │ │ │ ├── CylindricalBox.cppm │ │ │ │ └── Ex12_15.cpp │ │ │ ├── Ex12_16/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex12_16.cpp │ │ │ ├── Ex12_17/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Ex12_17.cpp │ │ │ │ ├── Package.cpp │ │ │ │ ├── RandomBoxes.cppm │ │ │ │ ├── SharedBox.cppm │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.cppm │ │ │ └── Ex12_18/ │ │ │ ├── Box.cppm │ │ │ ├── Ex12_18.cpp │ │ │ ├── RandomBoxes.cppm │ │ │ ├── Truckload.cpp │ │ │ └── Truckload.cppm │ │ ├── Chapter 13/ │ │ │ ├── Ex13_01/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex13_01.cpp │ │ │ ├── Ex13_02/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex13_02.cpp │ │ │ ├── Ex13_03/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex13_03.cpp │ │ │ ├── Ex13_03A/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex13_03A.cpp │ │ │ ├── Ex13_04/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex13_04.cpp │ │ │ ├── Ex13_05/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex13_05.cpp │ │ │ ├── Ex13_06/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex13_06.cpp │ │ │ ├── Ex13_07/ │ │ │ │ ├── Ex13_07.cpp │ │ │ │ └── Integer.cppm │ │ │ ├── Ex13_08/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex13_08.cpp │ │ │ ├── Ex13_09/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex13_09.cpp │ │ │ ├── Ex13_10/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Ex13_10.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.cppm │ │ │ ├── Ex13_11/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Ex13_11.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.cppm │ │ │ ├── Ex13_12/ │ │ │ │ ├── Ex13_12.cpp │ │ │ │ ├── Message.cpp │ │ │ │ └── Message.cppm │ │ │ ├── Ex13_12A/ │ │ │ │ ├── Ex13_12A.cpp │ │ │ │ ├── Message.cpp │ │ │ │ └── Message.cppm │ │ │ └── Ex13_12B/ │ │ │ ├── Ex13_12B.cpp │ │ │ ├── Message.cpp │ │ │ └── Message.cppm │ │ ├── Chapter 14/ │ │ │ ├── Ex14_01/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex14_01.cpp │ │ │ ├── Ex14_01A/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex14_01A.cpp │ │ │ ├── Ex14_02/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex14_02.cpp │ │ │ ├── Ex14_03/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex14_03.cpp │ │ │ ├── Ex14_04/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex14_04.cpp │ │ │ ├── Ex14_04A/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex14_04A.cpp │ │ │ ├── Ex14_04B/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex14_04B.cpp │ │ │ ├── Ex14_04C/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex14_04C.cpp │ │ │ ├── Ex14_05/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex14_05.cpp │ │ │ ├── Ex14_06/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex14_06.cpp │ │ │ ├── Ex14_07/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ ├── CerealPack.cppm │ │ │ │ ├── Ex14_07.cpp │ │ │ │ └── FoodContainer.cppm │ │ │ ├── Ex14_07A/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ ├── CerealPack.cppm │ │ │ │ ├── Ex14_07A.cpp │ │ │ │ └── FoodContainer.cppm │ │ │ └── Ex14_07B/ │ │ │ ├── Box.cppm │ │ │ ├── Carton.cppm │ │ │ ├── CerealPack.cppm │ │ │ ├── Ex14_07B.cpp │ │ │ └── FoodContainer.cppm │ │ ├── Chapter 15/ │ │ │ ├── Ex15_01/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Ex15_01.cpp │ │ │ │ └── ToughPack.cppm │ │ │ ├── Ex15_02/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ ├── Ex15_02.cpp │ │ │ │ └── ToughPack.cppm │ │ │ ├── Ex15_03/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ ├── Ex15_03.cpp │ │ │ │ └── ToughPack.cppm │ │ │ ├── Ex15_04/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ ├── Ex15_04.cpp │ │ │ │ └── ToughPack.cppm │ │ │ ├── Ex15_05/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ ├── Ex15_05.cpp │ │ │ │ └── ToughPack.cppm │ │ │ ├── Ex15_06/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ ├── Ex15_06.cpp │ │ │ │ └── ToughPack.cppm │ │ │ ├── Ex15_07/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ ├── Ex15_07.cpp │ │ │ │ └── ToughPack.cppm │ │ │ ├── Ex15_07A/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ ├── Ex15_07A.cpp │ │ │ │ └── ToughPack.cppm │ │ │ ├── Ex15_08/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Ex14_08.cpp │ │ │ │ └── ToughPack.cppm │ │ │ ├── Ex15_09/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ └── Ex15_09.cpp │ │ │ ├── Ex15_10/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── Boxes.cppm │ │ │ │ ├── Carton.cppm │ │ │ │ ├── Ex15_10.cpp │ │ │ │ └── ToughPack.cppm │ │ │ └── Ex15_11/ │ │ │ ├── Box.cppm │ │ │ ├── Boxes.cppm │ │ │ ├── Can.cppm │ │ │ ├── Carton.cppm │ │ │ ├── Ex15_11.cpp │ │ │ ├── ToughPack.cppm │ │ │ └── Vessel.cppm │ │ ├── Chapter 16/ │ │ │ ├── Ex16_01/ │ │ │ │ └── Ex16_01.cpp │ │ │ ├── Ex16_02/ │ │ │ │ ├── Ex16_02.cpp │ │ │ │ └── Troubles.cppm │ │ │ ├── Ex16_03/ │ │ │ │ ├── Ex16_03.cpp │ │ │ │ └── Troubles.cppm │ │ │ ├── Ex16_04/ │ │ │ │ ├── Ex16_04.cpp │ │ │ │ └── Troubles.cppm │ │ │ ├── Ex16_05/ │ │ │ │ ├── Ex16_05.cpp │ │ │ │ └── Troubles.cppm │ │ │ ├── Ex16_06/ │ │ │ │ ├── Ex16_06.cpp │ │ │ │ └── Troubles.cppm │ │ │ ├── Ex16_07/ │ │ │ │ ├── Ex16_07.cpp │ │ │ │ └── Troubles.cppm │ │ │ ├── Ex16_07A/ │ │ │ │ ├── Ex16_07A.cpp │ │ │ │ └── Troubles.cppm │ │ │ ├── Ex16_07B/ │ │ │ │ ├── DoubleArrayRAII.cppm │ │ │ │ ├── Ex16_07B.cpp │ │ │ │ └── Troubles.cppm │ │ │ ├── Ex16_07C/ │ │ │ │ ├── Ex16_07C.cpp │ │ │ │ └── Troubles.cppm │ │ │ ├── Ex16_07D/ │ │ │ │ ├── Ex16_07D.cpp │ │ │ │ └── Troubles.cppm │ │ │ └── Ex16_09/ │ │ │ ├── Box.cppm │ │ │ ├── DimensionError.cppm │ │ │ └── Ex16_09.cpp │ │ ├── Chapter 17/ │ │ │ ├── Ex17_01/ │ │ │ │ ├── Array.cppm │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex17_01.cpp │ │ │ ├── Ex17_01A/ │ │ │ │ ├── Array.cppm │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex17_01A.cpp │ │ │ ├── Ex17_02/ │ │ │ │ ├── Array.cppm │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex17_02.cpp │ │ │ ├── Ex17_02A/ │ │ │ │ ├── Array.cppm │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex17_02A.cpp │ │ │ ├── Ex17_03/ │ │ │ │ ├── Array.cppm │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex17_03.cpp │ │ │ ├── Ex17_04/ │ │ │ │ ├── Ex17_04.cpp │ │ │ │ └── Stack.cppm │ │ │ ├── Ex17_04A/ │ │ │ │ ├── Ex17_04A.cpp │ │ │ │ └── Stack.cppm │ │ │ ├── Ex17_04B/ │ │ │ │ ├── Ex17_04B.cpp │ │ │ │ └── Stack.cppm │ │ │ ├── Ex17_05/ │ │ │ │ ├── Ex17_05A.cpp │ │ │ │ ├── Ex17_05B.cpp │ │ │ │ └── Ex17_05C.cpp │ │ │ └── Ex17_06/ │ │ │ └── Ex17_06.cpp │ │ ├── Chapter 18/ │ │ │ ├── Ex18_01/ │ │ │ │ ├── Array.cppm │ │ │ │ └── Ex18_01.cpp │ │ │ ├── Ex18_02/ │ │ │ │ ├── Array.cppm │ │ │ │ └── Ex18_02.cpp │ │ │ ├── Ex18_03/ │ │ │ │ ├── Array.cppm │ │ │ │ └── Ex18_03.cpp │ │ │ ├── Ex18_04/ │ │ │ │ ├── Array.cppm │ │ │ │ └── Ex18_04.cpp │ │ │ ├── Ex18_05A/ │ │ │ │ ├── Array.cppm │ │ │ │ └── Ex18_05A.cpp │ │ │ ├── Ex18_05B/ │ │ │ │ ├── Array.cppm │ │ │ │ └── Ex18_05B.cpp │ │ │ ├── Ex18_06/ │ │ │ │ ├── Array.cppm │ │ │ │ └── Ex18_06.cpp │ │ │ └── Ex18_07/ │ │ │ ├── Array.cppm │ │ │ └── Ex18_07.cpp │ │ ├── Chapter 19/ │ │ │ ├── Ex19_01/ │ │ │ │ └── Ex19_01.cpp │ │ │ ├── Ex19_02/ │ │ │ │ ├── Ex19_02.cpp │ │ │ │ └── Optimum.cppm │ │ │ ├── Ex19_03/ │ │ │ │ ├── Ex19_03.cpp │ │ │ │ ├── Less.cppm │ │ │ │ └── Optimum.cppm │ │ │ ├── Ex19_03A/ │ │ │ │ ├── Ex19_03A.cpp │ │ │ │ └── Optimum.cppm │ │ │ ├── Ex19_04/ │ │ │ │ ├── Ex19_04.cpp │ │ │ │ ├── Nearer.cppm │ │ │ │ └── Optimum.cppm │ │ │ ├── Ex19_05/ │ │ │ │ ├── Ex19_05.cpp │ │ │ │ └── Optimum.cppm │ │ │ ├── Ex19_05A/ │ │ │ │ ├── Ex19_05A.cpp │ │ │ │ └── Optimum.cppm │ │ │ ├── Ex19_06/ │ │ │ │ ├── Ex19_06.cpp │ │ │ │ └── Optimum.cppm │ │ │ ├── Ex19_07/ │ │ │ │ ├── Ex19_07.cpp │ │ │ │ ├── Finder.cpp │ │ │ │ ├── Finder.cppm │ │ │ │ └── Optimum.cppm │ │ │ └── Ex19_08/ │ │ │ └── Ex19_08.cpp │ │ ├── Chapter 20/ │ │ │ ├── Ex20_01/ │ │ │ │ └── Ex20_01.cpp │ │ │ ├── Ex20_02/ │ │ │ │ └── Ex20_02.cpp │ │ │ ├── Ex20_03/ │ │ │ │ └── Ex20_03.cpp │ │ │ ├── Ex20_04/ │ │ │ │ └── Ex20_04.cpp │ │ │ ├── Ex20_05/ │ │ │ │ └── Ex20_05.cpp │ │ │ ├── Ex20_06/ │ │ │ │ └── Ex20_06.cpp │ │ │ ├── Ex20_07/ │ │ │ │ └── Ex20_07.cpp │ │ │ ├── Ex20_08/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex20_08.cpp │ │ │ ├── Ex20_09/ │ │ │ │ └── Ex20_09.cpp │ │ │ ├── Ex20_10/ │ │ │ │ └── Ex20_10.cpp │ │ │ ├── Ex20_11/ │ │ │ │ └── Ex20_11.cpp │ │ │ ├── Ex20_11A/ │ │ │ │ └── Ex20_11A.cpp │ │ │ ├── Ex20_12/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex20_12.cpp │ │ │ ├── Ex20_12A/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex20_12A.cpp │ │ │ ├── Ex20_13/ │ │ │ │ └── Ex20_13.cpp │ │ │ ├── Ex20_13A/ │ │ │ │ └── Ex20_13A.cpp │ │ │ ├── Ex20_13B/ │ │ │ │ └── Ex20_13B.cpp │ │ │ ├── Ex20_14/ │ │ │ │ └── Ex20_14.cpp │ │ │ ├── Ex20_14A/ │ │ │ │ └── Ex20_14A.cpp │ │ │ ├── Ex20_14B/ │ │ │ │ └── Ex20_14B.cpp │ │ │ ├── Ex20_15/ │ │ │ │ └── Ex20_15.cpp │ │ │ ├── Ex20_15A/ │ │ │ │ └── Ex20_15A.cpp │ │ │ ├── Ex20_16/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Ex20_16.cpp │ │ │ ├── Ex20_17/ │ │ │ │ └── Ex20_17.cpp │ │ │ └── Ex20_18/ │ │ │ └── Ex20_18.cpp │ │ └── Chapter 21/ │ │ ├── Ex21_01/ │ │ │ └── Ex21_01.cpp │ │ ├── Ex21_02/ │ │ │ └── Ex21_02.cpp │ │ ├── Ex21_03/ │ │ │ ├── Array.cppm │ │ │ └── Ex21_03.cpp │ │ └── Ex21_04/ │ │ └── Ex21_04.cpp │ ├── NoModules/ │ │ ├── Appendix A/ │ │ │ ├── ExA_01/ │ │ │ │ └── ExA_01.cpp │ │ │ ├── ExA_02/ │ │ │ │ └── ExA_02.cpp │ │ │ ├── ExA_02A/ │ │ │ │ └── ExA_02A.cpp │ │ │ ├── ExA_03/ │ │ │ │ └── ExA_03.cpp │ │ │ ├── ExA_04/ │ │ │ │ └── ExA_04.cpp │ │ │ ├── ExA_05/ │ │ │ │ ├── ExA_05.cpp │ │ │ │ └── inclusivity.quote │ │ │ ├── ExA_06/ │ │ │ │ └── ExA_06.cpp │ │ │ ├── ExA_07/ │ │ │ │ ├── ExA_07.cpp │ │ │ │ └── Power.cpp │ │ │ ├── ExA_07A/ │ │ │ │ ├── ExA_07A.cpp │ │ │ │ ├── Power.cpp │ │ │ │ └── Power.h │ │ │ ├── ExA_08/ │ │ │ │ ├── ExA_08.cpp │ │ │ │ ├── Power.cpp │ │ │ │ └── Range.cpp │ │ │ ├── ExA_08A/ │ │ │ │ ├── ExA_08A.cpp │ │ │ │ ├── Power.cpp │ │ │ │ └── Range.cpp │ │ │ ├── ExA_09/ │ │ │ │ ├── ExA_09.cpp │ │ │ │ └── Power.cpp │ │ │ ├── ExA_09A/ │ │ │ │ ├── ExA_09A.cpp │ │ │ │ └── Power.cpp │ │ │ ├── ExA_10/ │ │ │ │ ├── BadMath.h │ │ │ │ └── ExA_10.cpp │ │ │ ├── ExA_10A/ │ │ │ │ ├── BetterMath.h │ │ │ │ └── ExA_10A.cpp │ │ │ ├── ExA_11/ │ │ │ │ ├── BetterMath.h │ │ │ │ ├── ExA_11.cpp │ │ │ │ ├── Hypot.cpp │ │ │ │ ├── Hypot.h │ │ │ │ ├── Pow4.cpp │ │ │ │ └── Pow4.h │ │ │ ├── ExA_12/ │ │ │ │ ├── ExA_12.cpp │ │ │ │ ├── Hypot.cpp │ │ │ │ ├── Hypot.h │ │ │ │ ├── Pow4.cpp │ │ │ │ ├── Pow4.h │ │ │ │ └── ProperMath.h │ │ │ ├── ExA_13/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── ExA_13.cpp │ │ │ └── ExA_13A/ │ │ │ ├── Box.h │ │ │ └── ExA_13A.cpp │ │ ├── Chapter 01/ │ │ │ ├── Ex1_01.cpp │ │ │ └── Ex1_02.cpp │ │ ├── Chapter 02/ │ │ │ ├── Ex2_01.cpp │ │ │ ├── Ex2_02.cpp │ │ │ ├── Ex2_03.cpp │ │ │ ├── Ex2_03A.cpp │ │ │ ├── Ex2_03B.cpp │ │ │ ├── Ex2_03C.cpp │ │ │ ├── Ex2_03D.cpp │ │ │ ├── Ex2_04.cpp │ │ │ ├── Ex2_05.cpp │ │ │ ├── Ex2_06.cpp │ │ │ ├── Ex2_06B.cpp │ │ │ └── Ex2_07.cpp │ │ ├── Chapter 03/ │ │ │ ├── Ex3_01.cpp │ │ │ ├── Ex3_02.cpp │ │ │ └── Ex3_03.cpp │ │ ├── Chapter 04/ │ │ │ ├── Ex4_01.cpp │ │ │ ├── Ex4_01A.cpp │ │ │ ├── Ex4_02.cpp │ │ │ ├── Ex4_02A.cpp │ │ │ ├── Ex4_03.cpp │ │ │ ├── Ex4_04.cpp │ │ │ ├── Ex4_04A.cpp │ │ │ ├── Ex4_05.cpp │ │ │ ├── Ex4_06.cpp │ │ │ ├── Ex4_07.cpp │ │ │ ├── Ex4_08.cpp │ │ │ ├── Ex4_09.cpp │ │ │ └── Ex4_09A.cpp │ │ ├── Chapter 05/ │ │ │ ├── Ex5_01.cpp │ │ │ ├── Ex5_02.cpp │ │ │ ├── Ex5_03.cpp │ │ │ ├── Ex5_03A.cpp │ │ │ ├── Ex5_04.cpp │ │ │ ├── Ex5_04A.cpp │ │ │ ├── Ex5_05.cpp │ │ │ ├── Ex5_06.cpp │ │ │ ├── Ex5_07.cpp │ │ │ ├── Ex5_07A.cpp │ │ │ ├── Ex5_08.cpp │ │ │ ├── Ex5_09.cpp │ │ │ ├── Ex5_10.cpp │ │ │ ├── Ex5_11.cpp │ │ │ ├── Ex5_12.cpp │ │ │ ├── Ex5_12A.cpp │ │ │ ├── Ex5_13.cpp │ │ │ ├── Ex5_14.cpp │ │ │ └── Ex5_15.cpp │ │ ├── Chapter 06/ │ │ │ ├── Ex6_01.cpp │ │ │ ├── Ex6_02.cpp │ │ │ ├── Ex6_03.cpp │ │ │ ├── Ex6_04.cpp │ │ │ ├── Ex6_05.cpp │ │ │ ├── Ex6_06.cpp │ │ │ └── Ex6_07.cpp │ │ ├── Chapter 07/ │ │ │ ├── Ex7_01.cpp │ │ │ ├── Ex7_01A.cpp │ │ │ ├── Ex7_02.cpp │ │ │ ├── Ex7_02A.cpp │ │ │ ├── Ex7_03.cpp │ │ │ ├── Ex7_04.cpp │ │ │ ├── Ex7_05.cpp │ │ │ ├── Ex7_06.cpp │ │ │ └── Ex7_07.cpp │ │ ├── Chapter 08/ │ │ │ ├── Ex8_01.cpp │ │ │ ├── Ex8_02.cpp │ │ │ ├── Ex8_03.cpp │ │ │ ├── Ex8_04.cpp │ │ │ ├── Ex8_05.cpp │ │ │ ├── Ex8_05A.cpp │ │ │ ├── Ex8_06.cpp │ │ │ ├── Ex8_06A.cpp │ │ │ ├── Ex8_07.cpp │ │ │ ├── Ex8_08.cpp │ │ │ ├── Ex8_09A.cpp │ │ │ ├── Ex8_09B.cpp │ │ │ ├── Ex8_09C.cpp │ │ │ ├── Ex8_10.cpp │ │ │ ├── Ex8_11.cpp │ │ │ ├── Ex8_12.cpp │ │ │ ├── Ex8_13.cpp │ │ │ ├── Ex8_14.cpp │ │ │ ├── Ex8_15.cpp │ │ │ ├── Ex8_16.cpp │ │ │ └── Ex8_17.cpp │ │ ├── Chapter 09/ │ │ │ ├── Ex9_01.cpp │ │ │ ├── Ex9_02.cpp │ │ │ ├── Ex9_03.cpp │ │ │ └── Ex9_03A.cpp │ │ ├── Chapter 10/ │ │ │ ├── Ex10_01.cpp │ │ │ ├── Ex10_02.cpp │ │ │ ├── Ex10_03.cpp │ │ │ ├── Ex10_03A.cpp │ │ │ └── Ex10_04.cpp │ │ ├── Chapter 11/ │ │ │ ├── Ex11_06/ │ │ │ │ └── Ex11_06.cpp │ │ │ ├── Ex11_07/ │ │ │ │ ├── Ex11_07.cpp │ │ │ │ ├── math.cpp │ │ │ │ └── math.h │ │ │ └── Ex11_08/ │ │ │ ├── Ex11_08.cpp │ │ │ └── squaring.h │ │ ├── Chapter 12/ │ │ │ ├── Ex12_01/ │ │ │ │ └── Ex12_01.cpp │ │ │ ├── Ex12_01A/ │ │ │ │ └── Ex12_01A.cpp │ │ │ ├── Ex12_02/ │ │ │ │ └── Ex12_02.cpp │ │ │ ├── Ex12_03/ │ │ │ │ └── Ex12_03.cpp │ │ │ ├── Ex12_04/ │ │ │ │ └── Ex12_04.cpp │ │ │ ├── Ex12_05/ │ │ │ │ └── Ex12_05.cpp │ │ │ ├── Ex12_05A/ │ │ │ │ └── Ex12_05A.cpp │ │ │ ├── Ex12_06/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_06.cpp │ │ │ ├── Ex12_06A/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_06A.cpp │ │ │ ├── Ex12_06B/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_06B.cpp │ │ │ ├── Ex12_07/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_07.cpp │ │ │ ├── Ex12_08/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_08.cpp │ │ │ ├── Ex12_09/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_09.cpp │ │ │ ├── Ex12_10/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_10.cpp │ │ │ ├── Ex12_11/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_11.cpp │ │ │ ├── Ex12_12/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_12.cpp │ │ │ ├── Ex12_13/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_13.cpp │ │ │ ├── Ex12_14/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_14.cpp │ │ │ ├── Ex12_15/ │ │ │ │ ├── CylindricalBox.cpp │ │ │ │ ├── CylindricalBox.h │ │ │ │ └── Ex12_15.cpp │ │ │ ├── Ex12_16/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex12_16.cpp │ │ │ ├── Ex12_17/ │ │ │ │ ├── Box.h │ │ │ │ ├── Ex12_17.cpp │ │ │ │ ├── Package.h │ │ │ │ ├── RandomBoxes.h │ │ │ │ ├── SharedBox.h │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.h │ │ │ └── Ex12_18/ │ │ │ ├── Box.h │ │ │ ├── Ex12_18.cpp │ │ │ ├── RandomBoxes.h │ │ │ ├── Truckload.cpp │ │ │ └── Truckload.h │ │ ├── Chapter 13/ │ │ │ ├── Ex13_01/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex13_01.cpp │ │ │ ├── Ex13_02/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex13_02.cpp │ │ │ ├── Ex13_03/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex13_03.cpp │ │ │ ├── Ex13_03A/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex13_03A.cpp │ │ │ ├── Ex13_04/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex13_04.cpp │ │ │ ├── Ex13_05/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex13_05.cpp │ │ │ ├── Ex13_06/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Ex13_06.cpp │ │ │ ├── Ex13_07/ │ │ │ │ ├── Ex13_07.cpp │ │ │ │ └── Integer.h │ │ │ ├── Ex13_08/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex13_08.cpp │ │ │ ├── Ex13_09/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex13_09.cpp │ │ │ ├── Ex13_10/ │ │ │ │ ├── Box.h │ │ │ │ ├── Ex13_10.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.h │ │ │ ├── Ex13_11/ │ │ │ │ ├── Box.h │ │ │ │ ├── Ex13_11.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.h │ │ │ ├── Ex13_12/ │ │ │ │ ├── Ex13_12.cpp │ │ │ │ ├── Message.cpp │ │ │ │ └── Message.h │ │ │ ├── Ex13_12A/ │ │ │ │ ├── Ex13_12A.cpp │ │ │ │ ├── Message.cpp │ │ │ │ └── Message.h │ │ │ └── Ex13_12B/ │ │ │ ├── Ex13_12B.cpp │ │ │ ├── Message.cpp │ │ │ └── Message.h │ │ ├── Chapter 14/ │ │ │ ├── Ex14_01/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex14_01.cpp │ │ │ ├── Ex14_01A/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex14_01A.cpp │ │ │ ├── Ex14_02/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex14_02.cpp │ │ │ ├── Ex14_03/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex14_03.cpp │ │ │ ├── Ex14_04/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex14_04.cpp │ │ │ ├── Ex14_04A/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex14_04A.cpp │ │ │ ├── Ex14_04B/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex14_04B.cpp │ │ │ ├── Ex14_04C/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex14_04C.cpp │ │ │ ├── Ex14_05/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex14_05.cpp │ │ │ ├── Ex14_06/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex14_06.cpp │ │ │ ├── Ex14_07/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ ├── CerealPack.h │ │ │ │ ├── Ex14_07.cpp │ │ │ │ └── FoodContainer.h │ │ │ ├── Ex14_07A/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ ├── CerealPack.h │ │ │ │ ├── Ex14_07A.cpp │ │ │ │ └── FoodContainer.h │ │ │ └── Ex14_07B/ │ │ │ ├── Box.h │ │ │ ├── Carton.h │ │ │ ├── CerealPack.h │ │ │ ├── Ex14_07B.cpp │ │ │ └── FoodContainer.h │ │ ├── Chapter 15/ │ │ │ ├── Ex15_01/ │ │ │ │ ├── Box.h │ │ │ │ ├── Ex15_01.cpp │ │ │ │ └── ToughPack.h │ │ │ ├── Ex15_02/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ ├── Ex15_02.cpp │ │ │ │ └── ToughPack.h │ │ │ ├── Ex15_03/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ ├── Ex15_03.cpp │ │ │ │ └── ToughPack.h │ │ │ ├── Ex15_04/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ ├── Ex15_04.cpp │ │ │ │ └── ToughPack.h │ │ │ ├── Ex15_05/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ ├── Ex15_05.cpp │ │ │ │ └── ToughPack.h │ │ │ ├── Ex15_06/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ ├── Ex15_06.cpp │ │ │ │ └── ToughPack.h │ │ │ ├── Ex15_07/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ ├── Ex15_07.cpp │ │ │ │ └── ToughPack.h │ │ │ ├── Ex15_07A/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ ├── Ex15_07A.cpp │ │ │ │ └── ToughPack.h │ │ │ ├── Ex15_08/ │ │ │ │ ├── Box.h │ │ │ │ ├── Ex14_08.cpp │ │ │ │ └── ToughPack.h │ │ │ ├── Ex15_09/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ └── Ex15_09.cpp │ │ │ ├── Ex15_10/ │ │ │ │ ├── Box.h │ │ │ │ ├── Carton.h │ │ │ │ ├── Ex15_10.cpp │ │ │ │ └── ToughPack.h │ │ │ └── Ex15_11/ │ │ │ ├── Box.h │ │ │ ├── Can.h │ │ │ ├── Carton.h │ │ │ ├── Ex15_11.cpp │ │ │ ├── ToughPack.h │ │ │ └── Vessel.h │ │ ├── Chapter 16/ │ │ │ ├── Ex16_01/ │ │ │ │ └── Ex16_01.cpp │ │ │ ├── Ex16_02/ │ │ │ │ ├── Ex16_02.cpp │ │ │ │ └── Troubles.h │ │ │ ├── Ex16_03/ │ │ │ │ ├── Ex16_03.cpp │ │ │ │ └── Troubles.h │ │ │ ├── Ex16_04/ │ │ │ │ ├── Ex16_04.cpp │ │ │ │ └── Troubles.h │ │ │ ├── Ex16_05/ │ │ │ │ ├── Ex16_05.cpp │ │ │ │ └── Troubles.h │ │ │ ├── Ex16_06/ │ │ │ │ ├── Ex16_06.cpp │ │ │ │ └── Troubles.h │ │ │ ├── Ex16_07/ │ │ │ │ ├── Ex16_07.cpp │ │ │ │ └── Troubles.h │ │ │ ├── Ex16_07A/ │ │ │ │ ├── Ex16_07A.cpp │ │ │ │ └── Troubles.h │ │ │ ├── Ex16_07B/ │ │ │ │ ├── DoubleArrayRAII.h │ │ │ │ ├── Ex16_07B.cpp │ │ │ │ └── Troubles.h │ │ │ ├── Ex16_07C/ │ │ │ │ ├── Ex16_07C.cpp │ │ │ │ └── Troubles.h │ │ │ ├── Ex16_07D/ │ │ │ │ ├── Ex16_07D.cpp │ │ │ │ └── Troubles.h │ │ │ └── Ex16_09/ │ │ │ ├── Box.h │ │ │ ├── DimensionError.h │ │ │ └── Ex16_09.cpp │ │ ├── Chapter 17/ │ │ │ ├── Ex17_01/ │ │ │ │ ├── Array.h │ │ │ │ ├── Box.h │ │ │ │ └── Ex17_01.cpp │ │ │ ├── Ex17_01A/ │ │ │ │ ├── Array.h │ │ │ │ ├── Box.h │ │ │ │ └── Ex17_01A.cpp │ │ │ ├── Ex17_02/ │ │ │ │ ├── Array.h │ │ │ │ ├── Box.h │ │ │ │ └── Ex17_02.cpp │ │ │ ├── Ex17_02A/ │ │ │ │ ├── Array.h │ │ │ │ ├── Box.h │ │ │ │ └── Ex17_02A.cpp │ │ │ ├── Ex17_03/ │ │ │ │ ├── Array.h │ │ │ │ ├── Box.h │ │ │ │ └── Ex17_03.cpp │ │ │ ├── Ex17_04/ │ │ │ │ ├── Ex17_04.cpp │ │ │ │ └── Stack.h │ │ │ ├── Ex17_04A/ │ │ │ │ ├── Ex17_04A.cpp │ │ │ │ └── Stack.h │ │ │ ├── Ex17_04B/ │ │ │ │ ├── Ex17_04B.cpp │ │ │ │ └── Stack.h │ │ │ ├── Ex17_05/ │ │ │ │ ├── Ex17_05A.cpp │ │ │ │ ├── Ex17_05B.cpp │ │ │ │ └── Ex17_05C.cpp │ │ │ └── Ex17_06/ │ │ │ └── Ex17_06.cpp │ │ ├── Chapter 18/ │ │ │ ├── Ex18_01/ │ │ │ │ ├── Array.h │ │ │ │ └── Ex18_01.cpp │ │ │ ├── Ex18_02/ │ │ │ │ ├── Array.h │ │ │ │ └── Ex18_02.cpp │ │ │ ├── Ex18_03/ │ │ │ │ ├── Array.h │ │ │ │ └── Ex18_03.cpp │ │ │ ├── Ex18_04/ │ │ │ │ ├── Array.h │ │ │ │ └── Ex18_04.cpp │ │ │ ├── Ex18_05A/ │ │ │ │ ├── Array.h │ │ │ │ └── Ex18_05A.cpp │ │ │ ├── Ex18_05B/ │ │ │ │ ├── Array.h │ │ │ │ └── Ex18_05B.cpp │ │ │ ├── Ex18_06/ │ │ │ │ ├── Array.h │ │ │ │ └── Ex18_06.cpp │ │ │ └── Ex18_07/ │ │ │ ├── Array.h │ │ │ └── Ex18_07.cpp │ │ ├── Chapter 19/ │ │ │ ├── Ex19_01/ │ │ │ │ └── Ex19_01.cpp │ │ │ ├── Ex19_02/ │ │ │ │ ├── Ex19_02.cpp │ │ │ │ └── Optimum.h │ │ │ ├── Ex19_03/ │ │ │ │ ├── Ex19_03.cpp │ │ │ │ ├── Less.h │ │ │ │ └── Optimum.h │ │ │ ├── Ex19_03A/ │ │ │ │ ├── Ex19_03A.cpp │ │ │ │ └── Optimum.h │ │ │ ├── Ex19_04/ │ │ │ │ ├── Ex19_04.cpp │ │ │ │ ├── Nearer.h │ │ │ │ └── Optimum.h │ │ │ ├── Ex19_05/ │ │ │ │ ├── Ex19_05.cpp │ │ │ │ └── Optimum.h │ │ │ ├── Ex19_05A/ │ │ │ │ ├── Ex19_05A.cpp │ │ │ │ └── Optimum.h │ │ │ ├── Ex19_06/ │ │ │ │ ├── Ex19_06.cpp │ │ │ │ └── Optimum.h │ │ │ ├── Ex19_07/ │ │ │ │ ├── Ex19_07.cpp │ │ │ │ ├── Finder.cpp │ │ │ │ ├── Finder.h │ │ │ │ └── Optimum.h │ │ │ └── Ex19_08/ │ │ │ └── Ex19_08.cpp │ │ ├── Chapter 20/ │ │ │ ├── Ex20_01/ │ │ │ │ └── Ex20_01.cpp │ │ │ ├── Ex20_02/ │ │ │ │ └── Ex20_02.cpp │ │ │ ├── Ex20_03/ │ │ │ │ └── Ex20_03.cpp │ │ │ ├── Ex20_04/ │ │ │ │ └── Ex20_04.cpp │ │ │ ├── Ex20_05/ │ │ │ │ └── Ex20_05.cpp │ │ │ ├── Ex20_06/ │ │ │ │ └── Ex20_06.cpp │ │ │ ├── Ex20_07/ │ │ │ │ └── Ex20_07.cpp │ │ │ ├── Ex20_08/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex20_08.cpp │ │ │ ├── Ex20_09/ │ │ │ │ └── Ex20_09.cpp │ │ │ ├── Ex20_10/ │ │ │ │ └── Ex20_10.cpp │ │ │ ├── Ex20_11/ │ │ │ │ └── Ex20_11.cpp │ │ │ ├── Ex20_11A/ │ │ │ │ └── Ex20_11A.cpp │ │ │ ├── Ex20_12/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex20_12.cpp │ │ │ ├── Ex20_12A/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex20_12A.cpp │ │ │ ├── Ex20_13/ │ │ │ │ └── Ex20_13.cpp │ │ │ ├── Ex20_13A/ │ │ │ │ └── Ex20_13A.cpp │ │ │ ├── Ex20_13B/ │ │ │ │ └── Ex20_13B.cpp │ │ │ ├── Ex20_14/ │ │ │ │ └── Ex20_14.cpp │ │ │ ├── Ex20_14A/ │ │ │ │ └── Ex20_14A.cpp │ │ │ ├── Ex20_14B/ │ │ │ │ └── Ex20_14B.cpp │ │ │ ├── Ex20_15/ │ │ │ │ └── Ex20_15.cpp │ │ │ ├── Ex20_15A/ │ │ │ │ └── Ex20_15A.cpp │ │ │ ├── Ex20_16/ │ │ │ │ ├── Box.h │ │ │ │ └── Ex20_16.cpp │ │ │ ├── Ex20_17/ │ │ │ │ └── Ex20_17.cpp │ │ │ └── Ex20_18/ │ │ │ └── Ex20_18.cpp │ │ └── Chapter 21/ │ │ ├── Ex21_01/ │ │ │ └── Ex21_01.cpp │ │ ├── Ex21_02/ │ │ │ └── Ex21_02.cpp │ │ ├── Ex21_03/ │ │ │ ├── Array.h │ │ │ └── Ex21_03.cpp │ │ └── Ex21_04/ │ │ └── Ex21_04.cpp │ └── README.md ├── Exercises/ │ ├── Modules/ │ │ ├── Chapter 01/ │ │ │ ├── Exer1_03.cpp │ │ │ ├── Soln1_01.cpp │ │ │ └── Soln1_02.cpp │ │ ├── Chapter 02/ │ │ │ ├── Soln2_01A.cpp │ │ │ ├── Soln2_01B.cpp │ │ │ ├── Soln2_02A.cpp │ │ │ ├── Soln2_02B.cpp │ │ │ ├── Soln2_03.cpp │ │ │ ├── Soln2_04.cpp │ │ │ ├── Soln2_05.cpp │ │ │ ├── Soln2_06.cpp │ │ │ ├── Soln2_07.cpp │ │ │ └── Soln2_08.cpp │ │ ├── Chapter 03/ │ │ │ ├── Soln3_01.cpp │ │ │ ├── Soln3_02.cpp │ │ │ ├── Soln3_03.cpp │ │ │ ├── Soln3_04.cpp │ │ │ ├── Soln3_05.cpp │ │ │ └── Soln3_06.cpp │ │ ├── Chapter 04/ │ │ │ ├── Soln4_01.cpp │ │ │ ├── Soln4_02.cpp │ │ │ ├── Soln4_03.cpp │ │ │ ├── Soln4_04.cpp │ │ │ ├── Soln4_05.cpp │ │ │ ├── Soln4_06.cpp │ │ │ ├── Soln4_07.cpp │ │ │ └── Soln4_08.cpp │ │ ├── Chapter 05/ │ │ │ ├── Soln5_01.cpp │ │ │ ├── Soln5_02.cpp │ │ │ ├── Soln5_03.cpp │ │ │ ├── Soln5_04.cpp │ │ │ ├── Soln5_05.cpp │ │ │ ├── Soln5_06.cpp │ │ │ ├── Soln5_07.cpp │ │ │ └── Soln5_08.cpp │ │ ├── Chapter 06/ │ │ │ ├── Soln6_01.cpp │ │ │ ├── Soln6_02.cpp │ │ │ ├── Soln6_03.cpp │ │ │ ├── Soln6_04.cpp │ │ │ ├── Soln6_05.cpp │ │ │ └── Soln6_06.cpp │ │ ├── Chapter 08/ │ │ │ ├── Soln8_01.cpp │ │ │ ├── Soln8_02.cpp │ │ │ ├── Soln8_03.cpp │ │ │ ├── Soln8_04.cpp │ │ │ ├── Soln8_05.cpp │ │ │ ├── Soln8_06.cpp │ │ │ ├── Soln8_07.cpp │ │ │ ├── Soln8_07A.cpp │ │ │ ├── Soln8_08.cpp │ │ │ └── Soln8_09.cpp │ │ ├── Chapter 09/ │ │ │ ├── Soln9_01.cpp │ │ │ ├── Soln9_02.cpp │ │ │ ├── Soln9_03.cpp │ │ │ ├── Soln9_04.cpp │ │ │ ├── Soln9_05.cpp │ │ │ └── Soln9_06.cpp │ │ ├── Chapter 10/ │ │ │ ├── Soln10_01.cpp │ │ │ ├── Soln10_02.cpp │ │ │ ├── Soln10_03.cpp │ │ │ ├── Soln10_04.cpp │ │ │ ├── Soln10_05.cpp │ │ │ └── Soln10_06.cpp │ │ ├── Chapter 11/ │ │ │ ├── Soln11_01/ │ │ │ │ ├── Soln11_01.cpp │ │ │ │ └── words.cppm │ │ │ ├── Soln11_02/ │ │ │ │ ├── Soln11_02.cpp │ │ │ │ ├── words.cpp │ │ │ │ └── words.cppm │ │ │ ├── Soln11_03/ │ │ │ │ ├── Soln11_03.cpp │ │ │ │ ├── words.cppm │ │ │ │ ├── words.sorting.cpp │ │ │ │ ├── words.sorting.cppm │ │ │ │ ├── words.utils.cpp │ │ │ │ └── words.utils.cppm │ │ │ ├── Soln11_04/ │ │ │ │ ├── Soln11_04.cpp │ │ │ │ ├── internals.cpp │ │ │ │ ├── words.cpp │ │ │ │ └── words.cppm │ │ │ ├── Soln11_05/ │ │ │ │ ├── Soln11_05.cpp │ │ │ │ ├── alias.cppm │ │ │ │ ├── internals.cpp │ │ │ │ ├── sorting.cppm │ │ │ │ ├── utils.cppm │ │ │ │ └── words.cppm │ │ │ └── Soln11_06/ │ │ │ ├── Soln11_06.cpp │ │ │ ├── words.cpp │ │ │ └── words.cppm │ │ ├── Chapter 12/ │ │ │ ├── Soln12_01/ │ │ │ │ ├── Integer.cpp │ │ │ │ ├── Integer.cppm │ │ │ │ └── Soln12_01.cpp │ │ │ ├── Soln12_02/ │ │ │ │ ├── Integer.cpp │ │ │ │ ├── Integer.cppm │ │ │ │ └── Soln12_02.cpp │ │ │ ├── Soln12_03/ │ │ │ │ ├── Integer.cpp │ │ │ │ ├── Integer.cppm │ │ │ │ └── Soln12_03.cpp │ │ │ ├── Soln12_04/ │ │ │ │ ├── Integer.cpp │ │ │ │ ├── Integer.cppm │ │ │ │ └── Soln12_04.cpp │ │ │ ├── Soln12_05/ │ │ │ │ ├── Integer.cpp │ │ │ │ ├── Integer.cppm │ │ │ │ └── Soln12_05.cpp │ │ │ ├── Soln12_06/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── RandomBoxes.cppm │ │ │ │ ├── Soln12_06.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.cppm │ │ │ ├── Soln12_07/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── RandomBoxes.cppm │ │ │ │ ├── Soln12_07.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.cppm │ │ │ └── Soln12_08/ │ │ │ ├── Box.cppm │ │ │ ├── RandomBoxes.cppm │ │ │ ├── Soln12_08.cpp │ │ │ ├── Truckload.cpp │ │ │ └── Truckload.cppm │ │ ├── Chapter 13/ │ │ │ ├── Soln13_01/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Soln13_01.cpp │ │ │ ├── Soln13_02/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Soln13_02.cpp │ │ │ ├── Soln13_03/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ └── Soln13_03.cpp │ │ │ ├── Soln13_04/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Soln13_04.cpp │ │ │ ├── Soln13_05/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Soln13_05.cpp │ │ │ ├── Soln13_06/ │ │ │ │ ├── Box.cppm │ │ │ │ └── Soln13_06.cpp │ │ │ ├── Soln13_07/ │ │ │ │ ├── Rational.cppm │ │ │ │ └── Soln13_07.cpp │ │ │ ├── Soln13_08/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.cppm │ │ │ │ ├── PRNG.cppm │ │ │ │ └── Soln13_08.cpp │ │ │ └── Soln13_09/ │ │ │ ├── Box.cppm │ │ │ ├── Soln13_09.cpp │ │ │ ├── Truckload.cpp │ │ │ └── Truckload.cppm │ │ ├── Chapter 14/ │ │ │ ├── Soln14_01/ │ │ │ │ ├── Animals.cppm │ │ │ │ └── Soln14_01.cpp │ │ │ ├── Soln14_02/ │ │ │ │ ├── Animals.cppm │ │ │ │ └── Soln14_02.cpp │ │ │ ├── Soln14_03/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.cppm │ │ │ │ └── Soln14_03.cpp │ │ │ └── Soln14_04/ │ │ │ ├── Person.cpp │ │ │ ├── Person.cppm │ │ │ └── Soln14_04.cpp │ │ ├── Chapter 15/ │ │ │ ├── Soln15_01/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.cppm │ │ │ │ ├── Soln15_01.cpp │ │ │ │ ├── Zoo.cpp │ │ │ │ └── Zoo.cppm │ │ │ ├── Soln15_02/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.cppm │ │ │ │ ├── Soln15_02.cpp │ │ │ │ ├── Zoo.cpp │ │ │ │ └── Zoo.cppm │ │ │ ├── Soln15_03/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.cppm │ │ │ │ ├── Soln15_03.cpp │ │ │ │ ├── Zoo.cpp │ │ │ │ └── Zoo.cppm │ │ │ ├── Soln15_04/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.cppm │ │ │ │ ├── Soln15_04.cpp │ │ │ │ ├── Zoo.cpp │ │ │ │ └── Zoo.cppm │ │ │ ├── Soln15_05/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.cppm │ │ │ │ ├── Soln15_05.cpp │ │ │ │ ├── Zoo.cpp │ │ │ │ └── Zoo.cppm │ │ │ └── Soln15_06/ │ │ │ ├── Point.cppm │ │ │ ├── Shapes.cppm │ │ │ └── Soln15_06.cpp │ │ ├── Chapter 16/ │ │ │ ├── Exer16_06/ │ │ │ │ ├── Customer.cpp │ │ │ │ ├── Customer.cppm │ │ │ │ ├── DB.cpp │ │ │ │ ├── DB.h │ │ │ │ ├── DBException.cppm │ │ │ │ └── Exer16_06.cpp │ │ │ ├── Soln16_01/ │ │ │ │ ├── Curveball.cppm │ │ │ │ └── Soln16_01.cpp │ │ │ ├── Soln16_02/ │ │ │ │ ├── Curveball.cppm │ │ │ │ ├── Soln16_02.cpp │ │ │ │ └── TooManyExceptions.cppm │ │ │ ├── Soln16_03/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── RandomBoxes.cppm │ │ │ │ ├── Soln16_03.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.cppm │ │ │ ├── Soln16_04/ │ │ │ │ └── Soln16_04.cpp │ │ │ ├── Soln16_05/ │ │ │ │ ├── Curveball.cppm │ │ │ │ ├── DomainExceptions.cppm │ │ │ │ └── Soln16_05.cpp │ │ │ └── Soln16_06/ │ │ │ ├── Customer.cpp │ │ │ ├── Customer.cppm │ │ │ ├── DB.cpp │ │ │ ├── DB.h │ │ │ ├── DBException.cppm │ │ │ ├── DB_RAII.cppm │ │ │ └── Soln16_06.cpp │ │ ├── Chapter 17/ │ │ │ ├── Soln17_01/ │ │ │ │ ├── Array.cppm │ │ │ │ └── Soln17_01.cpp │ │ │ ├── Soln17_02/ │ │ │ │ ├── Pair.cppm │ │ │ │ └── Soln17_02.cpp │ │ │ ├── Soln17_03/ │ │ │ │ ├── Pair.cppm │ │ │ │ └── Soln17_03.cpp │ │ │ ├── Soln17_04/ │ │ │ │ ├── Pair.cppm │ │ │ │ ├── Soln17_04.cpp │ │ │ │ └── SparseArray.cppm │ │ │ ├── Soln17_05/ │ │ │ │ ├── LinkedList.cppm │ │ │ │ └── Soln17_05.cpp │ │ │ ├── Soln17_06/ │ │ │ │ ├── LinkedList.cppm │ │ │ │ ├── Pair.cppm │ │ │ │ ├── Soln17_06.cpp │ │ │ │ └── SparseArray.cppm │ │ │ ├── Soln17_07/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── BoxFormatter.cppm │ │ │ │ └── Soln17_07.cpp │ │ │ └── Soln17_07A/ │ │ │ ├── Box.cppm │ │ │ ├── BoxFormatter.cppm │ │ │ └── Soln17_07A.cpp │ │ ├── Chapter 18/ │ │ │ ├── Soln18_01/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── RandomBoxes.cppm │ │ │ │ ├── Soln18_01.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.cppm │ │ │ ├── Soln18_02/ │ │ │ │ ├── LinkedList.cppm │ │ │ │ └── Soln18_02.cpp │ │ │ ├── Soln18_03/ │ │ │ │ ├── Customer.cpp │ │ │ │ ├── Customer.cppm │ │ │ │ ├── DB.cpp │ │ │ │ ├── DB.h │ │ │ │ ├── DBException.cppm │ │ │ │ ├── DB_RAII.cppm │ │ │ │ └── Soln18_03.cpp │ │ │ ├── Soln18_04/ │ │ │ │ ├── Customer.cpp │ │ │ │ ├── Customer.cppm │ │ │ │ ├── DB.cpp │ │ │ │ ├── DB.h │ │ │ │ ├── DBException.cppm │ │ │ │ ├── DB_RAII.cppm │ │ │ │ └── Soln18_04.cpp │ │ │ └── Soln18_05/ │ │ │ ├── Customer.cpp │ │ │ ├── Customer.cppm │ │ │ ├── DB.cpp │ │ │ ├── DB.h │ │ │ ├── DBException.cppm │ │ │ ├── DB_RAII.cppm │ │ │ └── Soln18_05.cpp │ │ ├── Chapter 19/ │ │ │ ├── Soln19_01/ │ │ │ │ └── Soln19_01.cpp │ │ │ ├── Soln19_02/ │ │ │ │ ├── Soln19_02.cpp │ │ │ │ └── Sort.cppm │ │ │ ├── Soln19_03/ │ │ │ │ ├── BubbleSort.cppm │ │ │ │ ├── Quicksort.cppm │ │ │ │ ├── Soln19_03.cpp │ │ │ │ └── Sort.cppm │ │ │ ├── Soln19_04/ │ │ │ │ ├── Collect.cppm │ │ │ │ └── Soln19_04.cpp │ │ │ └── Soln19_05/ │ │ │ ├── Box.cppm │ │ │ ├── DeliveryTruck.cpp │ │ │ ├── DeliveryTruck.cppm │ │ │ ├── RandomBoxes.cppm │ │ │ ├── Soln19_05.cpp │ │ │ ├── Truckload.cpp │ │ │ └── Truckload.cppm │ │ ├── Chapter 20/ │ │ │ ├── Soln20_01/ │ │ │ │ ├── Box.cppm │ │ │ │ ├── RandomBoxes.cppm │ │ │ │ ├── Soln20_01.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.cppm │ │ │ ├── Soln20_02/ │ │ │ │ └── Soln20_02.cpp │ │ │ ├── Soln20_03/ │ │ │ │ └── Soln20_03.cpp │ │ │ ├── Soln20_03A/ │ │ │ │ └── Soln20_03A.cpp │ │ │ ├── Soln20_04/ │ │ │ │ └── Soln20_04.cpp │ │ │ ├── Soln20_05/ │ │ │ │ └── Soln20_05.cpp │ │ │ ├── Soln20_06/ │ │ │ │ └── Soln20_06.cpp │ │ │ ├── Soln20_07/ │ │ │ │ └── Soln20_07.cpp │ │ │ ├── Soln20_08/ │ │ │ │ └── Soln20_08.cpp │ │ │ ├── Soln20_09/ │ │ │ │ └── Soln20_09.cpp │ │ │ ├── Soln20_10/ │ │ │ │ └── Soln20_10.cpp │ │ │ ├── Soln20_11/ │ │ │ │ └── Soln20_11.cpp │ │ │ ├── Soln20_12/ │ │ │ │ └── Soln20_12.cpp │ │ │ ├── Soln20_13/ │ │ │ │ └── Soln20_13.cpp │ │ │ └── Soln20_14/ │ │ │ └── Soln20_14.cpp │ │ └── Chapter 21/ │ │ ├── Soln21_01/ │ │ │ └── Soln21_01.cpp │ │ ├── Soln21_02/ │ │ │ └── Soln21_02.cpp │ │ ├── Soln21_03/ │ │ │ └── Soln21_03.cpp │ │ ├── Soln21_04/ │ │ │ └── Soln21_04.cpp │ │ ├── Soln21_05/ │ │ │ └── Soln21_05.cpp │ │ └── Soln21_06/ │ │ ├── Array.cppm │ │ └── Soln21_06.cpp │ ├── NoModules/ │ │ ├── Appendix A/ │ │ │ ├── SolnA_01/ │ │ │ │ ├── ASSERT.h │ │ │ │ └── SolnA_01.cpp │ │ │ ├── SolnA_02/ │ │ │ │ ├── ASSERT.h │ │ │ │ └── SolnA_02.cpp │ │ │ ├── SolnA_03/ │ │ │ │ ├── SmartException.cpp │ │ │ │ ├── SmartException.h │ │ │ │ └── SolnA_03.cpp │ │ │ ├── SolnA_04/ │ │ │ │ ├── SmartException.h │ │ │ │ └── SolnA_04.cpp │ │ │ ├── SolnA_05/ │ │ │ │ ├── Print.cpp │ │ │ │ ├── PrintThat.cpp │ │ │ │ ├── PrintThat.h │ │ │ │ ├── PrintThis.cpp │ │ │ │ ├── PrintThis.h │ │ │ │ └── SolnA_05.cpp │ │ │ ├── SolnA_06/ │ │ │ │ ├── Print.h │ │ │ │ ├── PrintThat.cpp │ │ │ │ ├── PrintThat.h │ │ │ │ ├── PrintThis.cpp │ │ │ │ ├── PrintThis.h │ │ │ │ └── SolnA_06.cpp │ │ │ ├── SolnA_07A/ │ │ │ │ ├── Print.cpp │ │ │ │ ├── PrintThat.cpp │ │ │ │ ├── PrintThat.h │ │ │ │ ├── PrintThis.cpp │ │ │ │ ├── PrintThis.h │ │ │ │ └── SolnA_07A.cpp │ │ │ └── SolnA_07B/ │ │ │ ├── Print.h │ │ │ ├── PrintThat.cpp │ │ │ ├── PrintThat.h │ │ │ ├── PrintThis.cpp │ │ │ ├── PrintThis.h │ │ │ └── SolnA_07B.cpp │ │ ├── Chapter 01/ │ │ │ ├── Exer1_03.cpp │ │ │ ├── Soln1_01.cpp │ │ │ └── Soln1_02.cpp │ │ ├── Chapter 02/ │ │ │ ├── Soln2_01A.cpp │ │ │ ├── Soln2_01B.cpp │ │ │ ├── Soln2_02A.cpp │ │ │ ├── Soln2_02B.cpp │ │ │ ├── Soln2_03.cpp │ │ │ ├── Soln2_04.cpp │ │ │ ├── Soln2_05.cpp │ │ │ ├── Soln2_06.cpp │ │ │ ├── Soln2_07.cpp │ │ │ └── Soln2_08.cpp │ │ ├── Chapter 03/ │ │ │ ├── Soln3_01.cpp │ │ │ ├── Soln3_02.cpp │ │ │ ├── Soln3_03.cpp │ │ │ ├── Soln3_04.cpp │ │ │ ├── Soln3_05.cpp │ │ │ └── Soln3_06.cpp │ │ ├── Chapter 04/ │ │ │ ├── Soln4_01.cpp │ │ │ ├── Soln4_02.cpp │ │ │ ├── Soln4_03.cpp │ │ │ ├── Soln4_04.cpp │ │ │ ├── Soln4_05.cpp │ │ │ ├── Soln4_06.cpp │ │ │ ├── Soln4_07.cpp │ │ │ └── Soln4_08.cpp │ │ ├── Chapter 05/ │ │ │ ├── Soln5_01.cpp │ │ │ ├── Soln5_02.cpp │ │ │ ├── Soln5_03.cpp │ │ │ ├── Soln5_04.cpp │ │ │ ├── Soln5_05.cpp │ │ │ ├── Soln5_06.cpp │ │ │ ├── Soln5_07.cpp │ │ │ └── Soln5_08.cpp │ │ ├── Chapter 06/ │ │ │ ├── Soln6_01.cpp │ │ │ ├── Soln6_02.cpp │ │ │ ├── Soln6_03.cpp │ │ │ ├── Soln6_04.cpp │ │ │ ├── Soln6_05.cpp │ │ │ └── Soln6_06.cpp │ │ ├── Chapter 07/ │ │ │ ├── Soln7_01.cpp │ │ │ ├── Soln7_02.cpp │ │ │ ├── Soln7_03.cpp │ │ │ ├── Soln7_04.cpp │ │ │ ├── Soln7_05.cpp │ │ │ ├── Soln7_06.cpp │ │ │ ├── Soln7_07.cpp │ │ │ ├── Soln7_08A.cpp │ │ │ ├── Soln7_08B.cpp │ │ │ ├── Soln7_09A.cpp │ │ │ └── Soln7_09B.cpp │ │ ├── Chapter 08/ │ │ │ ├── Soln8_01.cpp │ │ │ ├── Soln8_02.cpp │ │ │ ├── Soln8_03.cpp │ │ │ ├── Soln8_04.cpp │ │ │ ├── Soln8_05.cpp │ │ │ ├── Soln8_06.cpp │ │ │ ├── Soln8_07.cpp │ │ │ ├── Soln8_07A.cpp │ │ │ ├── Soln8_08.cpp │ │ │ └── Soln8_09.cpp │ │ ├── Chapter 09/ │ │ │ ├── Soln9_01.cpp │ │ │ ├── Soln9_02.cpp │ │ │ ├── Soln9_03.cpp │ │ │ ├── Soln9_04.cpp │ │ │ ├── Soln9_05.cpp │ │ │ └── Soln9_06.cpp │ │ ├── Chapter 10/ │ │ │ ├── Soln10_01.cpp │ │ │ ├── Soln10_02.cpp │ │ │ ├── Soln10_03.cpp │ │ │ ├── Soln10_04.cpp │ │ │ ├── Soln10_05.cpp │ │ │ └── Soln10_06.cpp │ │ ├── Chapter 12/ │ │ │ ├── Soln12_01/ │ │ │ │ ├── Integer.cpp │ │ │ │ ├── Integer.h │ │ │ │ └── Soln12_01.cpp │ │ │ ├── Soln12_02/ │ │ │ │ ├── Integer.cpp │ │ │ │ ├── Integer.h │ │ │ │ └── Soln12_02.cpp │ │ │ ├── Soln12_03/ │ │ │ │ ├── Integer.cpp │ │ │ │ ├── Integer.h │ │ │ │ └── Soln12_03.cpp │ │ │ ├── Soln12_04/ │ │ │ │ ├── Integer.cpp │ │ │ │ ├── Integer.h │ │ │ │ └── Soln12_04.cpp │ │ │ ├── Soln12_05/ │ │ │ │ ├── Integer.cpp │ │ │ │ ├── Integer.h │ │ │ │ └── Soln12_05.cpp │ │ │ ├── Soln12_06/ │ │ │ │ ├── Box.h │ │ │ │ ├── RandomBoxes.h │ │ │ │ ├── Soln12_06.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.h │ │ │ ├── Soln12_07/ │ │ │ │ ├── Box.h │ │ │ │ ├── RandomBoxes.h │ │ │ │ ├── Soln12_07.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.h │ │ │ └── Soln12_08/ │ │ │ ├── Box.h │ │ │ ├── RandomBoxes.h │ │ │ ├── Soln12_08.cpp │ │ │ ├── Truckload.cpp │ │ │ └── Truckload.h │ │ ├── Chapter 13/ │ │ │ ├── Soln13_01/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Soln13_01.cpp │ │ │ ├── Soln13_02/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Soln13_02.cpp │ │ │ ├── Soln13_03/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ └── Soln13_03.cpp │ │ │ ├── Soln13_04/ │ │ │ │ ├── Box.h │ │ │ │ └── Soln13_04.cpp │ │ │ ├── Soln13_05/ │ │ │ │ ├── Box.h │ │ │ │ └── Soln13_05.cpp │ │ │ ├── Soln13_06/ │ │ │ │ ├── Box.h │ │ │ │ └── Soln13_06.cpp │ │ │ ├── Soln13_07/ │ │ │ │ ├── Rational.h │ │ │ │ └── Soln13_07.cpp │ │ │ ├── Soln13_08/ │ │ │ │ ├── Box.cpp │ │ │ │ ├── Box.h │ │ │ │ ├── PRNG.h │ │ │ │ └── Soln13_08.cpp │ │ │ └── Soln13_09/ │ │ │ ├── Box.h │ │ │ ├── Soln13_09.cpp │ │ │ ├── Truckload.cpp │ │ │ └── Truckload.h │ │ ├── Chapter 14/ │ │ │ ├── Soln14_01/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.h │ │ │ │ └── Soln14_01.cpp │ │ │ ├── Soln14_02/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.h │ │ │ │ └── Soln14_02.cpp │ │ │ ├── Soln14_03/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.h │ │ │ │ └── Soln14_03.cpp │ │ │ └── Soln14_04/ │ │ │ ├── Person.cpp │ │ │ ├── Person.h │ │ │ └── Soln14_04.cpp │ │ ├── Chapter 15/ │ │ │ ├── Soln15_01/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.h │ │ │ │ ├── Soln15_01.cpp │ │ │ │ ├── Zoo.cpp │ │ │ │ └── Zoo.h │ │ │ ├── Soln15_02/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.h │ │ │ │ ├── Soln15_02.cpp │ │ │ │ ├── Zoo.cpp │ │ │ │ └── Zoo.h │ │ │ ├── Soln15_03/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.h │ │ │ │ ├── Soln15_03.cpp │ │ │ │ ├── Zoo.cpp │ │ │ │ └── Zoo.h │ │ │ ├── Soln15_04/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.h │ │ │ │ ├── Soln15_04.cpp │ │ │ │ ├── Zoo.cpp │ │ │ │ └── Zoo.h │ │ │ ├── Soln15_05/ │ │ │ │ ├── Animals.cpp │ │ │ │ ├── Animals.h │ │ │ │ ├── Soln15_05.cpp │ │ │ │ ├── Zoo.cpp │ │ │ │ └── Zoo.h │ │ │ └── Soln15_06/ │ │ │ ├── Point.h │ │ │ ├── Shapes.h │ │ │ └── Soln15_06.cpp │ │ ├── Chapter 16/ │ │ │ ├── Exer16_06/ │ │ │ │ ├── Customer.cpp │ │ │ │ ├── Customer.h │ │ │ │ ├── DB.cpp │ │ │ │ ├── DB.h │ │ │ │ ├── DBException.h │ │ │ │ └── Exer16_06.cpp │ │ │ ├── Soln16_01/ │ │ │ │ ├── Curveball.h │ │ │ │ └── Soln16_01.cpp │ │ │ ├── Soln16_02/ │ │ │ │ ├── Curveball.h │ │ │ │ ├── Soln16_02.cpp │ │ │ │ └── TooManyExceptions.h │ │ │ ├── Soln16_03/ │ │ │ │ ├── Box.h │ │ │ │ ├── RandomBoxes.h │ │ │ │ ├── Soln16_03.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.h │ │ │ ├── Soln16_04/ │ │ │ │ └── Soln16_04.cpp │ │ │ ├── Soln16_05/ │ │ │ │ ├── Curveball.h │ │ │ │ ├── DomainExceptions.h │ │ │ │ └── Soln16_05.cpp │ │ │ └── Soln16_06/ │ │ │ ├── Customer.cpp │ │ │ ├── Customer.h │ │ │ ├── DB.cpp │ │ │ ├── DB.h │ │ │ ├── DBException.h │ │ │ ├── DB_RAII.h │ │ │ └── Soln16_06.cpp │ │ ├── Chapter 17/ │ │ │ ├── Soln17_01/ │ │ │ │ ├── Array.h │ │ │ │ └── Soln17_01.cpp │ │ │ ├── Soln17_02/ │ │ │ │ ├── Pair.h │ │ │ │ └── Soln17_02.cpp │ │ │ ├── Soln17_03/ │ │ │ │ ├── Pair.h │ │ │ │ └── Soln17_03.cpp │ │ │ ├── Soln17_04/ │ │ │ │ ├── Pair.h │ │ │ │ ├── Soln17_04.cpp │ │ │ │ └── SparseArray.h │ │ │ ├── Soln17_05/ │ │ │ │ ├── LinkedList.h │ │ │ │ └── Soln17_05.cpp │ │ │ ├── Soln17_06/ │ │ │ │ ├── LinkedList.h │ │ │ │ ├── Pair.h │ │ │ │ ├── Soln17_06.cpp │ │ │ │ └── SparseArray.h │ │ │ ├── Soln17_07/ │ │ │ │ ├── Box.h │ │ │ │ ├── BoxFormatter.h │ │ │ │ └── Soln17_07.cpp │ │ │ └── Soln17_07A/ │ │ │ ├── Box.h │ │ │ ├── BoxFormatter.h │ │ │ └── Soln17_07A.cpp │ │ ├── Chapter 18/ │ │ │ ├── Soln18_01/ │ │ │ │ ├── Box.h │ │ │ │ ├── RandomBoxes.h │ │ │ │ ├── Soln18_01.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.h │ │ │ ├── Soln18_02/ │ │ │ │ ├── LinkedList.h │ │ │ │ └── Soln18_02.cpp │ │ │ ├── Soln18_03/ │ │ │ │ ├── Customer.cpp │ │ │ │ ├── Customer.h │ │ │ │ ├── DB.cpp │ │ │ │ ├── DB.h │ │ │ │ ├── DBException.h │ │ │ │ ├── DB_RAII.h │ │ │ │ └── Soln18_03.cpp │ │ │ ├── Soln18_04/ │ │ │ │ ├── Customer.cpp │ │ │ │ ├── Customer.h │ │ │ │ ├── DB.cpp │ │ │ │ ├── DB.h │ │ │ │ ├── DBException.h │ │ │ │ ├── DB_RAII.h │ │ │ │ └── Soln18_04.cpp │ │ │ └── Soln18_05/ │ │ │ ├── Customer.cpp │ │ │ ├── Customer.h │ │ │ ├── DB.cpp │ │ │ ├── DB.h │ │ │ ├── DBException.h │ │ │ ├── DB_RAII.h │ │ │ └── Soln18_05.cpp │ │ ├── Chapter 19/ │ │ │ ├── Soln19_01/ │ │ │ │ └── Soln19_01.cpp │ │ │ ├── Soln19_02/ │ │ │ │ ├── Soln19_02.cpp │ │ │ │ └── Sort.h │ │ │ ├── Soln19_03/ │ │ │ │ ├── Soln19_03.cpp │ │ │ │ └── Sort.h │ │ │ ├── Soln19_04/ │ │ │ │ ├── Collect.h │ │ │ │ └── Soln19_04.cpp │ │ │ └── Soln19_05/ │ │ │ ├── Box.h │ │ │ ├── DeliveryTruck.cpp │ │ │ ├── DeliveryTruck.h │ │ │ ├── RandomBoxes.h │ │ │ ├── Soln19_05.cpp │ │ │ ├── Truckload.cpp │ │ │ └── Truckload.h │ │ ├── Chapter 20/ │ │ │ ├── Soln20_01/ │ │ │ │ ├── Box.h │ │ │ │ ├── RandomBoxes.h │ │ │ │ ├── Soln20_01.cpp │ │ │ │ ├── Truckload.cpp │ │ │ │ └── Truckload.h │ │ │ ├── Soln20_02/ │ │ │ │ └── Soln20_02.cpp │ │ │ ├── Soln20_03/ │ │ │ │ └── Soln20_03.cpp │ │ │ ├── Soln20_03A/ │ │ │ │ └── Soln20_03A.cpp │ │ │ ├── Soln20_04/ │ │ │ │ └── Soln20_04.cpp │ │ │ ├── Soln20_05/ │ │ │ │ └── Soln20_05.cpp │ │ │ ├── Soln20_06/ │ │ │ │ └── Soln20_06.cpp │ │ │ ├── Soln20_07/ │ │ │ │ └── Soln20_07.cpp │ │ │ ├── Soln20_08/ │ │ │ │ └── Soln20_08.cpp │ │ │ ├── Soln20_09/ │ │ │ │ └── Soln20_09.cpp │ │ │ ├── Soln20_10/ │ │ │ │ └── Soln20_10.cpp │ │ │ ├── Soln20_11/ │ │ │ │ └── Soln20_11.cpp │ │ │ ├── Soln20_12/ │ │ │ │ └── Soln20_12.cpp │ │ │ ├── Soln20_13/ │ │ │ │ └── Soln20_13.cpp │ │ │ └── Soln20_14/ │ │ │ └── Soln20_14.cpp │ │ └── Chapter 21/ │ │ ├── Soln21_01/ │ │ │ └── Soln21_01.cpp │ │ ├── Soln21_02/ │ │ │ └── Soln21_02.cpp │ │ ├── Soln21_03/ │ │ │ └── Soln21_03.cpp │ │ ├── Soln21_04/ │ │ │ └── Soln21_04.cpp │ │ ├── Soln21_05/ │ │ │ └── Soln21_05.cpp │ │ └── Soln21_06/ │ │ ├── Array.h │ │ └── Soln21_06.cpp │ └── README.md ├── LICENSE.txt ├── README.md ├── Workarounds/ │ ├── README.md │ ├── format │ └── ranges └── errata.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitmodules ================================================ [submodule "Workarounds/fmt"] path = Workarounds/fmt url = https://github.com/fmtlib/fmt.git [submodule "Workarounds/range-v3"] path = Workarounds/range-v3 url = https://github.com/ericniebler/range-v3 ================================================ FILE: Contributing.md ================================================ # Contributing to Apress Source Code Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. ## How to Contribute 1. Make sure you have a GitHub account. 2. Fork the repository for the relevant book. 3. Create a new branch on which to make your change, e.g. `git checkout -b my_code_contribution` 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 5. Submit a pull request. Thank you for your contribution! ================================================ FILE: Examples/Modules/Chapter 01/Ex1_01.cpp ================================================ // A complete C++ program import ; int main() { int answer {42}; // Defines answer with value 42 std::cout << "The answer to life, the universe, and everything is " << answer << std::endl; return 0; } ================================================ FILE: Examples/Modules/Chapter 01/Ex1_02.cpp ================================================ // Using escape sequences import ; int main() { std::cout << "\"Least \'said\' \\\n\t\tsoonest \'mended\'.\"" << std::endl; } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_01.cpp ================================================ // Writing values of variables to cout import ; // For user input and output through std::cin / cout int main() { int apple_count {15}; // Number of apples int orange_count {5}; // Number of oranges int total_fruit {apple_count + orange_count}; // Total number of fruit std::cout << "The value of apple_count is " << apple_count << std::endl; std::cout << "The value of orange_count is " << orange_count << std::endl; std::cout << "The value of total_fruit is " << total_fruit << std::endl; } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_02.cpp ================================================ // Converting distances import ; // For user input and output through std::cin / cout int main() { unsigned int yards {}, feet {}, inches {}; // Convert a distance in yards, feet, and inches to inches std::cout << "Enter a distance as yards, feet, and inches " << "with the three values separated by spaces: "; std::cin >> yards >> feet >> inches; const unsigned feet_per_yard {3}; const unsigned inches_per_foot {12}; unsigned total_inches {}; total_inches = inches + inches_per_foot * (yards*feet_per_yard + feet); std::cout << "The distances corresponds to " << total_inches << " inches.\n"; // Convert a distance in inches to yards feet and inches std::cout << "Enter a distance in inches: "; std::cin >> total_inches; feet = total_inches / inches_per_foot; inches = total_inches % inches_per_foot; yards = feet / feet_per_yard; feet = feet % feet_per_yard; std::cout << "The distances corresponds to " << yards << " yards " << feet << " feet " << inches << " inches." << std::endl; } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_03.cpp ================================================ // Sizing a pond for happy fish import ; import ; // For the pi constant #include // For the square root function int main() { // 2 square feet pond surface for every 6 inches of fish const double fish_factor { 2.0/0.5 }; // Area per unit length of fish const double inches_per_foot { 12.0 }; double fish_count {}; // Number of fish double fish_length {}; // Average length of fish std::cout << "Enter the number of fish you want to keep: "; std::cin >> fish_count; std::cout << "Enter the average fish length in inches: "; std::cin >> fish_length; fish_length /= inches_per_foot; // Convert to feet std::cout << '\n'; // Calculate the required surface area const double pond_area {fish_count * fish_length * fish_factor}; // Calculate the pond diameter from the area const double pond_diameter {2.0 * std::sqrt(pond_area / std::numbers::pi)}; std::cout << "Pond diameter required for " << fish_count << " fish is " << pond_diameter << " feet.\n"; } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_03A.cpp ================================================ // Expressions with mixed variables types // (The difference with the original example // is the type of fish_count and inches_per_foot) import ; import ; // For the pi constant #include // For the square root function int main() { // 2 square feet pond surface for every 6 inches of fish const double fish_factor { 2.0/0.5 }; // Area per unit length of fish const unsigned int inches_per_foot { 12 }; // <-- Used to be of type double unsigned int fish_count {}; // Number of fish (used to be of type double as well) double fish_length {}; // Average length of fish std::cout << "Enter the number of fish you want to keep: "; std::cin >> fish_count; std::cout << "Enter the average fish length in inches: "; std::cin >> fish_length; fish_length /= inches_per_foot; // Convert to feet std::cout << '\n'; // Calculate the required surface area const double pond_area {fish_count * fish_length * fish_factor}; // Calculate the pond diameter from the area const double pond_diameter {2.0 * std::sqrt(pond_area / std::numbers::pi)}; std::cout << "Pond diameter required for " << fish_count << " fish is " << pond_diameter << " feet.\n"; } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_03B.cpp ================================================ // Formatting text using std::format() import ; import ; import ; // For the pi constant #include // For the square root function int main() { // 2 square feet pond surface for every 6 inches of fish const double fish_factor{ 2.0 / 0.5 }; // Area per unit length of fish const unsigned int inches_per_foot{ 12 }; unsigned int fish_count{}; // Number of fish double fish_length{}; // Average length of fish std::cout << "Enter the number of fish you want to keep: "; std::cin >> fish_count; std::cout << "Enter the average fish length in inches: "; std::cin >> fish_length; fish_length /= inches_per_foot; // Convert to feet std::cout << '\n'; // Calculate the required surface area const double pond_area{ fish_count * fish_length * fish_factor }; // Calculate the pond diameter from the area const double pond_diameter{ 2.0 * std::sqrt(pond_area / std::numbers::pi) }; std::cout << std::format("Pond diameter required for {} fish is {} feet.\n", fish_count, pond_diameter); } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_03C.cpp ================================================ // Format specifiers for std::format() import ; import ; import ; // For the pi constant #include // For the square root function int main() { // 2 square feet pond surface for every 6 inches of fish const double fish_factor{ 2.0 / 0.5 }; // Area per unit length of fish const unsigned int inches_per_foot{ 12 }; unsigned int fish_count{}; // Number of fish double fish_length{}; // Average length of fish std::cout << "Enter the number of fish you want to keep: "; std::cin >> fish_count; std::cout << "Enter the average fish length in inches: "; std::cin >> fish_length; fish_length /= inches_per_foot; // Convert to feet std::cout << '\n'; // Calculate the required surface area const double pond_area{ fish_count * fish_length * fish_factor }; // Calculate the pond diameter from the area const double pond_diameter{ 2.0 * std::sqrt(pond_area / std::numbers::pi) }; std::cout << std::format("Pond diameter required for {} fish is {:.2} feet.\n", fish_count, pond_diameter); } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_03D.cpp ================================================ // Debugging format specifiers for std::format() using try/catch import ; import ; import ; // For the pi constant #include // For the square root function int main() { // 2 square feet pond surface for every 6 inches of fish const double fish_factor{ 2.0 / 0.5 }; // Area per unit length of fish const unsigned int inches_per_foot{ 12 }; unsigned int fish_count{}; // Number of fish double fish_length{}; // Average length of fish std::cout << "Enter the number of fish you want to keep: "; std::cin >> fish_count; std::cout << "Enter the average fish length in inches: "; std::cin >> fish_length; fish_length /= inches_per_foot; // Convert to feet std::cout << '\n'; // Calculate the required surface area const double pond_area{ fish_count * fish_length * fish_factor }; // Calculate the pond diameter from the area const double pond_diameter{ 2.0 * std::sqrt(pond_area / std::numbers::pi) }; try { std::cout << std::format("Pond diameter required for {:.2} fish is {:.2} feet.\n", fish_count, pond_diameter); } catch (const std::format_error& error) { std::cout << error.what(); // Outputs "precision not allowed for this argument type" } } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_04.cpp ================================================ // Using explicit type conversions import ; int main() { const unsigned feet_per_yard{ 3 }; const unsigned inches_per_foot{ 12 }; const unsigned inches_per_yard{ feet_per_yard * inches_per_foot }; double length{}; // Length as decimal yards unsigned int yards{}; // Whole yards unsigned int feet{}; // Whole feet unsigned int inches{}; // Whole inches std::cout << "Enter a length in yards as a decimal: "; std::cin >> length; // Get the length as yards, feet, and inches yards = static_cast(length); feet = static_cast((length - yards) * feet_per_yard); inches = static_cast(length * inches_per_yard) % inches_per_foot; std::cout << length << " yards converts to " << yards << " yards " << feet << " feet " << inches << " inches." << std::endl; } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_05.cpp ================================================ // The width, alignment, fill, and 0 formatting options of std::format() import ; import ; int main() { // Default alignment: right for numbers, left otherwise std::cout << std::format("{:7}|{:7}|{:7}|{:7}|{:7}\n", 1, -.2, "str", 'c', true); // Left and right alignment + custom fill character std::cout << std::format("{:*<7}|{:*<7}|{:*>7}|{:*>7}|{:*>7}\n", 1, -.2, "str", 'c', true); // Centered alignment + 0 formatting option for numbers std::cout << std::format("{:^07}|{:^07}|{:^7}|{:^7}|{:^7}\n", 1, -.2, "str", 'c', true); } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_06.cpp ================================================ // Formatting numeric values with std::format() import ; import ; import ; int main() { const double pi{ std::numbers::pi }; std::cout << std::format("Default: {:.2}, fixed: {:.2f}, scientific: {:.2e}, " "general: {:.2g}\n", pi, pi, pi, pi); std::cout << std::format("Default: {}, binary: {:b}, hex.: {:x}\n", 314, 314, 314); std::cout << std::format("Default: {}, decimal: {:d}, hex.: {:x}\n", 'c', 'c', 'c'); std::cout << std::format("Alternative hex.: {:#x}, binary: {:#b}, HEX.: {:#X}\n", 314, 314, 314); std::cout << std::format("Forced sign: {:+}, space sign: {: }\n", 314, 314); std::cout << std::format("All together: {:*<+10.4f}, {:+#09x}\n", pi, 314); } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_06B.cpp ================================================ // Argument indices for std::format() import ; import ; import ; int main() { const double pi{ std::numbers::pi }; std::cout << std::format("Default: {:.2}, fixed: {:.2f}, scientific: {:.2e}, " "general: {:.2g}\n", pi, pi, pi, pi); std::cout << std::format("Default: {0}, binary: {0:b}, hex.: {0:x}\n", 314); std::cout << std::format("Default: {0}, decimal: {0:d}, hex.: {0:x}\n", 'c'); std::cout << std::format("Alternative hex.: {0:#x}, binary: {0:#b}, HEX.: {0:#X}\n", 314); std::cout << std::format("Forced sign: {0:+}, space sign: {0: }\n", 314); std::cout << std::format("All together: {:*<+10.4f}, {:+#09x}\n", pi, 314); } ================================================ FILE: Examples/Modules/Chapter 02/Ex2_07.cpp ================================================ // Finding maximum and minimum values for data types import ; import ; import ; int main() { std::cout << std::format("The range for type short is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()) << std::format("The range for type unsigned int is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()) << std::format("The range for type long is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()) << std::format("The positive range for type float is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()) << std::format("The full range for type float is from {} to {}\n", std::numeric_limits::lowest(), std::numeric_limits::max()) << std::format("The positive range for type double is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()) << std::format("The positive range for type long double is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()); } ================================================ FILE: Examples/Modules/Chapter 03/Ex3_01.cpp ================================================ // Using the bitwise operators import ; import ; int main() { const unsigned int red{ 0xFF0000u }; // Color red const unsigned int white{ 0xFFFFFFu }; // Color white - RGB all maximum std::cout << "Try out bitwise complement, AND and OR operators:\n"; std::cout << std::format("Initial value: red = {:08X}\n", red); std::cout << std::format("Complement: ~red = {:08X}\n", ~red); std::cout << std::format("Initial value: white = {:08X}\n", white); std::cout << std::format("Complement: ~white = {:08X}\n", ~white); std::cout << std::format("Bitwise AND: red & white = {:08X}\n", red & white); std::cout << std::format("Bitwise OR: red | white = {:08X}\n", red | white); std::cout << "\nNow try successive exclusive OR operations:\n"; unsigned int mask{ red ^ white }; std::cout << std::format("mask = red ^ white = {:08X}\n", mask); std::cout << std::format(" mask ^ red = {:08X}\n", mask ^ red); std::cout << std::format(" mask ^ white = {:08X}\n", mask ^ white); unsigned int flags{ 0xFF }; // Flags variable unsigned int bit1mask{ 0x1 }; // Selects bit 1 unsigned int bit6mask{ 0b100000 }; // Selects bit 6 unsigned int bit20mask{ 1u << 19 }; // Selects bit 20 std::cout << "Use masks to select or set a particular flag bit:\n"; std::cout << std::format("Select bit 1 from flags : {:08X}\n", flags & bit1mask); std::cout << std::format("Select bit 6 from flags : {:08X}\n", flags & bit6mask); std::cout << std::format("Switch off bit 6 in flags: {:08X}\n", flags &= ~bit6mask); std::cout << std::format("Switch on bit 20 in flags: {:08X}\n", flags |= bit20mask); } ================================================ FILE: Examples/Modules/Chapter 03/Ex3_02.cpp ================================================ // Demonstrating scope, lifetime, and global variables import ; long count1{999L}; // Global count1 double count2{3.14}; // Global count2 int count3; // Global count3 - default initialization int main() { /* Function scope starts here */ int count1{10}; // Hides global count1 int count3{50}; // Hides global count3 std::cout << "Value of outer count1 = " << count1 << std::endl; std::cout << "Value of global count1 = " << ::count1 << std::endl; std::cout << "Value of global count2 = " << count2 << std::endl; { /* New block scope starts here... */ int count1{20}; // This is a new variable that hides the outer count1 int count2{30}; // This hides global count2 std::cout << "\nValue of inner count1 = "<< count1 << std::endl; std::cout << "Value of global count1 = " << ::count1 << std::endl; std::cout << "Value of inner count2 = " << count2 << std::endl; std::cout << "Value of global count2 = " << ::count2 << std::endl; count1 = ::count1 + 3; // This sets inner count1 to global count1+3 ++::count1; // This changes global count1 std::cout << "\nValue of inner count1 = " << count1 << std::endl; std::cout << "Value of global count1 = " << ::count1 << std::endl; count3 += count2; // Increments outer count3 by inner count2; int count4 {}; } /* ...and ends here. */ // std::cout << count4 << std::endl; // count4 does not exist in this scope! std::cout << "\nValue of outer count1 = "<< count1 << std::endl << "Value of outer count3 = " << count3 << std::endl; std::cout << "Value of global count3 = " << ::count3 << std::endl; std::cout << "Value of global count2 = " << count2 << std::endl; } /* Function scope ends here */ ================================================ FILE: Examples/Modules/Chapter 03/Ex3_03.cpp ================================================ // Operations with enumerations import ; import ; int main() { enum class Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }; Day yesterday{ Day::Monday }, today{ Day::Tuesday }, tomorrow{ Day::Wednesday }; const Day poets_day{ Day::Friday }; enum class Punctuation : char { Comma = ',', Exclamation = '!', Question = '?' }; Punctuation ch{ Punctuation::Comma }; std::cout << std::format("yesterday's value is {}{} but poets_day's is {}{}\n", static_cast(yesterday), static_cast(ch), static_cast(poets_day), static_cast(Punctuation::Exclamation)); today = Day::Thursday; // Assign new ... ch = Punctuation::Question; // ... enumerator values tomorrow = poets_day; // Copy enumerator value std::cout << std::format("Is today's value({}) the same as poets_day({}){}\n", static_cast(today), static_cast(poets_day), static_cast(ch)); // ch = tomorrow; /* Uncomment any of these for an error */ // tomorrow = Friday; // today = 6; } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_01.cpp ================================================ // Comparing data values import ; int main() { char first {}; // Stores the first character char second {}; // Stores the second character std::cout << "Enter a character: "; std::cin >> first; std::cout << "Enter a second character: "; std::cin >> second; // std::cout << std::boolalpha; /* Output true/false instead of 1/0 */ std::cout << "The value of the expression " << first << '<' << second << " is " << (first < second) << std::endl; std::cout << "The value of the expression " << first << "==" << second << " is " << (first == second) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_01A.cpp ================================================ // Comparing data values (output using std::format()) import ; import ; int main() { char first {}; // Stores the first character char second {}; // Stores the second character std::cout << "Enter a character: "; std::cin >> first; std::cout << "Enter a second character: "; std::cin >> second; std::cout << std::format("The value of the expression {} < {} is {}\n", first, second, first < second); std::cout << std::format("The value of the expression {} == {} is {}\n", first, second, first == second); } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_02.cpp ================================================ // Three-way comparison of integers import ; // Required when using operator <=> (even for fundamental types) import ; import ; int main() { std::cout << "Please enter a number: "; int value; std::cin >> value; std::strong_ordering ordering{ value <=> 0 }; std::cout << std::format("value < 0: {}\n", ordering == std::strong_ordering::less); std::cout << std::format("value > 0: {}\n", ordering == std::strong_ordering::greater); std::cout << std::format("value == 0: {}\n", ordering == std::strong_ordering::equal); } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_02A.cpp ================================================ // Using the named comparison functions import ; // Required when using operator <=> (even for fundamental types) import ; import ; int main() { std::cout << "Please enter a number: "; int value; std::cin >> value; std::strong_ordering ordering{ value <=> 0 }; std::cout << std::format("value < 0: {}\n", std::is_lt(ordering)); // is less than std::cout << std::format("value > 0: {}\n", std::is_gt(ordering)); // is greater than std::cout << std::format("value == 0: {}\n", std::is_eq(ordering)); // is equivalent } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_03.cpp ================================================ // Using an if statement import ; int main() { std::cout << "Enter an integer between 50 and 100: "; int value {}; std::cin >> value; if (value) std::cout << "You have entered a value that is different from zero." << std::endl; if (value < 50) std::cout << "The value is invalid - it is less than 50." << std::endl; if (value > 100) std::cout << "The value is invalid - it is greater than 100." << std::endl; std::cout << "You entered " << value << std::endl; } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_04.cpp ================================================ // Using a nested if import ; int main() { char letter {}; // Store input here std::cout << "Enter a letter: "; // Prompt for the input std::cin >> letter; if (letter >= 'A') { // letter is 'A' or larger if (letter <= 'Z') { // letter is 'Z' or smaller std::cout << "You entered an uppercase letter." << std::endl; return 0; } } if (letter >= 'a') // Test for 'a' or larger if (letter <= 'z') { // letter is >= 'a' and <= 'z' std::cout << "You entered a lowercase letter." << std::endl; return 0; } std::cout << "You did not enter a letter." << std::endl; } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_04A.cpp ================================================ // Using the std::isupper() / islower() character classification functions import ; #include int main() { char letter {}; // Store input here std::cout << "Enter a letter: "; // Prompt for the input std::cin >> letter; if (std::isupper(letter)) { std::cout << "You entered an uppercase letter." << std::endl; return 0; } if (std::islower(letter)) { std::cout << "You entered a lowercase letter." << std::endl; return 0; } std::cout << "You did not enter a letter." << std::endl; } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_05.cpp ================================================ // Using the if-else statement import ; int main() { long number {}; // Stores input std::cout << "Enter an integer less than 2 billion: "; std::cin >> number; if (number % 2) // Test remainder after division by 2 { // Here if remainder is 1 std::cout << "Your number is odd." << std::endl; } else { // Here if remainder is 0 std::cout << "Your number is even." << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_06.cpp ================================================ // Combining logical operators for loan approval import ; int main() { int age {}; // Age of the prospective borrower int income {}; // Income of the prospective borrower int balance {}; // Current bank balance // Get the basic data for assessing the loan std::cout << "Please enter your age in years: "; std::cin >> age; std::cout << "Please enter your annual income in dollars: "; std::cin >> income; std::cout << "What is your current account balance in dollars: "; std::cin >> balance; // We only lend to people who are at least 21 years of age, // who make over $25,000 per year, // or have over $100,000 in their account, or both. if (age >= 21 && (income > 25'000 || balance > 100'000)) { // OK, you are good for the loan - but how much? // This will be the lesser of twice income and half balance int loan {}; // Stores maximum loan amount if (2*income < balance/2) { loan = 2*income; } else { loan = balance/2; } std::cout << "\nYou can borrow up to $" << loan << std::endl; } else // No loan for you... { std::cout << "\nUnfortunately, you don't qualify for a loan." << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_07.cpp ================================================ // Using the conditional operator to select output. import ; import ; int main() { int mice {}; // Count of all mice int brown {}; // Count of brown mice int white {}; // Count of white mice std::cout << "How many brown mice do you have? "; std::cin >> brown; std::cout << "How many white mice do you have? "; std::cin >> white; mice = brown + white; std::cout << std::format("You have {} {} in total.\n", mice, mice == 1 ? "mouse" : "mice"); } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_08.cpp ================================================ // Using the switch statement import ; int main() { std::cout << "Your electronic recipe book is at your service.\n" << "You can choose from the following delicious dishes:\n" << "1. Boiled eggs\n" << "2. Fried eggs\n" << "3. Scrambled eggs\n" << "4. Coddled eggs\n\n" << "Enter your selection number: "; int choice {}; // Stores selection value std::cin >> choice; switch (choice) { case 1: std::cout << "Boil some eggs." << std::endl; break; case 2: std::cout << "Fry some eggs." << std::endl; break; case 3: std::cout << "Scramble some eggs." << std::endl; break; case 4: std::cout << "Coddle some eggs." << std::endl; break; default: std::cout << "You entered a wrong number - try raw eggs." << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_09.cpp ================================================ // Multiple case actions import ; #include int main() { char letter {}; std::cout << "Enter a letter: "; std::cin >> letter; if (std::isalpha(letter)) { switch (std::tolower(letter)) { case 'a': case 'e': case 'i': case 'o': case 'u': std::cout << "You entered a vowel." << std::endl; break; default: std::cout << "You entered a consonant." << std::endl; break; } } else { std::cout << "You did not enter a letter." << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 04/Ex4_09A.cpp ================================================ // Using a return statement to exit a switch statement import ; #include int main() { char letter {}; std::cout << "Enter a letter: "; std::cin >> letter; if (std::isalpha(letter)) { switch (std::tolower(letter)) { case 'a': case 'e': case 'i': case 'o': case 'u': std::cout << "You entered a vowel." << std::endl; return 0; // Ends the program } // We did not exit main() in the above switch, so letter is not a vowel: std::cout << "You entered a consonant." << std::endl; } else { std::cout << "You did not enter a letter." << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_01.cpp ================================================ // Using a for loop with an array import ; int main() { const unsigned size {6}; // Array size unsigned height[size] {26, 37, 47, 55, 62, 75}; // An array of heights unsigned total {}; // Sum of heights for (size_t i {}; i < size; ++i) { total += height[i]; } const unsigned average {total/size}; // Calculate average height std::cout << "The average height is " << average << std::endl; unsigned count {}; for (size_t i {}; i < size; ++i) { if (height[i] < average) ++count; } std::cout << count << " people are below average height." << std::endl; } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_02.cpp ================================================ // Obtaining the number of array elements import ; import ; // for std::size() int main() { int values[] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; std::cout << "There are " << std::size(values) << " elements in the array.\n"; int sum {}; const size_t old_school_size{ sizeof(values) / sizeof(values[0]) }; for (size_t i {}; i < old_school_size; ++i) { sum += values[i]; } std::cout << "The sum of the array elements is " << sum << std::endl; } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_03.cpp ================================================ // Floating-point control in a for loop import ; import ; import ; int main() { const size_t values_per_line {3}; // Outputs per line size_t values_current_line {}; // Number of outputs on current line for (double radius {0.2}; radius <= 3.0; radius += 0.2) { const auto area{ std::numbers::pi * radius * radius }; std::cout << std::format("radius = {:4.2f}, area = {:5.2f}; ", radius, area); if (++values_current_line == values_per_line) // When enough values written... { std::cout << std::endl; // ...start a new line... values_current_line = 0; // ...and reset the line counter } } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_03A.cpp ================================================ // Floating-point control in a for loop import ; import ; import ; int main() { const size_t values_per_line {3}; // Outputs per line size_t values_current_line {}; // Number of outputs on current line for (double radius {0.2}; radius < 3.0 + 0.001; radius += 0.2) { const auto area{ std::numbers::pi * radius * radius }; std::cout << std::format("radius = {:4.2f}, area = {:5.2f}; ", radius, area); if (++values_current_line == values_per_line) // When enough values written... { std::cout << std::endl; // ...start a new line... values_current_line = 0; // ...and reset the line counter } } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_04.cpp ================================================ // Multiple initializations in a loop expression import ; import ; int main() { unsigned int limit {}; std::cout << "This program calculates n! and the sum of the integers " << "up to n for values 1 to limit.\n"; std::cout << "What upper limit for n would you like? "; std::cin >> limit; // The format string for all rows of the table const auto table_format{ "{:>8} {:>8} {:>20}\n" }; // Output column headings std::cout << std::format(table_format, "integer", "sum", "factorial"); for (unsigned long long n {1}, sum {}, factorial {1}; n <= limit; ++n) { sum += n; // Accumulate sum to current n factorial *= n; // Calculate n! for current n std::cout << std::format(table_format, n, sum, factorial); } } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_04A.cpp ================================================ // Multiple calculations in a loop expression's third control expression // by using the comma operator import ; import ; int main() { unsigned int limit {}; std::cout << "This program calculates n! and the sum of the integers " << "up to n for values 1 to limit.\n"; std::cout << "What upper limit for n would you like? "; std::cin >> limit; // The format string for all rows of the table const auto table_format{ "{:>8} {:>8} {:>20}\n" }; // Output column headings std::cout << std::format(table_format, "integer", "sum", "factorial"); for (unsigned long long n {1}, sum {1}, factorial {1}; n <= limit; ++n, sum += n, factorial *= n) { std::cout << std::format(table_format, n, sum, factorial); } } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_05.cpp ================================================ // Using a while loop to calculate the sum of integers from 1 to n and n! import ; import ; int main() { unsigned int limit {}; std::cout << "This program calculates n! and the sum of the integers " << "up to n for values 1 to limit.\n"; std::cout << "What upper limit for n would you like? "; std::cin >> limit; // The format string for all rows of the table const auto table_format{ "{:>8} {:>8} {:>20}\n" }; // Output column headings std::cout << std::format(table_format, "integer", "sum", "factorial"); unsigned int n {}; unsigned int sum {}; unsigned long long factorial {1ULL}; while (++n <= limit) { sum += n; // Accumulate sum to current n factorial *= n; // Calculate n! for current n std::cout << std::format(table_format, n, sum, factorial); } } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_06.cpp ================================================ // Using a do-while loop to manage input import ; #include // For tolower() function int main() { char reply {}; // Stores response to prompt for input int count {}; // Counts the number of input values double temperature {}; // Stores an input value double total {}; // Stores the sum of all input values do { std::cout << "Enter a temperature reading: "; // Prompt for input std::cin >> temperature; // Read input value total += temperature; // Accumulate total of values ++count; // Increment count std::cout << "Do you want to enter another? (y/n): "; std::cin >> reply; // Get response } while (std::tolower(reply) == 'y'); std::cout << "The average temperature is " << total/count << std::endl; } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_07.cpp ================================================ // Generating multiplication tables using nested loops import ; import ; #include int main() { size_t table {}; // Table size const size_t table_min {2}; // Minimum table size - at least up to the 2-times const size_t table_max {12}; // Maximum table size char reply {}; // Response to prompt do { std::cout << std::format("What size table would you like ({} to {})? ", table_min, table_max); std::cin >> table; // Get the table size std::cout << std::endl; // Make sure table size is within the limits if (table < table_min || table > table_max) { std::cout << "Invalid table size entered. Program terminated." << std::endl; return 1; } // Create the top line of the table std::cout << std::format("{:>6}", '|'); for (size_t i {1}; i <= table; ++i) { std::cout << std::format(" {:3} |", i); } std::cout << std::endl; // Create the separator row for (size_t i {}; i <= table; ++i) { std::cout << "------"; } std::cout << std::endl; for (size_t i {1}; i <= table; ++i) { // Iterate over rows std::cout << std::format(" {:3} |", i); // Start the row // Output the values in a row for (size_t j {1}; j <= table; ++j) { std::cout << std::format(" {:3} |", i*j); // For each column } std::cout << std::endl; // End the row } // Check if another table is required std::cout << "\nDo you want another table (y or n)? "; std::cin >> reply; } while (std::tolower(reply) == 'y'); } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_07A.cpp ================================================ // Generating multiplication tables using nested loops // In this version an indefinite for loop is used, in combination with break statements. import ; import ; #include // for std::tolower() int main() { size_t table {}; // Table size const size_t table_min {2}; // Minimum table size - at least up to the 2-times const size_t table_max {12}; // Maximum table size char reply {}; // Response to prompt const size_t max_tries{ 3 }; // Max. number of times a user can try entering a table size do { for (size_t count{ 1 }; ; ++count) // Indefinite loop { std::cout << std::format("What size table would you like ({} to {})? ", table_min, table_max); std::cin >> table; // Get the table size // Make sure table size is within the limits if (table >= table_min && table <= table_max) { break; // Exit the input loop } else if (count < max_tries) { std::cout << "Invalid input - try again.\n"; } else { std::cout << "Invalid table size entered - yet again!\nSorry, only " << max_tries << " allowed - program terminated." << std::endl; return 1; } } // Create the top line of the table std::cout << std::format("{:>6}", '|'); for (size_t i {1}; i <= table; ++i) { std::cout << std::format(" {:3} |", i); } std::cout << std::endl; // Create the separator row for (size_t i {}; i <= table; ++i) { std::cout << "------"; } std::cout << std::endl; for (size_t i {1}; i <= table; ++i) { // Iterate over rows std::cout << std::format(" {:3} |", i); // Start the row // Output the values in a row for (size_t j {1}; j <= table; ++j) { std::cout << std::format(" {:3} |", i*j); // For each column } std::cout << std::endl; // End the row } // Check if another table is required std::cout << "\nDo you want another table (y or n)? "; std::cin >> reply; } while (std::tolower(reply) == 'y'); } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_08.cpp ================================================ // Using the continue statement to display ASCII character codes import ; import ; #include int main() { const auto header_format{ "{:^11}{:^11}{:^11}\n" }; // 3 cols., 11 wide, centered (^) const auto body_format{ "{0:^11}{0:^11X}{0:^11d}\n" }; // Print same argument three times std::cout << std::format(header_format, "Character", "Hexadecimal", "Decimal"); // Output 7-bit ASCII characters and corresponding codes char ch{}; do { if (!std::isprint(ch)) // If it's not printable... continue; // ...skip this iteration std::cout << std::format(body_format, ch); } while (ch++ < 127); } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_09.cpp ================================================ // Sorting an array in ascending sequence - using an indefinite while loop import ; import ; int main() { const size_t size {1000}; // Array size double x[size] {}; // Stores data to be sorted size_t count {}; // Number of values in array while (true) { double input {}; // Temporary store for a value std::cout << "Enter a non-zero value, or 0 to end: "; std::cin >> input; if (input == 0) break; x[count] = input; if (++count == size) { std::cout << "Sorry, I can only store " << size << " values.\n"; break; } } if (count == 0) { std::cout << "Nothing to sort..." << std::endl; return 0; } std::cout << "Starting sort..." << std::endl; while (true) { bool swapped{ false }; // Becomes true when not all values are in order for (size_t i {}; i < count - 1; ++i) { if (x[i] > x[i + 1]) // Out of order so swap them { const auto temp{ x[i] }; x[i] = x[i+1]; x[i + 1] = temp; swapped = true; } } if (!swapped) // If there were no swaps break; // ...all values are in order... } // ...otherwise, go round again. std::cout << "Your data in ascending sequence:\n"; const size_t perline {10}; // Number output per line size_t n {}; // Number on current line for (size_t i {}; i < count; ++i) { std::cout << std::format("{:8.1f}", x[i]); if (++n == perline) // When perline have been written... { std::cout << std::endl; // Start a new line and... n = 0; // ...reset count on this line } } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_10.cpp ================================================ // Classifying the letters in a C-style string import ; #include int main() { const int max_length {100}; // Array size char text[max_length] {}; // Array to hold input string std::cout << "Enter a line of text:" << std::endl; // Read a line of characters including spaces std::cin.getline(text, max_length); std::cout << "You entered:\n" << text << std::endl; size_t vowels {}; // Count of vowels size_t consonants {}; // Count of consonants for (int i {}; text[i] != '\0'; i++) { if (std::isalpha(text[i])) // If it is a letter... { switch (std::tolower(text[i])) { // ...check lowercase... case 'a': case 'e': case 'i': case 'o': case 'u': ++vowels; // ...it is a vowel break; default: ++consonants; // ...it is a consonant } } } std::cout << "Your input contained " << vowels << " vowels and " << consonants << " consonants." << std::endl; } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_11.cpp ================================================ // Working with strings in an array import ; import ; // for std::size() int main() { const size_t max_length{ 80 }; // Maximum string length (including \0) char stars[][max_length]{ "Fatty Arbuckle", "Clara Bow", "Lassie", "Slim Pickens", "Boris Karloff", "Mae West", "Oliver Hardy", "Greta Garbo" }; size_t choice{}; std::cout << "Pick a lucky star! Enter a number between 1 and " << std::size(stars) << ": "; std::cin >> choice; if (choice >= 1 && choice <= std::size(stars)) { std::cout << "Your lucky star is " << stars[choice - 1] << std::endl; } else { std::cout << "Sorry, you haven't got a lucky star." << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_12.cpp ================================================ // Allocating an array at runtime // This example does not work with some compilers (such as Visual C++) // because dynamic arrays is not standard C++ (it is valid C though). import ; import ; #ifdef _MSC_VER // See Appendix A for an explanation of preprocessing macros #error Visual Studio does not support variable length arrays (not standard C++) #endif int main() { size_t count {}; std::cout << "How many heights will you enter? "; std::cin >> count; int height[count]; // Create the array of count elements // Read the heights size_t entered {}; while (entered < count) { std::cout <<"Enter a height (in inches): "; std::cin >> height[entered]; if (height[entered] > 0) // Make sure value is positive { ++entered; } else { std::cout << "A height must be positive - try again.\n"; } } // Calculate the sum of the heights unsigned int total {}; for (size_t i {}; i < count; ++i) { total += height[i]; } std::cout << std::format("The average height is {:.1f}\n", static_cast(total) / count); } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_12A.cpp ================================================ // Allocating an array at runtime (for loop merged into preceding while loop) // This example does not work with some compilers (such as Visual C++) // because dynamic arrays is not standard C++ (it is valid C though). import ; import ; #ifdef _MSC_VER // See Appendix A for an explanation of preprocessing macros #error Visual Studio does not support variable length arrays (not standard C++) #endif int main() { size_t count {}; std::cout << "How many heights will you enter? "; std::cin >> count; int height[count]; // Create the array of count elements // Read the heights unsigned int total {}; size_t entered {}; while (entered < count) { std::cout << "Enter a height (in inches): "; std::cin >> height[entered]; if (height[entered] > 0) // Make sure value is positive { total += height[entered++]; } else { std::cout << "A height must be positive - try again.\n"; } } std::cout << std::format("The average height is {:.1f}\n", static_cast(total) / count); } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_13.cpp ================================================ // Comparing array<> objects and plain arrays import ; import ; int main() { { std::cout << "First we try out the comparison operators for std::array<> objects:" << std::endl; std::array these {1.0, 2.0, 3.0, 4.0}; std::array those {1.0, 2.0, 3.0, 4.0}; std::array them {1.0, 1.0, 5.0, 5.0}; if (these == those) std::cout << "these and those are equal." << std::endl; if (those != them) std::cout << "those and them are not equal." << std::endl; if (those > them) std::cout << "those are greater than them." << std::endl; if (them < those) std::cout << "them are less than those." << std::endl; } std::cout << std::endl; { std::cout << "Next we repeat exactly the same comparisons with plain C++ arrays:" << std::endl; double these[4] {1.0, 2.0, 3.0, 4.0}; double those[4] {1.0, 2.0, 3.0, 4.0}; double them[4] {1.0, 1.0, 5.0, 5.0}; if (these == those) std::cout << "these and those are equal." << std::endl; if (those != them) std::cout << "those and them are not equal." << std::endl; if (those > them) std::cout << "those are greater than them." << std::endl; if (them < those) std::cout << "them are less than those." << std::endl; } /* The explanation of why this does not work as expected with plain arrays follows in Chapter 6 */ } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_14.cpp ================================================ // Using array to create Body Mass Index (BMI) table // BMI = weight/(height*height) // weight in kilograms, height in meters import ; import ; import ; // For array int main() { const unsigned min_wt {100}; // Minimum weight in table (in pounds) const unsigned max_wt {250}; // Maximum weight in table const unsigned wt_step {10}; const size_t wt_count {1 + (max_wt - min_wt) / wt_step}; const unsigned min_ht {48}; // Minimum height in table (inches) const unsigned max_ht {84}; // Maximum height in table const unsigned ht_step {2}; const size_t ht_count { 1 + (max_ht - min_ht) / ht_step }; const double lbs_per_kg {2.2}; // Pounds per kilogram const double ins_per_m {39.37}; // Inches per meter std::array weight_lbs {}; std::array height_ins {}; // Create weights from 100lbs in steps of 10lbs for (unsigned i{}, w{ min_wt }; i < wt_count; w += wt_step, ++i) { weight_lbs[i] = w; } // Create heights from 48 inches in steps of 2 inches for (unsigned i{}, h{ min_ht }; h <= max_ht; h += ht_step) { height_ins.at(i++) = h; } // Output table headings std::cout << std::format("{:>8}", '|'); for (auto w : weight_lbs) std::cout << std::format("{:^6}|", w); std::cout << std::endl; // Output line below headings for (unsigned i{1}; i < wt_count; ++i) std::cout << "--------"; std::cout << std::endl; const unsigned int inches_per_foot {12U}; for (auto h : height_ins) { const unsigned feet{ h / inches_per_foot }; const unsigned inches{ h % inches_per_foot }; std::cout << std::format("{:2}'{:2}\" |", feet, inches); const double h_m{ h / ins_per_m }; // Height in meter for (auto w : weight_lbs) { const double w_kg = w / lbs_per_kg; // Weight in kilogram const double bmi = w_kg / (h_m * h_m); std::cout << std::format(" {:2.1f} |", bmi); } std::cout << std::endl; } // Output line below table for (size_t i {1}; i < wt_count; ++i) std::cout << "--------"; std::cout << "\nBMI from 18.5 to 24.9 is normal" << std::endl; } ================================================ FILE: Examples/Modules/Chapter 05/Ex5_15.cpp ================================================ // Sorting an array in ascending sequence - using a vector container import ; import ; import ; int main() { std::vector x; // Stores data to be sorted while (true) { double input {}; // Temporary store for a value std::cout << "Enter a non-zero value, or 0 to end: "; std::cin >> input; if (input == 0) break; x.push_back(input); } if (x.empty()) { std::cout << "Nothing to sort..." << std::endl; return 0; } std::cout << "Starting sort." << std::endl; while (true) { bool swapped{ false }; // Becomes true when not all values are in order for (size_t i {}; i < x.size() - 1; ++i) { if (x[i] > x[i + 1]) // Out of order so swap them { const auto temp{ x[i] }; x[i] = x[i+1]; x[i + 1] = temp; swapped = true; } } if (!swapped) // If there were no swaps break; // ...all values are in order... } // ...otherwise, go round again. std::cout << "Your data in ascending sequence:\n"; const size_t perline {10}; // Number output per line size_t n {}; // Number on current line for (size_t i {}; i < x.size(); ++i) { std::cout << std::format("{:8.1f}", x[i]); if (++n == perline) // When perline have been written... { std::cout << std::endl; // Start a new line and... n = 0; // ...reset count on this line } } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 06/Ex6_01.cpp ================================================ // The size of pointers import ; int main() { // Print out the size (in number of bytes) of some data types // and the corresponding pointer types: std::cout << sizeof(double) << " > " << sizeof(char16_t) << std::endl; std::cout << sizeof(double*) << " == " << sizeof(char16_t*) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 06/Ex6_02.cpp ================================================ // Dereferencing pointers // Calculates the purchase price for a given quantity of items import ; import ; int main() { int unit_price {295}; // Item unit price in cents int count {}; // Number of items ordered int discount_threshold {25}; // Quantity threshold for discount double discount {0.07}; // Discount for quantities over discount_threshold int* pcount {&count}; // Pointer to count std::cout << "Enter the number of items you want: "; std::cin >> *pcount; std::cout << std::format("The unit price is ${:.2f}\n", unit_price / 100.0); // Calculate gross price int* punit_price{ &unit_price }; // Pointer to unit_price int price{ *pcount * *punit_price }; // Gross price via pointers auto* pprice {&price}; // Pointer to gross price // Calculate net price in US$ double net_price{}; double* pnet_price {nullptr}; pnet_price = &net_price; if (*pcount > discount_threshold) { std::cout << std::format("You qualify for a discount of {:.0f} percent.\n", discount * 100); *pnet_price = price*(1 - discount) / 100; } else { net_price = *pprice / 100; } std::cout << std::format("The net price for {} items is ${:.2f}\n", *pcount, net_price); } ================================================ FILE: Examples/Modules/Chapter 06/Ex6_03.cpp ================================================ // Initializing pointers with strings import ; int main() { const char* pstar1 {"Fatty Arbuckle"}; const char* pstar2 {"Clara Bow"}; const char* pstar3 {"Lassie"}; const char* pstar4 {"Slim Pickens"}; const char* pstar5 {"Boris Karloff"}; const char* pstar6 {"Mae West"}; const char* pstar7 {"Oliver Hardy"}; const char* pstar8 {"Greta Garbo"}; const char* pstr {"Your lucky star is "}; std::cout << "Pick a lucky star! Enter a number between 1 and 8: "; size_t choice {}; std::cin >> choice; switch (choice) { case 1: std::cout << pstr << pstar1 << std::endl; break; case 2: std::cout << pstr << pstar2 << std::endl; break; case 3: std::cout << pstr << pstar3 << std::endl; break; case 4: std::cout << pstr << pstar4 << std::endl; break; case 5: std::cout << pstr << pstar5 << std::endl; break; case 6: std::cout << pstr << pstar6 << std::endl; break; case 7: std::cout << pstr << pstar7 << std::endl; break; case 8: std::cout << pstr << pstar8 << std::endl; break; default: std::cout << "Sorry, you haven't got a lucky star." << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 06/Ex6_04.cpp ================================================ // Using an array of pointers import ; import ; // for std::size() int main() { const char* pstars[] { "Fatty Arbuckle", "Clara Bow", "Lassie", "Slim Pickens", "Boris Karloff", "Mae West", "Oliver Hardy", "Greta Garbo" }; std::cout << "Pick a lucky star! Enter a number between 1 and " << std::size(pstars) << ": "; size_t choice {}; std::cin >> choice; if (choice >= 1 && choice <= std::size(pstars)) { std::cout << "Your lucky star is " << pstars[choice - 1] << std::endl; } else { std::cout << "Sorry, you haven't got a lucky star." << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 06/Ex6_05.cpp ================================================ // Calculating primes using pointer notation import ; import ; int main() { const size_t max {100}; // Number of primes required long primes[max] {2L}; // First prime defined size_t count {1}; // Count of primes found so far long trial {3L}; // Candidate prime while (count < max) { bool isprime {true}; // Indicates when a prime is found // Try dividing the candidate by all the primes we have for (size_t i {}; i < count && isprime; ++i) { isprime = trial % *(primes + i) > 0; // False for exact division } if (isprime) { // We got one... *(primes + count++) = trial; // ...so save it in primes array } trial += 2; // Next value for checking } // Output primes 10 to a line std::cout << "The first " << max << " primes are:" << std::endl; for (size_t i{}; i < max; ++i) { std::cout << std::format("{:7}", *(primes + i)); if ((i+1) % 10 == 0) // Newline after every 10th prime std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 06/Ex6_06.cpp ================================================ // Calculating primes using dynamic memory allocation import ; import ; #include // For square root function (std::sqrt()) int main() { size_t max {}; // Number of primes required std::cout << "How many primes would you like? "; std::cin >> max; // Read number required if (max == 0) return 0; // Zero primes: do nothing auto* primes {new unsigned[max]}; // Allocate memory for max primes size_t count {1}; // Count of primes found primes[0] = 2; // Insert first seed prime unsigned trial {3}; // Initial candidate prime while (count < max) { bool isprime {true}; // Indicates when a prime is found const auto limit = static_cast(std::sqrt(trial)); for (size_t i {}; primes[i] <= limit && isprime; ++i) { isprime = trial % primes[i] > 0; // False for exact division } if (isprime) // We got one... primes[count++] = trial; // ...so save it in primes array trial += 2; // Next value for checking } // Output primes 10 to a line for (size_t i{}; i < max; ++i) { std::cout << std::format("{:10}", primes[i]); if ((i + 1) % 10 == 0) // After every 10th prime... std::cout << std::endl; // ...start a new line } std::cout << std::endl; delete[] primes; // Free up memory... primes = nullptr; // ... and reset the pointer } ================================================ FILE: Examples/Modules/Chapter 06/Ex6_07.cpp ================================================ // Using smart pointers import ; import ; import ; // For smart pointers import ; // For std::vector<> container #include // For std::toupper() int main() { std::vector>> records; // Temperature records by days size_t day{ 1 }; // Day number while (true) // Collect temperatures by day { // Vector to store current day's temperatures created in the free store auto day_records{ std::make_shared>() }; records.push_back(day_records); // Save pointer in records vector std::cout << "Enter the temperatures for day " << day++ << " separated by spaces. Enter 1000 to end:\n"; while (true) { // Get temperatures for current day double t{}; // A temperature std::cin >> t; if (t == 1000.0) break; day_records->push_back(t); } std::cout << "Enter another day's temperatures (Y or N)? "; char answer{}; std::cin >> answer; if (std::toupper(answer) != 'Y') break; } day = 1; for (auto record : records) { double total{}; size_t count{}; std::cout << std::format("\nTemperatures for day {}:\n", day++); for (auto temp : *record) { total += temp; std::cout << std::format("{:6.2f}", temp); if (++count % 5 == 0) std::cout << std::endl; } std::cout << std::format("\nAverage temperature: {:.2f}", total / count) << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_01.cpp ================================================ // Calculating powers import ; import ; // Function to calculate x to the power n double power(double x, int n) { double result{ 1.0 }; if (n >= 0) { for (int i{ 1 }; i <= n; ++i) result *= x; } else // n < 0 { for (int i{ 1 }; i <= -n; ++i) result /= x; } return result; } int main() { // Calculate powers of 8 from -3 to +3 for (int i{ -3 }; i <= 3; ++i) std::cout << std::format("{:10g}", power(8.0, i)); std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_02.cpp ================================================ // Calculating powers - rearranged import ; import ; //double power(double x, int n); // Function prototype - uncomment for successful compilation int main() { // Calculate powers of 8 from -3 to +3 for (int i{ -3 }; i <= 3; ++i) std::cout << std::format("{:10}", power(8.0, i)); std::cout << std::endl; } // Function to calculate x to the power n double power(double x, int n) { double result{ 1.0 }; if (n >= 0) { for (int i{ 1 }; i <= n; ++i) result *= x; } else // n < 0 { for (int i{ 1 }; i <= -n; ++i) result /= x; } return result; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_03.cpp ================================================ // Failing to modify the original value of a function argument import ; double changeIt(double value_to_be_changed); // Function prototype int main() { double it {5.0}; double result {changeIt(it)}; std::cout << "After function execution, it = " << it << "\nResult returned is " << result << std::endl; } // Function that attempts to modify an argument and return it double changeIt(double it) { it += 10.0; // This modifies the copy std::cout << "Within function, it = " << it << std::endl; return it; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_04.cpp ================================================ // Modifying the value of a caller variable import ; double changeIt(double* pointer_to_it); // Function prototype int main() { double it {5.0}; double result {changeIt(&it)}; // Now we pass the address std::cout << "After function execution, it = " << it << "\nResult returned is " << result << std::endl; } // Function to modify an argument and return it double changeIt(double* pit) { *pit += 10.0; // This modifies the original double std::cout << "Within function, *pit = " << *pit << std::endl; return *pit; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_05.cpp ================================================ // Passing an array to a function import ; import ; // For std::size() double average(double array[], size_t count); // Function prototype int main() { double values[] {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}; std::cout << "Average = " << average(values, std::size(values)) << std::endl; } // Function to compute an average double average(double array[], size_t count) { double sum {}; // Accumulate total in here for (size_t i {}; i < count; ++i) sum += array[i]; // Sum array elements return sum / count; // Return average } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_05A.cpp ================================================ // Passing an array to a function - false expectations // Note: with main() as defined in this file, // this program will likely either crash or produce garbage output! import ; double average10(double array[10]); // Function prototype int main() { // double values[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; double values[] { 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(values) << std::endl; } // Function to compute an average double average10(double array[10]) /* The [10] does not mean what you might expect! */ { double sum{}; // Accumulate total in here for (size_t i{} ; i < 10; ++i) sum += array[i]; // Sum array elements return sum / 10; // Return average } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_06.cpp ================================================ // Passing a two-dimensional array to a function import ; import ; // For std::size() double yield(const double values[][4], size_t n); int main() { double beans[3][4] { { 1.0, 2.0, 3.0, 4.0}, { 5.0, 6.0, 7.0, 8.0}, { 9.0, 10.0, 11.0, 12.0} }; std::cout << "Yield = " << yield(beans, std::size(beans)) << std::endl; } // Function to compute total yield double yield(const double array[][4], size_t size) { double sum {}; for (size_t i {}; i < size; ++i) // Loop through rows { for (size_t j {}; j < std::size(array[i]); ++j) // Loop through elements in a row { sum += array[i][j]; } } return sum; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_06A.cpp ================================================ // Passing a two-dimensional array to a function (range-based for loop) import ; import ; // For std::size() double yield(const double values[][4], size_t n); int main() { double beans[3][4]{ { 1.0, 2.0, 3.0, 4.0}, { 5.0, 6.0, 7.0, 8.0}, { 9.0, 10.0, 11.0, 12.0} }; std::cout << "Yield = " << yield(beans, std::size(beans)) << std::endl; } // Function to compute total yield double yield(const double array[][4], size_t size) { double sum{}; for (size_t i{}; i < size; ++i) // Loop through rows { for (double val : array[i]) // Loop through elements in a row { sum += val; } } return sum; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_07.cpp ================================================ // Modifying the value of a caller variable references vs pointers import ; void change_it_by_pointer(double* reference_to_it); // Pass pointer (by value) void change_it_by_reference(double& reference_to_it); // Pass by reference int main() { double it {5.0}; change_it_by_pointer(&it); // Now we pass the address std::cout << "After first function execution, it = " << it << std::endl; change_it_by_reference(it); // Now we pass a reference, not the value! std::cout << "After second function execution, it = " << it << std::endl; } void change_it_by_pointer(double* pit) { *pit += 10.0; // This modifies the original double } void change_it_by_reference(double& pit) { pit += 10.0; // This modifies the original double as well! } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_08.cpp ================================================ // Using a reference parameter import ; import ; import ; import ; using std::string; using std::vector; void find_words(vector& words, const string& str, const string& separators); void list_words(const vector& words); int main() { std::string text; // The string to be searched std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); const std::string separators {" ,;:.\"!?'\n"}; // Word delimiters std::vector words; // Words found find_words(words, text, separators); list_words(words); } void find_words(vector& words, const string& text, const string& separators) { size_t start {text.find_first_not_of(separators)}; // First word start index while (start != string::npos) // Find the words { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of word if (end == string::npos) // Found a separator? end = text.length(); // No, so set to end of text words.push_back(text.substr(start, end - start)); // Store the word start = text.find_first_not_of(separators, end + 1); // Find 1st character of next word } } void list_words(const vector& words) { std::cout << "Your string contains the following " << words.size() << " words:\n"; size_t count {}; // Number of outputted words for (const auto& word : words) { std::cout << std::format("{:>15}", word); if (!(++count % 5)) std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_09A.cpp ================================================ // Passing an array to a function - pass by reference // Note: with main() as defined in this file, this program will not compile... import ; double average10(const double (&)[10]); // Function prototype int main() { // Use 10 values to make example compile... // double values[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; double values[] { 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(values) << std::endl; } // Function to compute an average double average10(const double (&array)[10]) /* Only arrays of length 10 can be passed! */ { double sum {}; // Accumulate total in here for (size_t i {}; i < 10; ++i) sum += array[i]; // Sum array elements return sum / 10; // Return average } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_09B.cpp ================================================ // Passing an array to a function - pass by reference improved import ; import ; // for std::size() double average10(const double (&)[10]); // Function prototype int main() { double values[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; // double values[] { 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(values) << std::endl; } // Function to compute an average double average10(const double (&array)[10]) { double sum {}; // Accumulate total in here for (double val : array) sum += val; // Sum array elements return sum / std::size(array); // Return average } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_09C.cpp ================================================ // Passing an array to a function - use std::array<> import ; import ; double average10(const std::array& array); // Function prototype int main() { std::array values{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; // std::array values{ 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(values) << std::endl; } // Function to compute an average double average10(const std::array& array) { double sum {}; // Accumulate total in here for (double val : array) sum += val; // Sum array elements return sum / array.size(); // Return average } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_10.cpp ================================================ // Implicit conversions of reference parameters import ; void double_it(double& it) { it *= 2; } void print_it(const double& it) { std::cout << it << std::endl; } int main() { double d{123}; double_it(d); print_it(d); int i{456}; // double_it(i); /* error, does not compile! */ print_it(i); } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_11.cpp ================================================ // Using multiple default parameter values import ; import ; import ; // The function prototype including defaults for parameters void show_data(const int data[], size_t count = 1, const std::string& title = "Data Values", size_t width = 10, size_t perLine = 5); int main() { int samples[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; int dataItem {-99}; show_data(&dataItem); dataItem = 13; show_data(&dataItem, 1, "Unlucky for some!"); show_data(samples, std::size(samples)); show_data(samples, std::size(samples), "Samples"); show_data(samples, std::size(samples), "Samples", 6); show_data(samples, std::size(samples), "Samples", 8, 4); } void show_data(const int data[], size_t count, const std::string& title, size_t width, size_t perLine) { std::cout << title << std::endl; // Display the title // Output the data values for (size_t i {}; i < count; ++i) { std::cout << std::format("{:{}}", data[i], width); // Display a data item if ((i+1) % perLine == 0) // Newline after perLine values std::cout << '\n'; } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_12.cpp ================================================ // Program that lists its command line arguments import ; int main(int argc, char* argv[]) { for (int i{}; i < argc; ++i) std::cout << argv[i] << std::endl; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_13.cpp ================================================ // Returning a pointer import ; import ; import ; import ; // for std::size() void show_data(const double data[], size_t count = 1, const std::string& title = "Data Values", size_t width = 10, size_t perLine = 5); const double* largest(const double data[], size_t count); const double* smallest(const double data[], size_t count); double* shift_range(double data[], size_t count, double delta); double* scale_range(double data[], size_t count, double divisor); double* normalize_range(double data[], size_t count); int main() { double samples[] { 11.0, 23.0, 13.0, 4.0, 57.0, 36.0, 317.0, 88.0, 9.0, 100.0, 121.0, 12.0 }; const size_t count{std::size(samples)}; // Number of samples show_data(samples, count, "Original Values"); // Output original values normalize_range(samples, count); // Normalize the values show_data(samples, count, "Normalized Values", 12); // Output normalized values } // Outputs an array of double values void show_data(const double data[], size_t count, const std::string& title, size_t width, size_t perLine) { std::cout << title << std::endl; // Display the title // Output the data values for (size_t i {}; i < count; ++i) { // Display a data item (uses a dynamic field width: see Chapter 7) std::cout << std::format("{:{}.6g}", data[i], width); if ((i + 1) % perLine == 0) // Newline after perLine values std::cout << '\n'; } std::cout << std::endl; } const double* smallest(const double data[], size_t count) { if (!count) return nullptr; // There is no smallest in an empty array size_t index_min {}; for (size_t i {1}; i < count; ++i) if (data[index_min] > data[i]) index_min = i; return &data[index_min]; } double* shift_range(double data[], size_t count, double delta) { for (size_t i {}; i < count; ++i) data[i] += delta; return data; } const double* largest(const double data[], size_t count) { if (!count) return nullptr; // There is no largest in an empty array size_t index_max {}; for (size_t i {1}; i < count; ++i) if (data[index_max] < data[i]) index_max = i; return &data[index_max]; } double* scale_range(double data[], size_t count, double divisor) { if (!divisor) return data; // Do nothing for a zero divisor for (size_t i{}; i < count; ++i) data[i] /= divisor; return data; } double* normalize_range(double data[], size_t count) { shift_range(data, count, -(*smallest(data, count))); return scale_range(data, count, *largest(data, count)); } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_14.cpp ================================================ // Overloading a function import ; import ; import ; // Function prototypes double largest(const double data[], size_t count); double largest(const std::vector& data); int largest(const std::vector& data); std::string largest(const std::vector& words); // int largest(const std::vector& words); /* Above function overload would not compile: overloaded functions must differ in more than just their return type! */ int main() { double array[] {1.5, 44.6, 13.7, 21.2, 6.7}; std::vector numbers {15, 44, 13, 21, 6, 8, 5, 2}; std::vector data{3.5, 5, 6, -1.2, 8.7, 6.4}; std::vector names {"Charles Dickens", "Emily Bronte", "Jane Austen", "Henry James", "Arthur Miller"}; std::cout << "The largest of array is " << largest(array, std::size(array)) << std::endl; std::cout << "The largest of numbers is " << largest(numbers) << std::endl; std::cout << "The largest of data is " << largest(data) << std::endl; std::cout << "The largest of names is " << largest(names) << std::endl; } // Finds the largest of an array of double values double largest(const double data[], size_t count) { double max{ data[0] }; for (size_t i{ 1 }; i < count; ++i) if (max < data[i]) max = data[i]; return max; } // Finds the largest of a vector of double values double largest(const std::vector& data) { double max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of int values int largest(const std::vector& data) { int max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of string objects std::string largest(const std::vector& words) { std::string max_word {words[0]}; for (const auto& word : words) if (max_word < word) max_word = word; return max_word; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_15.cpp ================================================ // Overloading a function with reference parameters import ; import ; double larger(double a, double b); // Non-reference parameters long& larger(long& a, long& b); // Reference parameters int main() { double a_double {1.5}, b_double {2.5}; std::cout << std::format("The larger of double values {} and {} is {}\n", a_double, b_double, larger(a_double, b_double)); int a_int {15}, b_int {25}; std::cout << std::format("The larger of int values {} and {} is {}\n", a_int, b_int, larger(static_cast(a_int), static_cast(b_int))); } // Returns the larger of two floating point values double larger(double a, double b) { std::cout << "double larger() called." << std::endl; return a > b ? a : b; } // Returns the larger of two long references long& larger(long& a, long& b) { std::cout << "long ref larger() called" << std::endl; return a > b ? a : b; } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_16.cpp ================================================ // Recursive version of function for x to the power n, n positive or negative import ; import ; double power(double x, int n); int main() { for (int i{ -3 }; i <= 3; ++i) // Calculate powers of 8 from -3 to +3 std::cout << std::format("{:10g}", power(8.0, i)); std::cout << std::endl; } // Recursive function to calculate x to the power n double power(double x, int n) { if (n == 0) return 1.0; else if (n > 0) return x * power(x, n - 1); else /* n < 0 */ return 1.0 / power(x, -n); } ================================================ FILE: Examples/Modules/Chapter 08/Ex8_17.cpp ================================================ // Sorting words recursively import ; import ; import ; import ; import ; using Words = std::vector>; void swap(Words& words, size_t first, size_t second); void sort(Words& words); void sort(Words& words, size_t start, size_t end); void extract_words(Words& words, const std::string& text, const std::string& separators); void show_words(const Words& words); size_t max_word_length(const Words& words); int main() { Words words; std::string text; // The string to be sorted const auto separators{" ,.!?\"\n"}; // Word delimiters // Read the string to be processed from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); extract_words(words, text, separators); if (words.empty()) { std::cout << "No words in text." << std::endl; return 0; } sort(words); // Sort the words show_words(words); // Output the words } void extract_words(Words& words, const std::string& text, const std::string& separators) { size_t start {text.find_first_not_of(separators)}; // Start index of first word while (start != std::string::npos) { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of a word if (end == std::string::npos) // Found a separator? end = text.length(); // Yes, so set to end of text words.push_back(std::make_shared(text.substr(start, end - start))); start = text.find_first_not_of(separators, end + 1); // Find start next word } } void swap(Words& words, size_t first, size_t second) { auto temp{words[first]}; words[first] = words[second]; words[second] = temp; } // Sort strings in ascending sequence void sort(Words& words) { if (!words.empty()) sort(words, 0, words.size() - 1); } void sort(Words& words, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle address to partition set swap(words, start, (start + end) / 2); // Swap middle address with start // Check words against chosen word size_t current {start}; for (size_t i {start + 1}; i <= end; i++) { if (*words[i] < *words[start]) // Is word less than chosen word? swap(words, ++current, i); // Yes, so swap to the left } swap(words, start, current); // Swap chosen and last swapped words if (current > start) sort(words, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(words, current + 1, end); // Sort right subset if exists } size_t max_word_length(const Words& words) { size_t max {}; for (auto& pword : words) if (max < pword->length()) max = pword->length(); return max; } void show_words(const Words& words) { const size_t field_width {max_word_length(words) + 1}; const size_t words_per_line {8}; std::cout << std::format("{:{}}", *words[0], field_width); // Output first word size_t words_in_line {}; // Number of words in current line for (size_t i {1}; i < words.size(); ++i) { // Output newline when initial letter changes or after 8 per line if ((*words[i])[0] != (*words[i - 1])[0] || ++words_in_line == words_per_line) { words_in_line = 0; std::cout << std::endl; } std::cout << std::format("{:{}}", *words[i], field_width); // Output a word } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 09/Ex9_01.cpp ================================================ // Working with std::optional<> import ; // std::optional<> is defined in the module import ; import ; std::optional find_last( const std::string& string, char to_find, std::optional start_index = std::nullopt); // or: ... start_index = {}); int main() { const auto string{ "Growing old is mandatory; growing up is optional." }; const std::optional found_a{ find_last(string, 'a') }; if (found_a) std::cout << "Found the last a at index " << *found_a << std::endl; const auto found_b{ find_last(string, 'b') }; if (found_b.has_value()) std::cout << "Found the last b at index " << found_b.value() << std::endl; // following line gives an error (cannot convert std::optional to size_t) // const size_t found_c{ find_last(string, 'c') }; const auto found_early_i{ find_last(string, 'i', 10) }; if (found_early_i != std::nullopt) std::cout << "Found an early i at index " << *found_early_i << std::endl; } std::optional find_last(const std::string& string, char to_find, std::optional start_index) { // code below will not work for empty strings if (string.empty()) return std::nullopt; // or: 'return std::optional{};' // or: 'return {};' // determine the starting index for the loop that follows: size_t index{ start_index.value_or(string.size() - 1) }; while (true) // never use while (index >= 0) here, as size_t is always >= 0! { if (string[index] == to_find) return index; if (index == 0) return std::nullopt; --index; } } ================================================ FILE: Examples/Modules/Chapter 09/Ex9_02.cpp ================================================ // Using std::string_view parameters import ; import ; import ; import ; import ; using std::string; using std::string_view; using std::vector; void find_words(vector& words, string_view str, string_view separators); void list_words(const vector& words); int main() { std::string text; // The string to be searched std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); const std::string separators{ " ,;:.\"!?'\n" }; // Word delimiters std::vector words; // Words found find_words(words, text, separators); list_words(words); } void find_words(vector& words, string_view text, string_view separators) { size_t start{ text.find_first_not_of(separators) }; // First word start index while (start != string_view::npos) // Find the words { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of word if (end == string_view::npos) // Found a separator? end = text.length(); // No, so set to end of text words.push_back(std::string{ text.substr(start, end - start) }); // Store the word // Or: words.emplace_back(text.substr(start, end - start)); // (in-place construction) start = text.find_first_not_of(separators, end + 1); // Find 1st character of next word } } void list_words(const vector& words) { std::cout << "Your string contains the following " << words.size() << " words:\n"; size_t count{}; // Number of outputted words for (const auto& word : words) { std::cout << std::format("{:>15}", word); if (!(++count % 5)) std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 09/Ex9_03.cpp ================================================ // Using std::span<> to reduce the number of overloads of largest() // Clearly the three resulting functions are still similar. // See Chapter 10 on how you can eliminate this duplication using function templates. import ; import ; import ; import ; import ; // Old function prototypes //double largest(const double data[], size_t count); //double largest(const std::vector& data); //int largest(const std::vector& data); //std::string largest(const std::vector& words); // New function prototypes // (these functions work for any sequential input, not just arrays or vectors) /* Caution: these signatures are not ideal yet: see Ex9_03A */ double largest(std::span data); int largest(std::span data); std::string largest(std::span words); int main() { double array[] {1.5, 44.6, 13.7, 21.2, 6.7}; std::vector numbers {15, 44, 13, 21, 6, 8, 5, 2}; std::vector data{3.5, 5.0, 6.0, -1.2, 8.7, 6.4}; std::array array_data{ 3.5, 5.0, 6.0, -1.2, 8.7, 6.4 }; // Throwing in an std::array for good measure std::vector names {"Charles Dickens", "Emily Bronte", "Jane Austen", "Henry James", "Arthur Miller"}; std::cout << "The largest of array is " << largest(array) << std::endl; std::cout << "The largest of numbers is " << largest(numbers) << std::endl; std::cout << "The largest of data is " << largest(data) << std::endl; std::cout << "The largest of array_data is (also) " << largest(array_data) << std::endl; std::cout << "The largest of names is " << largest(names) << std::endl; } // Finds the largest of a span of values double largest(std::span data) { double max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of int values int largest(std::span data) { int max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of string objects std::string largest(std::span words) { std::string max_word {words[0]}; for (const auto& word : words) if (max_word < word) max_word = word; return max_word; } ================================================ FILE: Examples/Modules/Chapter 09/Ex9_03A.cpp ================================================ // Using std::span to ensure largest() works for const inputs import ; import ; import ; import ; import ; // Old function prototypes //double largest(const double data[], size_t count); //double largest(const std::vector& data); //int largest(const std::vector& data); //std::string largest(const std::vector& words); // New function prototypes // (these functions work for any sequential input, not just arrays or vectors) double largest(std::span data); int largest(std::span data); std::string largest(std::span words); int main() { const double array[] {1.5, 44.6, 13.7, 21.2, 6.7}; const std::vector numbers {15, 44, 13, 21, 6, 8, 5, 2}; const std::vector data{3.5, 5.0, 6.0, -1.2, 8.7, 6.4}; const std::array array_data{ 3.5, 5.0, 6.0, -1.2, 8.7, 6.4 }; // Throwing in an std::array for good measure const std::vector names {"Charles Dickens", "Emily Bronte", "Jane Austen", "Henry James", "Arthur Miller"}; std::cout << "The largest of array is " << largest(array) << std::endl; std::cout << "The largest of numbers is " << largest(numbers) << std::endl; std::cout << "The largest of data is " << largest(data) << std::endl; std::cout << "The largest of array_data is (also) " << largest(array_data) << std::endl; std::cout << "The largest of names is " << largest(names) << std::endl; } // Finds the largest of a span of values double largest(std::span data) { double max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of int values int largest(std::span data) { int max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of string objects std::string largest(std::span words) { std::string max_word {words[0]}; for (const auto& word : words) if (max_word < word) max_word = word; return max_word; } ================================================ FILE: Examples/Modules/Chapter 10/Ex10_01.cpp ================================================ // Using a function template import ; import ; import ; template T larger(T a, T b); // Function template prototype int main() { std::cout << "Larger of 1.5 and 2.5 is " << larger(1.5, 2.5) << std::endl; std::cout << "Larger of 3.5 and 4.5 is " << larger(3.5, 4.5) << std::endl; int big_int {17011983}, small_int {10}; std::cout << std::format("Larger of {} and {} is {}\n", big_int, small_int, larger(big_int, small_int)); std::string a_string {"A"}, z_string {"Z"}; std::cout << std::format(R"(Larger of "{}" and "{}" is "{}")", a_string, z_string, larger(a_string, z_string)) << std::endl; } // Template for functions to return the larger of two values template T larger(T a, T b) { return a > b ? a : b; } ================================================ FILE: Examples/Modules/Chapter 10/Ex10_02.cpp ================================================ // Overloading function templates import ; import ; import ; import ; template T larger(T a, T b); // Function template prototype template T* larger(T*, T*); template const T* larger(const std::vector& data); int main() { int big_int {17011983}, small_int {10}; std::cout << std::format("Larger of {} and {} is {}", big_int, small_int, larger(big_int, small_int)) << std::endl; std::cout << std::format("Larger of {} and {} is {}", big_int, small_int, *larger(&big_int, &small_int)) << std::endl; std::vector data {-1.4, 7.3, -100.0, 54.1, 16.3}; std::cout << "The largest value in data is " << *larger(data) << std::endl; std::vector words {"The", "higher", "the", "fewer"}; std::cout << std::format(R"(The largest word in words is "{}")", *larger(words)) << std::endl; } // Template for functions to return the larger of two values template T larger(T a, T b) { return a > b ? a : b; } template T* larger(T* a, T* b) { return *a > * b ? a : b; } template const T* larger(const std::vector& data) { const T* result {}; // The largest of an empty vector is nullptr for (auto& value : data) if (!result || value > *result) result = &value; return result; } ================================================ FILE: Examples/Modules/Chapter 10/Ex10_03.cpp ================================================ // Using return type deduction with templates import ; import ; // Template for functions to return the larger of two values // Supports implicit converion of differently-typed arguments template auto larger(const T1& a, const T2& b) { return a > b ? a : b; } int main() { int small_int {10}; std::cout << "Larger of " << small_int << " and 9.6 is " << larger(small_int, 9.6) << std::endl; // deduced return type: double std::string a_string {"A"}; std::cout << "Larger of \"" << a_string << "\" and \"Z\" is \"" << larger(a_string, "Z") << '"' << std::endl; // deduced return type: std::string } ================================================ FILE: Examples/Modules/Chapter 10/Ex10_03A.cpp ================================================ // Using return type deduction with templates (decltype(auto) instead of auto) import ; import ; import ; // Template for functions to return the larger of two values // Supports implicit converion of differently-typed arguments template decltype(auto) larger(const T1& a, const T2& b) { return a > b ? a : b; } int main() { const int small_int {10}; std::cout << "Larger of " << small_int << " and 9.6 is " << larger(small_int, 9.6) << std::endl; // deduced return type: double const std::string a_string {"A"}; std::cout << "Larger of \"" << a_string << "\" and \"Z\" is \"" << larger(a_string, "Z") << '"' << std::endl; // deduced return type: std::string const std::vector v1{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; const std::vector v2{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 11 }; std::cout << "The larger of our two vectors ends with " << larger(v1, v2).back(); } ================================================ FILE: Examples/Modules/Chapter 10/Ex10_04.cpp ================================================ // Defining templates for functions that accept fixed-size arrays import ; template T average(const T(&array)[N]); int main() { double doubles[2]{ 1.0, 2.0 }; std::cout << average(doubles) << std::endl; double moreDoubles[]{ 1.0, 2.0, 3.0, 4.0 }; std::cout << average(moreDoubles) << std::endl; // double* pointer = doubles; // std::cout << average(pointer) << std::endl; /* will not compile */ std::cout << average({ 1.0, 2.0, 3.0, 4.0 }) << std::endl; int ints[] = { 1, 2, 3, 4 }; std::cout << average(ints) << std::endl; } template T average(const T(&array)[N]) { T sum{}; // Accumulate total in here for (size_t i{}; i < N; ++i) sum += array[i]; // Sum array elements return sum / N; // Return average } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_01/Ex11_01.cpp ================================================ // Consuming your own module import ; import ; import math; int main() { std::cout << "Lambda squared: " << square(lambda) << std::endl; int number; std::cout << "\nPlease enter an odd number: "; std::cin >> number; std::cout << std::endl; // if (isOdd(number)) /* Error: identifier not found: 'isOdd' */ // std::cout << "Well done!" << std::endl; switch (getOddity(number)) { using enum Oddity; case Odd: std::cout << "Well done! And remember: you have to be odd to be number one!"; break; case Even: std::cout << std::format("Odd, {} seems to be even?", number); break; } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_01/math.cppm ================================================ export module math; export auto square(const auto& x) { return x * x; } // An abbreviated function template export const double lambda = 1.303577269034296391257; // Conway's constant export enum class Oddity { Even, Odd }; bool isOdd(int x) { return x % 2 != 0; } // Module-local function (not exported) export auto getOddity(int x) { return isOdd(x) ? Oddity::Odd : Oddity::Even; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_01A/Ex11_01A.cpp ================================================ // Exporting multiple entities at once import ; import ; import math; int main() { std::cout << "Lambda squared: " << square(lambda) << std::endl; int number; std::cout << "\nPlease enter an odd number: "; std::cin >> number; std::cout << std::endl; // if (isOdd(number)) /* Error: identifier not found: 'isOdd' */ // std::cout << "Well done!" << std::endl; switch (getOddity(number)) { using enum Oddity; case Odd: std::cout << "Well done! And remember: you have to be odd to be number one!"; break; case Even: std::cout << std::format("Odd, {} seems to be even?", number); break; } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_01A/math.cppm ================================================ export module math; bool isOdd(int x) { return x % 2 != 0; } // Module-local function (not exported) export { auto square(const auto& x) { return x * x; } const double lambda = 1.303577269034296391257; // Conway's constant enum class Oddity { Even, Odd }; auto getOddity(int x) { return isOdd(x) ? Oddity::Odd : Oddity::Even; } } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_01B/Ex11_01B.cpp ================================================ // Separating implementation from interface within the module interface file import ; import ; import math; int main() { std::cout << "Lambda squared: " << square(lambda) << std::endl; int number; std::cout << "\nPlease enter an odd number: "; std::cin >> number; std::cout << std::endl; // if (isOdd(number)) /* Error: identifier not found: 'isOdd' */ // std::cout << "Well done!" << std::endl; switch (getOddity(number)) { using enum Oddity; case Odd: std::cout << "Well done! And remember: you have to be odd to be number one!"; break; case Even: std::cout << std::format("Odd, {} seems to be even?", number); break; } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_01B/math.cppm ================================================ export module math; export { auto square(const auto& x); const double lambda = 1.303577269034296391257; // Conway's constant enum class Oddity { Even, Odd }; auto getOddity(int x); } // The implementation of the module's functions (+ local helpers) auto square(const auto& x) { return x * x; } bool isOdd(int x) { return x % 2 != 0; } auto getOddity(int x) { return isOdd(x) ? Oddity::Odd : Oddity::Even; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_02/Ex11_02.cpp ================================================ // Defining functions in module implementation files import ; import ; import roman; int main() { std::cout << "1234 in Roman numerals is " << to_roman(1234) << std::endl; std::cout << "MMXX in Arabic numerals is " << from_roman("MMXX") << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_02/from_roman.cpp ================================================ // Implementation of the from_roman() function module roman; unsigned int from_roman(char c) { switch (c) { case 'I': return 1; case 'V': return 5; case 'X': return 10; case 'L': return 50; case 'C': return 100; case 'D': return 500; case 'M': return 1000; default: return 0; } } unsigned int from_roman(std::string_view roman) { unsigned int result{}; for (size_t i{}, n{ roman.length() }; i < n; ++i) { const auto j{ from_roman(roman[i]) }; // Integer value of the i'th roman digit // Look at the next digit (if there is one) to know whether to add or subtract j if (i + 1 == n || j >= from_roman(roman[i + 1])) result += j; else result -= j; } return result; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_02/roman.cppm ================================================ // Interface file for a Roman numerals module export module roman; import ; import ; export std::string to_roman(unsigned int i); export unsigned int from_roman(std::string_view roman); ================================================ FILE: Examples/Modules/Chapter 11/Ex11_02/to_roman.cpp ================================================ // Implementation of the to_roman() function module roman; std::string to_roman(unsigned int i) { if (i > 3999) return {}; // 3999, or MMMCMXCIX, is the largest standard Roman numeral static const std::string ms[]{ "","M","MM","MMM" }; static const std::string cds[]{ "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }; static const std::string xls[]{ "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" }; static const std::string ivs[]{ "","I","II","III","IV","V","VI","VII","VIII","IX" }; return ms[i / 1000] + cds[(i % 1000) / 100] + xls[(i % 100) / 10] + ivs[i % 10]; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_03/Ex11_03.cpp ================================================ // Using types with reachable definitions but whose names are not visible import ; import roman; int main() { // std::string_view is reachable, so its constructor can be invoked // (this constructor, unlike the std::string_view name itself, is visible) std::cout << "MMXX in Arabic numerals is " << from_roman("MMXX") << std::endl; // The names of the c_str() and size() members are visible as well // (because the definition of std::string is reachable), // and can thus be invoked. std::cout << "1234 in Roman numerals is " << to_roman(1234).c_str() << std::endl; std::cout << "This consists of " << to_roman(1234).size() << " numerals" << std::endl; // std::string_view s{ "MMXX" }; /* Error: the name std::string_view is not visible */ // std::string roman{ to_roman(567) }; /* Error: the name std::string is not visible */ auto roman{ to_roman(567) }; std::cout << "567 in Roman numerals is " << roman.c_str() << std::endl; // std::cout << "std::stoi() is not visible: " << std::stoi("1234") << std::endl; // The << operator (which is implemented as a non-member function) is not visible either // std::cout << "1234 in Roman numerals is " << to_roman(1234) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_03/from_roman.cpp ================================================ // Implementation of the from_roman() function module roman; unsigned int from_roman(char c) { switch (c) { case 'I': return 1; case 'V': return 5; case 'X': return 10; case 'L': return 50; case 'C': return 100; case 'D': return 500; case 'M': return 1000; default: return 0; } } unsigned int from_roman(std::string_view roman) { unsigned int result{}; for (size_t i{}, n{ roman.length() }; i < n; ++i) { const auto j{ from_roman(roman[i]) }; // Integer value of the i'th roman digit // Look at the next digit (if there is one) to know whether to add or subtract j if (i + 1 == n || j >= from_roman(roman[i + 1])) result += j; else result -= j; } return result; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_03/roman.cppm ================================================ // Interface file for a Roman numerals module export module roman; import ; import ; export std::string to_roman(unsigned int i); export unsigned int from_roman(std::string_view roman); ================================================ FILE: Examples/Modules/Chapter 11/Ex11_03/to_roman.cpp ================================================ // Implementation of the to_roman() function module roman; std::string to_roman(unsigned int i) { if (i > 3999) return {}; // 3999, or MMMCMXCIX, is the largest standard Roman numeral static const std::string ms[]{ "","M","MM","MMM" }; static const std::string cds[]{ "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }; static const std::string xls[]{ "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" }; static const std::string ivs[]{ "","I","II","III","IV","V","VI","VII","VIII","IX" }; return ms[i / 1000] + cds[(i % 1000) / 100] + xls[(i % 100) / 10] + ivs[i % 10]; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_04/Ex11_04.cpp ================================================ // Module implementation partitions import ; import ; import roman; int main() { std::cout << "1234 in Roman numerals is " << to_roman(1234) << std::endl; std::cout << "MMXX in Arabic numerals is " << from_roman("MMXX") << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_04/from_roman.cpp ================================================ // Implementation of the from_roman() function module roman; import :internals; unsigned int from_roman(std::string_view roman) { unsigned int result{}; for (size_t i{}, n{ roman.length() }; i < n; ++i) { const auto j{ from_roman(roman[i]) }; // Integer value of the i'th roman digit // Look at the next digit (if there is one) to know whether to add or subtract j if (i + 1 == n || j >= from_roman(roman[i + 1])) result += j; else result -= j; } return result; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_04/roman-internals.cpp ================================================ // Implementation of the internal from_roman() function module roman:internals; unsigned int from_roman(char c) { switch (c) { case 'I': return 1; case 'V': return 5; case 'X': return 10; case 'L': return 50; case 'C': return 100; case 'D': return 500; case 'M': return 1000; default: return 0; } } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_04/roman.cppm ================================================ // Interface file for a Roman numerals module export module roman; import ; import ; export std::string to_roman(unsigned int i); export unsigned int from_roman(std::string_view roman); ================================================ FILE: Examples/Modules/Chapter 11/Ex11_04/to_roman.cpp ================================================ // Implementation of the to_roman() function module roman; std::string to_roman(unsigned int i) { if (i > 3999) return {}; // 3999, or MMMCMXCIX, is the largest standard Roman numeral static const std::string ms[]{ "","M","MM","MMM" }; static const std::string cds[]{ "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }; static const std::string xls[]{ "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" }; static const std::string ivs[]{ "","I","II","III","IV","V","VI","VII","VIII","IX" }; return ms[i / 1000] + cds[(i % 1000) / 100] + xls[(i % 100) / 10] + ivs[i % 10]; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_05/Ex11_05.cpp ================================================ // Creating module interface partitions import ; import ; import roman; int main() { std::cout << "1234 in Roman numerals is " << to_roman(1234) << std::endl; std::cout << "MMXX in Arabic numerals is " << from_roman("MMXX") << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_05/from_roman.cpp ================================================ // Implementation of the from_roman() function module roman; import :internals; unsigned int from_roman(std::string_view roman) { unsigned int result{}; for (size_t i{}, n{ roman.length() }; i < n; ++i) { const auto j{ from_roman(roman[i]) }; // Integer value of the i'th roman digit // Look at the next digit (if there is one) to know whether to add or subtract j if (i + 1 == n || j >= from_roman(roman[i + 1])) result += j; else result -= j; } return result; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_05/roman-from.cppm ================================================ // Module interface file for the from partition of the roman module export module roman:from; import ; export unsigned int from_roman(std::string_view roman); ================================================ FILE: Examples/Modules/Chapter 11/Ex11_05/roman-internals.cpp ================================================ // Implementation of the internal from_roman() function module roman:internals; unsigned int from_roman(char c) { switch (c) { case 'I': return 1; case 'V': return 5; case 'X': return 10; case 'L': return 50; case 'C': return 100; case 'D': return 500; case 'M': return 1000; default: return 0; } } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_05/roman-to.cppm ================================================ // Module interface file for the to partition of the roman module export module roman:to; import ; export std::string to_roman(unsigned int i) { if (i > 3999) return {}; // 3999, or MMMCMXCIX, is the largest standard Roman numeral static const std::string ms[]{ "","M","MM","MMM" }; static const std::string cds[]{ "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }; static const std::string xls[]{ "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" }; static const std::string ivs[]{ "","I","II","III","IV","V","VI","VII","VIII","IX" }; return ms[i / 1000] + cds[(i % 1000) / 100] + xls[(i % 100) / 10] + ivs[i % 10]; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_05/roman.cppm ================================================ // Primary module interface file for the roman module export module roman; export import :to; // Not: 'export import roman:to;' export import :from; // Not: 'export import roman:from;' // export import :internals; /* Error: only interface partitions can be exported */ ================================================ FILE: Examples/Modules/Chapter 11/Ex11_06/Ex11_06.cpp ================================================ // Defining and using a namespace import ; import ; namespace math { const double sqrt2{ 1.414213562373095 }; // the square root of 2 auto square(const auto& x) { return x * x; } auto pow4(const auto& x) { return square(square(x)); } } int main() { std::cout << "math::sqrt2 has the value " << math::sqrt2 << std::endl; std::cout << "This should be 0: " << (math::sqrt2 - std::numbers::sqrt2) << std::endl; std::cout << "This should be 2: " << math::square(math::sqrt2) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_06A/Ex11_06A.cpp ================================================ // Defining and using a namespace import ; import ; import squaring; int main() { std::cout << "math::sqrt2 has the value " << math::sqrt2 << std::endl; std::cout << "This should be 0: " << (math::sqrt2 - std::numbers::sqrt2) << std::endl; std::cout << "This should be 2: " << math::square(math::sqrt2) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_06A/squaring.cppm ================================================ export module squaring; namespace math { export const double sqrt2{ 1.414213562373095 }; // the square root of 2 export auto square(const auto& x) { return x * x; } export auto pow4(const auto& x) { return square(square(x)); } } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_06B/Ex11_06B.cpp ================================================ // Defining and using a namespace import ; import ; import squaring; int main() { std::cout << "math::sqrt2 has the value " << math::sqrt2 << std::endl; std::cout << "This should be 0: " << (math::sqrt2 - std::numbers::sqrt2) << std::endl; std::cout << "This should be 2: " << math::square(math::sqrt2) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_06B/squaring.cppm ================================================ export module squaring; export namespace math // Exports all nested declarations at once { const double sqrt2{ 1.414213562373095 }; // the square root of 2 auto square(const auto& x) { return x * x; } auto pow4(const auto& x) { return square(square(x)); } } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_07/Ex11_07.cpp ================================================ // Separating declarations and definitions of functions // declared in a namespace. import ; import math; int main() { const double values[]{ 10, 2, 1, 8, 3, 7, 4, 5, 6, 9 }; std::cout << "Arithmetic mean: " << math::averages::arithmetic_mean(values) << std::endl; std::cout << "Geometric mean: " << math::averages::geometric_mean(values) << std::endl; std::cout << "Root mean square: " << math::averages::rms(values) << std::endl; std::cout << "Median: " << math::averages::median(values) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_07/math.cpp ================================================ module; #include // For std::pow(), std::sqrt(), ... module math; import ; // For std::numeric_limits::quiet_NaN() import ; // For std::numeric_limits::quiet_NaN() void quicksort(std::vector& data); // See Chapter 8 // Option 1: define in nested namespace block (compact syntax) namespace math::averages { double arithmetic_mean(std::span data) { // The arithmetic mean, the most commonly used average, // is defined as the sum of all elements divided by the number of elements. double sum {}; for (auto value : data) sum += value; return data.empty() ? std::numeric_limits::quiet_NaN() // Or std::nan("") : sum / data.size(); } } // Option 2: define in nested namespace blocks namespace math { namespace averages { double geometric_mean(std::span data) { // The geometric mean of n elements // is defined as the n-th root of the product of all n elements double product{ 1 }; for (auto value : data) product *= value; return data.empty() ? std::numeric_limits::quiet_NaN() : std::pow(product, 1.0 / data.size()); } } } // Option 3: define using fully qualified function name double math::averages::rms(std::span data) { // The RMS or root mean square is defined as // square root of the arithmetic mean of the squares of the elements. double sum_squares{}; for (auto value : data) sum_squares += square(value); return data.empty() ? std::numeric_limits::quiet_NaN() // Or std::nan("") : std::sqrt(sum_squares / data.size()); } // Option 4: define using qualified name in outer namespace block namespace math { double averages::median(std::span data) { // The median of an odd number of elements is the value // that appears in the middle position of these elements when they are sorted. // The median of an even number of elements is typically // defined as the mean of the two middle elements in a sorted range. // We cannot sort the data span itself, because it's elements are const. // Therefore, we first copy the const input data to be able to sort it std::vector sorted; // Use range-based for loop to copy the input data. // See Chapter 20 for built-in means of copying a data range. for (auto value : data) sorted.push_back(value); // See Chapter 20 for built-in means of (partially) sorting data quicksort(sorted); const size_t mid = data.size() / 2; return data.empty() ? std::numeric_limits::quiet_NaN() // Or std::nan : data.size() % 2 == 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; } } void quicksort(std::vector& data, size_t start, size_t end); void quicksort(std::vector& data) { if (!data.empty()) quicksort(data, 0, data.size() - 1); } void quicksort(std::vector& data, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle value to partition set, // and move it to the front of the current range std::swap(data[start], data[(start + end) / 2]); // Compare all other values against chosen value at index start size_t current{ start }; for (size_t i{ start + 1 }; i <= end; i++) { if (data[i] < data[start]) // Is the value less than chosen value? std::swap(data[++current], data[i]); // Yes, so swap to the left } std::swap(data[start], data[current]); // Swap chosen and last swapped words if (current > start) quicksort(data, start, current - 1); // Sort left subset if exists if (end > current + 1) quicksort(data, current + 1, end); // Sort right subset if exists } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_07/math.cppm ================================================ export module math; import ; export namespace math { auto square(const auto& x) { return x * x; }; namespace averages { double arithmetic_mean(std::span data); double geometric_mean(std::span data); double rms(std::span data); double median(std::span data); } } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_08/Ex11_08.cpp ================================================ // Using using declarations and using directives // (Note: example was not named in the text) import ; import squaring; /* Make names from the math namespace available locally */ // Note: text uses hypot(), but this causes ambiguities with hypot() // function of with Visual Studio auto hypotenuse(const auto& x, const auto& y) { using namespace math; // Or: // using math::square; // using math::sqrt; /* Same as, of course: using std::sqrt; */ return sqrt(square(x) + square(y)); } int main() { std::cout << "math::sqrt2 has the value " << math::sqrt2 << std::endl; std::cout << "This should be 0: " << (math::sqrt2 - std::numbers::sqrt2) << std::endl; std::cout << "This should be 2: " << math::square(math::sqrt2) << std::endl; std::cout << "This should be 5: " << hypotenuse(3, 4) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 11/Ex11_08/squaring.cppm ================================================ module; // Start of the global module fragment (for #include directives) #include // For std::sqrt() export module squaring; import ; // For std::numbers::sqrt2 /* Re-export two existing entities from the math namespace using using declarations */ export namespace math // Exports all nested declarations at once { using std::numbers::sqrt2; using std::sqrt; // Never 'using std::sqrt();' or 'using std::sqrt(double);'! auto square(const auto& x) { return x * x; } auto pow4(const auto& x) { return square(square(x)); } } /* Export all names from a namespace from the math namespace using a using directive */ //namespace math //{ // export using namespace std::numbers; //} ================================================ FILE: Examples/Modules/Chapter 12/Ex12_01/Ex12_01.cpp ================================================ // Defining a class constructor import ; // Class to represent a box class Box { public: // Constructor Box(double length, double width, double height) { std::cout << "Box constructor called." << std::endl; m_length = length; m_width = width; m_height = height; } // Function to calculate the volume of a box double volume() { return m_length * m_width * m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; int main() { Box firstBox {80.0, 50.0, 40.0}; // Create a box double firstBoxVolume {firstBox.volume()}; // Calculate the box volume std::cout << "Volume of Box object is " << firstBoxVolume << std::endl; // Box secondBox; // Causes a compiler error message } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_01A/Ex12_01A.cpp ================================================ // Defining a default constructor import ; // Class to represent a box class Box { public: // Box() {} // Explicitly defined default constructor Box() = default; // Defaulted default constructor // Constructor Box(double length, double width, double height) { std::cout << "Box constructor called." << std::endl; m_length = length; m_width = width; m_height = height; } // Function to calculate the volume of a box double volume() { return m_length * m_width * m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; int main() { Box firstBox {80.0, 50.0, 40.0}; // Create a box double firstBoxVolume {firstBox.volume()}; // Calculate the box volume std::cout << "Volume of Box object is " << firstBoxVolume << std::endl; Box secondBox; // No longer causes a compiler error message } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_02/Ex12_02.cpp ================================================ // Defining member functions outside a class // Note that, unlike what we said in the text, // in this example we put the function definitions after the main() function. // This illustrates that to invoke a member function, // the compiler again only needs access to the signature of the member function. import ; // Class to represent a box class Box { public: Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; int main() { Box firstBox {80.0, 50.0, 40.0}; // Create a box double firstBoxVolume {firstBox.volume()}; // Calculate the box volume std::cout << "Volume of Box object is " << firstBoxVolume << std::endl; Box secondBox; // No longer causes a compiler error message } // Constructor definition Box::Box(double length, double width, double height) { std::cout << "Box constructor called." << std::endl; m_length = length; m_width = width; m_height = height; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_03/Ex12_03.cpp ================================================ // Using a member initializer list import ; // Class to represent a box class Box { public: Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; int main() { Box firstBox {80.0, 50.0, 40.0}; // Create a box double firstBoxVolume {firstBox.volume()}; // Calculate the box volume std::cout << "Volume of Box object is " << firstBoxVolume << std::endl; Box secondBox; // No longer causes a compiler error message } // Constructor definition Box::Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} { std::cout << "Box constructor called." << std::endl; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_04/Ex12_04.cpp ================================================ // Using the explicit keyword // Uncomment "explicit" to make the compilation of // the expression "box1.hasLargerVolumeThan(50.0)" fail import ; class Cube { public: /*explicit*/ Cube(double side); // Constructor double volume(); // Calculate volume of a cube bool hasLargerVolumeThan(Cube cube); // Compare volume of a cube with another private: double m_side; }; Cube::Cube(double side) : m_side{ side } { std::cout << "Cube constructor called." << std::endl; } double Cube::volume() { return m_side * m_side * m_side; } bool Cube::hasLargerVolumeThan(Cube cube) { return volume() > cube.volume(); } int main() { Cube box1{ 7.0 }; Cube box2{ 3.0 }; if (box1.hasLargerVolumeThan(box2)) std::cout << "box1 is larger than box2." << std::endl; else std::cout << "Volume of box1 is less than or equal to that of box2." << std::endl; std::cout << "Volume of box1 is " << box1.volume() << std::endl; if (box1.hasLargerVolumeThan(50.0)) std::cout << "Volume of box1 is greater than 50" << std::endl; else std::cout << "Volume of box1 is less than or equal to 50" << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_05/Ex12_05.cpp ================================================ // Delegating constructors import ; class Box { public: Box(double length, double width, double height); explicit Box(double side); // Constructor for a cube (explicit!) Box() = default; // Defaulted default constructor double volume(); // Function to calculate the volume of a box private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; // A constructor that initializes all three member variables Box::Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} { std::cout << "Box constructor 1 called." << std::endl; } // This second constructor forwards to the first one to initialize all members. // Note that you do not repeat the explicit keyword here! Box::Box(double side) : Box{ side, side, side } { std::cout << "Box constructor 2 called." << std::endl; } int main() { Box box1{ 2.0, 3.0, 4.0 }; // An arbitrary box Box box2{ 5.0 }; // A box that is a cube std::cout << "box1 volume = " << box1.volume() << std::endl; std::cout << "box2 volume = " << box2.volume() << std::endl; } double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_05A/Ex12_05A.cpp ================================================ // Implementing the copy constructor // Note: this example is explained but not named in the text import ; class Box { public: Box(double length, double width, double height); explicit Box(double side); // Constructor for a cube (explicit!) Box() = default; // Defaulted default constructor Box(const Box& box); // Copy constructor double volume(); // Function to calculate the volume of a box private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; // A basic copy constructor Box::Box(const Box& box) : m_length{ box.m_length }, m_width{ box.m_width }, m_height{ box.m_height } { std::cout << "Copy constructor called." << std::endl; } // A delegating copy constructor //Box::Box(const Box& box) : Box{ box.m_length, box.m_width, box.m_height } //{ // std::cout << "Copy constructor called." << std::endl; //} int main() { Box box1{ 2.0, 3.0, 4.0 }; // An arbitrary box Box box2{ 5.0 }; // A box that is a cube std::cout << "box1 volume = " << box1.volume() << std::endl; std::cout << "box2 volume = " << box2.volume() << std::endl; Box box3{ box2 }; std::cout << "box3 volume = " << box3.volume() << std::endl; // Volume = 125 } // A constructor that initializes all three member variables Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor 1 called." << std::endl; } // This second constructor forwards to the first one to initialize all members. // Note that you do not repeat the explicit keyword here! Box::Box(double side) : Box{ side, side, side } { std::cout << "Box constructor 2 called." << std::endl; } double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_06/Box.cppm ================================================ // Module interface file for a module exporting the Box class export module box; import ; // Class to represent a box export class Box { public: Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_06/Ex12_06.cpp ================================================ // Exporting a class from a module import ; // For use of std::cout, std::endl, etc. import box; // For use of the Box class int main() { Box myBox{ 6.0, 6.0, 18.5 }; // Create a box std::cout << "Volume of the first Box object is " << myBox.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_06A/Box.cpp ================================================ module box; import ; // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_06A/Box.cppm ================================================ export module box; // Class to represent a box export class Box { public: Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_06A/Ex12_06A.cpp ================================================ // Defining class members in a module implementation file import ; // For use of std::cout, std::endl, etc. import box; // For use of the Box class int main() { Box myBox{ 6.0, 6.0, 18.5 }; // Create a box std::cout << "Volume of the first Box object is " << myBox.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_06B/Box.cppm ================================================ export module box; import ; // Class to represent a box export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Function to calculate the volume of a box double volume() { return m_length * m_width * m_height; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_06B/Ex12_06B.cpp ================================================ // Defining classes with in-class member definitions. import ; // For use of std::cout, std::endl, etc. import box; // For use of the Box class int main() { Box myBox{ 6.0, 6.0, 18.5 }; // Create a box std::cout << "Volume of the first Box object is " << myBox.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_07/Box.cpp ================================================ module box; import ; // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_07/Box.cppm ================================================ export module box; export class Box { public: // Constructors Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box // Functions to provide access to the values of member variables double getLength() { return m_length; } double getWidth() { return m_width; } double getHeight() { return m_height; } // Functions to set member variable values void setLength(double length) { if (length > 0) m_length = length; } void setWidth(double width) { if (width > 0) m_width = width; } void setHeight(double height) { if (height > 0) m_height = height; } private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_07/Ex12_07.cpp ================================================ // Accessing private members through getters and setters import ; import box; int main() { Box myBox {3.0, 4.0, 5.0}; std::cout << "myBox dimensions are " << myBox.getLength() << " by " << myBox.getWidth() << " by " << myBox.getHeight() << std::endl; myBox.setLength(-20.0); // ignored! myBox.setWidth(40.0); myBox.setHeight(10.0); std::cout << "myBox dimensions are now " << myBox.getLength() // 3 (unchanged) << " by " << myBox.getWidth() // by 40 << " by " << myBox.getHeight() << std::endl; // by 10 } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_08/Box.cpp ================================================ module box; import ; // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } // Mutator function definitions Box& Box::setLength(double length) { if (length > 0) m_length = length; return *this; } Box& Box::setWidth(double width) { if (width > 0) m_width = width; return *this; } Box& Box::setHeight(double height) { if (height > 0) m_height = height; return *this; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_08/Box.cppm ================================================ export module box; export class Box { public: // Constructors Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box // Inspector functions double getLength() { return m_length; } double getWidth() { return m_width; } double getHeight() { return m_height; } // Mutator functions Box& setLength(double length); Box& setWidth(double width); Box& setHeight(double height); private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_08/Ex12_08.cpp ================================================ // Accessing private members through getters and setters (method chaining variant) import ; import box; int main() { Box myBox {3.0, 4.0, 5.0}; std::cout << "myBox dimensions are " << myBox.getLength() << " by " << myBox.getWidth() << " by " << myBox.getHeight() << std::endl; myBox.setLength(-20.0).setWidth(40.0).setHeight(10.0); // Set all dimensions of myBox std::cout << "myBox dimensions are now " << myBox.getLength() // 3 (unchanged) << " by " << myBox.getWidth() // by 40 << " by " << myBox.getHeight() << std::endl; // by 10 } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_09/Box.cpp ================================================ module box; import ; // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Const member function definition double Box::volume() const { return m_length * m_width * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_09/Box.cppm ================================================ export module box; export class Box { public: // Constructors Box() = default; Box(double length, double width, double height); double volume() const; // Const function to calculate the volume of a box // Functions to provide access to the values of member variables (all const!) double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions to set member variable values (not const!) void setLength(double length) { if (length > 0) m_length = length; } void setWidth(double width) { if (width > 0) m_width = width; } void setHeight(double height) { if (height > 0) m_height = height; } private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_09/Ex12_09.cpp ================================================ // Const objects and const member functions import ; import box; int main() { // v-- this const was added... const Box myBox {3.0, 4.0, 5.0}; std::cout << "myBox dimensions are " << myBox.getLength() << " by " << myBox.getWidth() << " by " << myBox.getHeight() << std::endl; // Invoking mutators / setters is not possible on a const object: //myBox.setLength(-20.0); // ignored! //myBox.setWidth(40.0); //myBox.setHeight(10.0); //std::cout << "myBox dimensions are now " << myBox.getLength() // 3 (unchanged) // << " by " << myBox.getWidth() // by 40 // << " by " << myBox.getHeight() << std::endl; // by 10 std::cout << "myBox's volume is " << myBox.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_10/Box.cpp ================================================ module box; import ; // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Const member function definition double Box::volume() const { return m_length * m_width * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_10/Box.cppm ================================================ export module box; import ; export class Box { public: // Constructors Box() = default; Box(double length, double width, double height); double volume() const; // Const function to calculate the volume of a box // Non-const overloads (return references to dimension variable) double& length() { std::cout << "non-const overload called\n"; return m_length; }; double& width() { std::cout << "non-const overload called\n"; return m_width; }; double& height() { std::cout << "non-const overload called\n"; return m_height; }; // Const overloads (return references to const variables) const double& length() const { std::cout << "const overload called\n"; return m_length; }; const double& width() const { std::cout << "const overload called\n"; return m_width; }; const double& height() const { std::cout << "const overload called\n"; return m_height; }; // Attempt to return non-const references to member variables from const functions // double& length() const { return m_length; }; // This must not be allowed to compile! // double& width() const { return m_width; }; // double& height() const { return m_height; }; private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_10/Ex12_10.cpp ================================================ // Overloading on const import ; import box; int main() { const Box constBox{ 1, 2, 3 }; // constBox.length() = 2; // Does not compile: good! std::cout << constBox.length() << std::endl; Box nonConstBox{ 3, 2, 1 }; nonConstBox.length() *= 2; std::cout << nonConstBox.length() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_11/Box.cpp ================================================ module box; import ; // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Const member function definition double Box::volume() const { return m_length * m_width * m_height; } // Modify mutable member variable from a const member function void Box::printVolume() const { // Count how many times printVolume() is called using a mutable member in a const function std::cout << "The volume of this box is " << volume() << std::endl; std::cout << "printVolume() has been called " << ++m_count << " time(s)" << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_11/Box.cppm ================================================ export module box; export class Box { public: // Constructors Box() = default; Box(double length, double width, double height); double volume() const; // Function to calculate the volume of a box void printVolume() const; // Function to print out the volume of a box (const!) // Functions to provide access to the values of member variables (all const!) double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions to set member variable values (not const!) void setLength(double length) { if (length > 0) m_length = length; } void setWidth(double width) { if (width > 0) m_width = width; } void setHeight(double height) { if (height > 0) m_height = height; } private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; mutable unsigned m_count{}; // Counts the amount of time printVolume() is called }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_11/Ex12_11.cpp ================================================ // Const objects and const member functions import ; import box; int main() { const Box myBox {3.0, 4.0, 5.0}; std::cout << "myBox dimensions are " << myBox.getLength() << " by " << myBox.getWidth() << " by " << myBox.getHeight() << std::endl; myBox.printVolume(); myBox.printVolume(); myBox.printVolume(); } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_12/Box.cpp ================================================ module box; import ; // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Const member function definition double Box::volume() const { return m_length * m_width * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_12/Box.cppm ================================================ export module box; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0} {} // A delegating default constructor Box(double length, double width, double height); double volume() const; // Function to calculate the volume of a box friend double surfaceArea(const Box& box); // Friend function for the surface area private: double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_12/Ex12_12.cpp ================================================ // Using a friend function of a class import ; import ; import box; int main() { Box box1 {2.2, 1.1, 0.5}; // An arbitrary box Box box2; // A default box auto box3{ std::make_unique(15.0, 20.0, 8.0) }; // Dynamically allocated Box std::cout << "Volume of box1 = " << box1.volume() << std::endl; std::cout << "Surface area of box1 = " << surfaceArea(box1) << std::endl; std::cout << "Volume of box2 = "<< box2.volume() << std::endl; std::cout << "Surface area of box2 = " << surfaceArea(box2) << std::endl; std::cout << "Volume of box3 = " << box3->volume() << std::endl; std::cout << "Surface area of box3 = " << surfaceArea(*box3) << std::endl; } // friend function to calculate the surface area of a Box object double surfaceArea(const Box& box) { return 2.0 * (box.m_length * box.m_width + box.m_length * box.m_height + box.m_height * box.m_width); } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_13/Box.cpp ================================================ module box; import ; Box::Box(double length, double width, double height) // Constructor definition : m_length{length}, m_width{width}, m_height{height} { std::cout << "Box constructor 1 called." << std::endl; } Box::Box(double side) : Box{side, side, side} // Constructor for a cube { std::cout << "Box constructor 2 called." << std::endl; } Box::Box() // Default constructor { std::cout << "Default Box constructor called." << std::endl; } Box::Box(const Box& box) // Copy constructor : m_length{box.m_length}, m_width{box.m_width}, m_height{box.m_height} { std::cout << "Box copy constructor called." << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_13/Box.cppm ================================================ export module box; export class Box { public: /* Constructors */ Box(double length, double width, double height); Box(double side); // Constructor for a cube Box(); // Default constructor Box(const Box& box); // Copy constructor double volume() const { return m_length * m_width * m_height; }; private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_13/Ex12_13.cpp ================================================ // Creating an array of objects import ; import box; int main() { const Box box1 {2.0, 3.0, 4.0}; // An arbitrary box Box box2 {5.0}; // A box that is a cube std::cout << "box1 volume = " << box1.volume() << std::endl; std::cout << "box2 volume = " << box2.volume() << std::endl; Box box3 {box2}; std::cout << "box3 volume = " << box3.volume() << std::endl; // Volume = 125 std::cout << std::endl; Box boxes[6] {box1, box2, box3, Box {2.0}}; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_14/Box.cpp ================================================ module box; import ; Box::Box(double length, double width, double height) // Constructor definition : m_length{length}, m_width{width}, m_height{height} { ++s_object_count; std::cout << "Box constructor 1 called." << std::endl; } Box::Box(double side) : Box{side, side, side} // Constructor for a cube { // Do not increment s_object_count in forwarding constructor: // already incremented in the constructor this constructor forwards to! std::cout << "Box constructor 2 called." << std::endl; } Box::Box() // Default constructor { ++s_object_count; std::cout << "Default Box constructor called." << std::endl; } Box::Box(const Box& box) // Copy constructor : m_length{box.m_length}, m_width{box.m_width}, m_height{box.m_height} { ++s_object_count; std::cout << "Box copy constructor called." << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_14/Box.cppm ================================================ export module box; export class Box { public: Box(); // Default constructor Box(double side); // Constructor for a cube Box(const Box& box); // Copy constructor Box(double length, double width, double height); double volume() const { return m_length * m_width * m_height; } size_t getObjectCount() const { return s_object_count; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; static inline size_t s_object_count {}; // Count of objects ever created }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_14/Ex12_14.cpp ================================================ // Using a static member variable import ; import box; int main() { const Box box1 {2.0, 3.0, 4.0}; // An arbitrary box Box box2 {5.0}; // A box that is a cube std::cout << "box1 volume = " << box1.volume() << std::endl; std::cout << "box2 volume = " << box2.volume() << std::endl; Box box3 {box2}; std::cout << "box3 volume = " << box3.volume() << std::endl; // Volume = 125 std::cout << std::endl; Box boxes[6] {box1, box2, box3, Box {2.0}}; std::cout << "\nThere are now " << box1.getObjectCount() << " Box objects.\n"; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_15/CylindricalBox.cpp ================================================ module cylindrical; import ; CylindricalBox::CylindricalBox(float radius, float height, std::string_view material) : m_radius{ radius } , m_height{ height } , m_material{ material } { std::cout << "Box constructed consisting of " << material; if (material == s_default_material) { std::cout << " (the default material!)"; } std::cout << std::endl; } float CylindricalBox::volume() const { return PI * m_radius * m_radius * m_height; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_15/CylindricalBox.cppm ================================================ export module cylindrical; import ; import ; export class CylindricalBox { public: static inline const float s_max_radius { 35.0f }; static inline const float s_max_height { 60.0f }; static inline const std::string_view s_default_material { "paperboard" }; CylindricalBox(float radius, float height, std::string_view material = s_default_material); float volume() const; private: // The value of PI used by CylindricalBox's volume() function static inline const float PI { 3.141592f }; float m_radius; float m_height; std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_15/Ex12_15.cpp ================================================ // Defining and using static constants import ; import cylindrical; int main() { CylindricalBox bigBox{ 1.23f, CylindricalBox::s_max_height, CylindricalBox::s_default_material }; std::cout << "The volume of bigBox is " << bigBox.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_16/Box.cpp ================================================ module box; import ; Box::Box(double length, double width, double height) // Constructor definition : m_length{length}, m_width{width}, m_height{height} { ++s_object_count; std::cout << "Box constructor 1 called." << std::endl; } Box::Box(double side) : Box{side, side, side} // Constructor for a cube { // Do not increment s_object_count in forwarding constructor: // already incremented in the constructor this constructor forwards to! std::cout << "Box constructor 2 called." << std::endl; } Box::Box() // Default constructor { ++s_object_count; std::cout << "Default Box constructor called." << std::endl; } Box::Box(const Box& box) // Copy constructor : m_length{box.m_length}, m_width{box.m_width}, m_height{box.m_height} { ++s_object_count; std::cout << "Box copy constructor called." << std::endl; } Box::~Box() // Destructor { std::cout << "Box destructor called." << std::endl; --s_object_count; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_16/Box.cppm ================================================ export module box; export class Box { public: Box(); // Default constructor Box(double side); // Constructor for a cube Box(const Box& box); // Copy constructor Box(double length, double width, double height); ~Box(); // Destructor double volume() const { return m_length * m_width * m_height; } static size_t getObjectCount() { return s_object_count; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; static inline size_t s_object_count {}; // Count of objects ever created }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_16/Ex12_16.cpp ================================================ // Implementing a destructor import ; import box; int main() { std::cout << "There are now " << Box::getObjectCount() << " Box objects." << std::endl; const Box box1 {2.0, 3.0, 4.0}; // An arbitrary box Box box2 {5.0}; // A box that is a cube std::cout << "There are now " << Box::getObjectCount() << " Box objects." << std::endl; for (double d {} ; d < 3.0 ; ++d) { Box box {d, d + 1.0, d + 2.0}; std::cout << "Box volume is " << box.volume() << std::endl; } std::cout << "There are now " << Box::getObjectCount() << " Box objects." << std::endl; auto pBox{ std::make_unique(1.5, 2.5, 3.5) }; std::cout << "Box volume is " << pBox->volume() << std::endl; std::cout << "There are now " << pBox->getObjectCount() << " Box objects." << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_17/Box.cppm ================================================ export module box; import ; import ; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } void listBox() const { std::cout << std::format("Box({:.1f},{:.1f},{:.1f})", m_length, m_width, m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_17/Ex12_17.cpp ================================================ // Using a linked list import box.random; import truckload; import ; int main() { Truckload load1; // Create an empty list // Add 12 random Box objects to the list const size_t boxCount {12}; for (size_t i {} ; i < boxCount ; ++i) load1.addBox(randomSharedBox()); std::cout << "The first list:\n"; load1.listBoxes(); // Copy the truckload Truckload copy{load1}; std::cout << "The copied truckload:\n"; copy.listBoxes(); // Find the largest Box in the list SharedBox largestBox{load1.getFirstBox()}; SharedBox nextBox{load1.getNextBox()}; while (nextBox) { if (nextBox->compare(*largestBox) > 0) largestBox = nextBox; nextBox = load1.getNextBox(); } std::cout << "\nThe largest box in the first list is "; largestBox->listBox(); std::cout << std::endl; load1.removeBox(largestBox); std::cout << "\nAfter deleting the largest box, the list contains:\n"; load1.listBoxes(); const size_t nBoxes {20}; // Number of vector elements std::vector boxes; // Array of Box objects for (size_t i {} ; i < nBoxes ; ++i) boxes.push_back(randomSharedBox()); Truckload load2{boxes}; std::cout << "\nThe second list:\n"; load2.listBoxes(); auto smallestBox{ load2.getFirstBox() }; for (auto box{ load2.getNextBox() }; box; box = load2.getNextBox()) if (box->compare(*smallestBox) < 0) smallestBox = box; std::cout << "\nThe smallest box in the second list is "; smallestBox->listBox(); std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_17/Package.cpp ================================================ module truckload:package; import :shared_box; class Package { public: Package(SharedBox box) : m_box{box}, m_next{nullptr} {} // Constructor ~Package() { delete m_next; } // Destructor // Retrieve the Box pointer SharedBox getBox() const { return m_box; } // Retrieve or update the pointer to the next Package Package* getNext() { return m_next; } void setNext(Package* package) { m_next = package; } private: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_17/RandomBoxes.cppm ================================================ export module box.random; import box; import ; // For random number generation import ; // For std::bind() import ; // For std::make_shared<>() and std::shared_ptr<>; // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } export Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return { random(), random(), random() }; } export auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_17/SharedBox.cppm ================================================ export module truckload:shared_box; import ; import box; export using SharedBox = std::shared_ptr; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_17/Truckload.cpp ================================================ module truckload; import ; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->getNext()) { addBox(package->getBox()); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } void Truckload::listBoxes() const { const size_t boxesPerLine{ 4 }; size_t count {}; for (Package* package{m_head}; package; package = package->getNext()) { std::cout << ' '; package->getBox()->listBox(); if (! (++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } SharedBox Truckload::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->getBox() : nullptr; } SharedBox Truckload::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->getNext(); // Move to the next package return m_current? m_current->getBox() : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->setNext(package); // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->getBox() == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->setNext(current->getNext()); // Update pointers in member variables where required: if (current == m_head) m_head = current->getNext(); if (current == m_tail) m_tail = previous; if (current == m_current) m_current = current->getNext(); current->setNext(nullptr); // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->getNext(); // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_17/Truckload.cppm ================================================ export module truckload; export import :shared_box; import :package; import ; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload void listBoxes() const; // Output the Boxes private: Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list Package* m_current {}; // Last retrieved from the list }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_18/Box.cppm ================================================ export module box; import ; import ; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } void listBox() const { std::cout << std::format("Box({:.1f},{:.1f},{:.1f})", m_length, m_width, m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Examples/Modules/Chapter 12/Ex12_18/Ex12_18.cpp ================================================ // Using nested classes import box.random; import truckload; import ; int main() { Truckload load1; // Create an empty list // Add 12 random Box objects to the list const size_t boxCount {12}; for (size_t i {} ; i < boxCount ; ++i) load1.addBox(randomSharedBox()); std::cout << "The first list:\n"; load1.listBoxes(); // Copy the truckload Truckload copy{load1}; std::cout << "The copied truckload:\n"; copy.listBoxes(); // Find the largest Box in the list SharedBox largestBox{load1.getFirstBox()}; SharedBox nextBox{load1.getNextBox()}; while (nextBox) { if (nextBox->compare(*largestBox) > 0) largestBox = nextBox; nextBox = load1.getNextBox(); } std::cout << "\nThe largest box in the first list is "; largestBox->listBox(); std::cout << std::endl; load1.removeBox(largestBox); std::cout << "\nAfter deleting the largest box, the list contains:\n"; load1.listBoxes(); const size_t nBoxes {20}; // Number of vector elements std::vector boxes; // Array of Box objects for (size_t i {} ; i < nBoxes ; ++i) boxes.push_back(randomSharedBox()); Truckload load2{boxes}; std::cout << "\nThe second list:\n"; load2.listBoxes(); auto smallestBox{ load2.getFirstBox() }; for (auto box{ load2.getNextBox() }; box; box = load2.getNextBox()) if (box->compare(*smallestBox) < 0) smallestBox = box; std::cout << "\nThe smallest box in the second list is "; smallestBox->listBox(); std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_18/RandomBoxes.cppm ================================================ export module box.random; import box; import ; // For random number generation import ; // For std::bind() import ; // For std::make_shared<>() and std::shared_ptr<>; // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } export Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } export auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_18/Truckload.cpp ================================================ module truckload; import ; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } void Truckload::listBoxes() const { const size_t boxesPerLine{ 4 }; size_t count {}; for (Package* package{m_head}; package; package = package->m_next) { std::cout << ' '; package->m_box->listBox(); if (! (++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } SharedBox Truckload::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } SharedBox Truckload::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; if (current == m_current) m_current = current->m_next; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } ================================================ FILE: Examples/Modules/Chapter 12/Ex12_18/Truckload.cppm ================================================ export module truckload; import box; import ; import ; export using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload void listBoxes() const; // Output the Boxes private: class Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list Package* m_current {}; // Last retrieved from the list }; ================================================ FILE: Examples/Modules/Chapter 13/Ex13_01/Box.cppm ================================================ export module box; export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } bool operator<(const Box& aBox) const // Less-than operator { return volume() < aBox.volume(); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Examples/Modules/Chapter 13/Ex13_01/Ex13_01.cpp ================================================ // Implementing a less-than operator import ; import ; import box; int main() { std::vector boxes {Box {2.0, 2.0, 3.0}, Box {1.0, 3.0, 2.0}, Box {1.0, 2.0, 1.0}, Box {2.0, 3.0, 3.0}}; Box smallBox {boxes[0]}; for (const auto& box : boxes) { if (box < smallBox) smallBox = box; } std::cout << "The smallest box has dimensions " << smallBox.getLength() << 'x' << smallBox.getWidth() << 'x' << smallBox.getHeight() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_02/Box.cppm ================================================ export module box; export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } bool operator<(const Box& aBox) const // Less-than operator { return volume() < aBox.volume(); } bool operator<(double value) const; // Compare Box volume < double value private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; // Compare the volume of a Box object with a constant bool Box::operator<(double value) const { return volume() < value; } // Function comparing a constant with volume of a Box object export bool operator<(double value, const Box& aBox) { return value < aBox.volume(); } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_02/Ex13_02.cpp ================================================ // Using the overloaded 'less-than' operators for Box ojects import ; import ; import ; import box; // Display box dimensions void show(const Box& box) { std::cout << std::format("Box {:g}x{:g}x{:g}", box.getLength(), box.getWidth(), box.getHeight()) << std::endl; } int main() { std::vector boxes {Box {2.0, 2.0, 3.0}, Box {1.0, 3.0, 2.0}, Box {1.0, 2.0, 1.0}, Box {2.0, 3.0, 3.0}}; const double minVolume{6.0}; std::cout << "Objects with volumes less than " << minVolume << " are:\n"; for (const auto& box : boxes) if (box < minVolume) show(box); std::cout << "Objects with volumes greater than " << minVolume << " are:\n"; for (const auto& box : boxes) if (minVolume < box) show(box); } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_03/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const { return m_length == otherBox.m_length && m_width == otherBox.m_width && m_height == otherBox.m_height; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; ================================================ FILE: Examples/Modules/Chapter 13/Ex13_03/Ex13_03.cpp ================================================ // Overloading <=> and == to fully support all comparison operators import ; import ; import ; import ; import box; void show(const Box& box) { std::cout << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } void show(const Box& box1, std::string_view relationship, const Box& box2) { show(box1); std::cout << relationship; show(box2); std::cout << std::endl; } int main() { const std::vector boxes {Box {2.0, 1.5, 3.0}, Box {1.0, 3.0, 5.0}, Box {1.0, 2.0, 1.0}, Box {2.0, 3.0, 2.0}}; const Box theBox {3.0, 1.0, 4.0}; for (const auto& box : boxes) if (theBox > box) show(theBox, " is greater than ", box); // > works std::cout << std::endl; for (const auto& box : boxes) if (theBox != box) show(theBox, " is not equal to ", box); // != works std::cout << std::endl; for (const auto& box : boxes) if (6.0 <= box) // Yes, even double <= Box works!! { std::cout << "6 is less than or equal to "; show(box); std::cout << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_03A/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; ================================================ FILE: Examples/Modules/Chapter 13/Ex13_03A/Ex13_03A.cpp ================================================ // Defaulting the == operator import ; import ; import ; import ; import box; void show(const Box& box) { std::cout << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } void show(const Box& box1, std::string_view relationship, const Box& box2) { show(box1); std::cout << relationship; show(box2); std::cout << std::endl; } int main() { const std::vector boxes {Box {2.0, 1.5, 3.0}, Box {1.0, 3.0, 5.0}, Box {1.0, 2.0, 1.0}, Box {2.0, 3.0, 2.0}}; const Box theBox {3.0, 1.0, 4.0}; for (const auto& box : boxes) if (theBox > box) show(theBox, " is greater than ", box); // > works std::cout << std::endl; for (const auto& box : boxes) if (theBox != box) show(theBox, " is not equal to ", box); // != works std::cout << std::endl; for (const auto& box : boxes) if (6.0 <= box) // Yes, even double <= Box works!! { std::cout << "6 is less than or equal to "; show(box); std::cout << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_04/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream import ; export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; export std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_04/Ex13_04.cpp ================================================ // Overloading the << operator import ; import ; import ; import ; import box; int main() { const std::vector boxes {Box {2.0, 1.5, 3.0}, Box {1.0, 3.0, 5.0}, Box {1.0, 2.0, 1.0}, Box {2.0, 3.0, 2.0}}; const Box theBox {3.0, 1.0, 4.0}; for (const auto& box : boxes) if (theBox > box) std::cout << theBox << " is greater than " << box << std::endl; // > works std::cout << std::endl; for (const auto& box : boxes) if (theBox != box) std::cout << theBox << " is not equal to " << box << std::endl; // != works std::cout << std::endl; for (const auto& box : boxes) if (6.0 <= box) // Yes, even double <= Box works!! std::cout << "6 is less than or equal to " << box << std::endl; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_05/Box.cpp ================================================ module box; import ; import ; // For the min() and max() function templates double Box::volume() const { return m_length * m_width * m_height; } Box Box::operator+(const Box& aBox) const { // New object has larger length and width, and sum of heights return Box{ std::max(m_length, aBox.m_length), std::max(m_width, aBox.m_width), m_height + aBox.m_height }; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_05/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream export class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box operator+(const Box& aBox) const; // Function to add two Box objects private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; export std::ostream& operator<<(std::ostream& stream, const Box& box); ================================================ FILE: Examples/Modules/Chapter 13/Ex13_05/Ex13_05.cpp ================================================ // Using the addition operator for Box objects import ; import ; import ; import ; // For random number generation import ; // For std::bind() import box; // See Chapter 12 for an explanation of this function auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 1.0, max }; // Generate in [1, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const double limit {99}; // Upper limit on Box dimensions auto random { createUniformPseudoRandomNumberGenerator(limit) }; const size_t boxCount {20}; // Number of Box object to be created std::vector boxes; // Vector of Box objects // Create 20 Box objects for (size_t i {}; i < boxCount; ++i) boxes.push_back(Box{ random(), random(), random() }); size_t first {}; // Index of first Box object of pair size_t second {1}; // Index of second Box object of pair double minVolume {(boxes[first] + boxes[second]).volume()}; for (size_t i {}; i < boxCount - 1; ++i) { for (size_t j {i + 1}; j < boxCount; j++) { if (boxes[i] + boxes[j] < minVolume) { first = i; second = j; minVolume = (boxes[i] + boxes[j]).volume(); } } } std::cout << "The two boxes that sum to the smallest volume are " << boxes[first] << " and " << boxes[second] << '\n'; std::cout << std::format("The volume of the first box is {:.1f}\n", boxes[first].volume()); std::cout << std::format("The volume of the second box is {:.1f}\n", boxes[second].volume()); std::cout << "The sum of these boxes is " << (boxes[first] + boxes[second]) << '\n'; std::cout << std::format("The volume of the sum is {:.1f}", minVolume) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_06/Box.cpp ================================================ module; #include // For the min() and max() function templates module box; import ; double Box::volume() const { return m_length * m_width * m_height; } // Overloaded += operator Box& Box::operator+=(const Box& aBox) { // New object has larger length and width, and sum of heights m_length = std::max(m_length, aBox.m_length); m_width = std::max(m_width, aBox.m_width); m_height += aBox.m_height; return *this; } // Function to add two Box objects Box Box::operator+(const Box& aBox) const { Box copy{ *this }; copy += aBox; return copy; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_06/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream export class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box& operator+=(const Box& aBox); // Function to add a Box objects Box operator+(const Box& aBox) const; // Function to add two Box objects private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; export std::ostream& operator<<(std::ostream& stream, const Box& box); ================================================ FILE: Examples/Modules/Chapter 13/Ex13_06/Ex13_06.cpp ================================================ // Using the addition operator for Box objects import ; import ; import ; import ; // For random number generation import ; // For std::bind() import box; // See Chapter 12 for an explanation of this function auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 1.0, max }; // Generate in [1, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const double limit {99}; // Upper limit on Box dimensions auto random { createUniformPseudoRandomNumberGenerator(limit) }; const size_t boxCount {20}; // Number of Box object to be created std::vector boxes; // Vector of Box objects // Create 20 Box objects for (size_t i {}; i < boxCount; ++i) boxes.push_back(Box{ random(), random(), random() }); size_t first {}; // Index of first Box object of pair size_t second {1}; // Index of second Box object of pair double minVolume {(boxes[first] + boxes[second]).volume()}; for (size_t i {}; i < boxCount - 1; ++i) { for (size_t j {i + 1}; j < boxCount; j++) { if (boxes[i] + boxes[j] < minVolume) { first = i; second = j; minVolume = (boxes[i] + boxes[j]).volume(); } } } std::cout << "The two boxes that sum to the smallest volume are " << boxes[first] << " and " << boxes[second] << '\n'; std::cout << std::format("The volume of the first box is {:.1f}\n", boxes[first].volume()); std::cout << std::format("The volume of the second box is {:.1f}\n", boxes[second].volume()); std::cout << "The sum of these boxes is " << (boxes[first] + boxes[second]) << '\n'; std::cout << std::format("The volume of the sum is {:.1f}", minVolume) << std::endl; Box sum{ 0, 0, 0 }; // Start from an empty Box for (const auto& box : boxes) // And then add all randomly generated Box objects sum += box; std::cout << "The sum of " << boxCount << " random boxes is " << sum << std::endl; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_07/Ex13_07.cpp ================================================ // Implicit conversions reduce the number of operator functions import ; import integer; int main() { const Integer i{1}; const Integer j{2}; const auto result = (i * 2 + 4 / j - 1) % j; std::cout << result.getValue() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_07/Integer.cppm ================================================ export module integer; export class Integer { public: Integer(int value = 0) : m_value{value} {} int getValue() const { return m_value; } void setValue(int value) { m_value = value; } private: int m_value; }; export Integer operator+(const Integer& one, const Integer& other) { return one.getValue() + other.getValue(); } export Integer operator-(const Integer& one, const Integer& other) { return one.getValue() - other.getValue(); } export Integer operator*(const Integer& one, const Integer& other) { return one.getValue() * other.getValue(); } export Integer operator/(const Integer& one, const Integer& other) { return one.getValue() / other.getValue(); } export Integer operator%(const Integer& one, const Integer& other) { return one.getValue() % other.getValue(); } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_08/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream import ; export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} Box operator~() const { return Box{m_width, m_length, m_height}; // Width and length are swapped } double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; export std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_08/Ex13_08.cpp ================================================ // Overloading a unary "rotate" operator import ; import box; int main() { Box someBox{ 1, 2, 3 }; std::cout << ~someBox << std::endl; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_09/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream import ; export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} Box& operator++(); // Prefix ++operator const Box operator++(int); // Postfix operator++ Box& operator--(); // Prefix --operator const Box operator--(int); // Postfix operator-- double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; Box& Box::operator++() // Prefix ++operator { ++m_length; ++m_width; ++m_height; return *this; } const Box Box::operator++(int) // Postfix operator++ { auto copy{ *this }; // Create a copy of the current object ++(*this); // Increment the current object using the prefix operator... return copy; // Return the unincremented copy } Box& Box::operator--() // Prefix --operator { --m_length; --m_width; --m_height; return *this; } const Box Box::operator--(int) // Postfix operator-- { auto copy{ *this }; // Create a copy of the current object --(*this); // Decrement the current object using the prefix operator... return copy; // Return copy of the original value } export std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_09/Ex13_09.cpp ================================================ // Overloading pre- and postfix inrement and decrement operators import ; import box; int main() { Box theBox{ 3.0, 1.0, 3.0 }; std::cout << "Our test Box is " << theBox << std::endl; std::cout << "Postfix increment evaluates to the original object: " << theBox++ << std::endl; std::cout << "After postfix increment: " << theBox << std::endl; std::cout << "Prefix decrement evaluates to the decremented object: " << --theBox << std::endl; std::cout << "After prefix decrement: " << theBox << std::endl; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_10/Box.cppm ================================================ export module box; import ; import ; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f},{:.1f},{:.1f})", box.m_length, box.m_width, box.m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Examples/Modules/Chapter 13/Ex13_10/Ex13_10.cpp ================================================ // Using the subscript operator import ; import ; import ; // For random number generation import ; // For std::bind() import truckload; // See Chapter 12 for an explanation of this function auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 1.0, max }; // Generate in [1, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const double limit {99.0}; // Upper limit on Box dimensions auto random = createUniformPseudoRandomNumberGenerator(limit); Truckload load; const size_t boxCount {16}; // Number of Box object to be created // Create boxCount Box objects for (size_t i {}; i < boxCount; ++i) load.addBox(std::make_shared(random(), random(), random())); std::cout << "The boxes in the Truckload are:\n"; std::cout << load; // Find the largest Box in the Truckload double maxVolume {}; size_t maxIndex {}; size_t i {}; while (load[i]) { if (load[i]->volume() > maxVolume) { maxIndex = i; maxVolume = load[i]->volume(); } ++i; } std::cout << "\nThe largest box is: "; std::cout << *load[maxIndex] << std::endl; load.removeBox(load[maxIndex]); std::cout << "\nAfter deleting the largest box, the Truckload contains:\n"; std::cout << load; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_10/Truckload.cpp ================================================ module truckload; import ; // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } return nullptr; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_10/Truckload.cppm ================================================ export module truckload; import box; import ; import ; import ; export using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; export std::ostream& operator<<(std::ostream& stream, const Truckload& load); ================================================ FILE: Examples/Modules/Chapter 13/Ex13_11/Box.cppm ================================================ export module box; import ; import ; import ; // For the std::min()/max() function templates export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f},{:.1f},{:.1f})", box.m_length, box.m_width, box.m_height); } Box operator+(const Box& aBox) const // Function to add two Box objects { return Box{ std::max(m_length, aBox.m_length), std::max(m_width, aBox.m_width), m_height + aBox.m_height }; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Examples/Modules/Chapter 13/Ex13_11/Ex13_11.cpp ================================================ // Modifying the result of an overloaded subscript operator import ; import ; import ; // For random number generation import ; // For std::bind() import truckload; /* Caution: in the text, we suggest to add static SharedBox nullBox{}; to the Truckload class definition. This will not compile. In-class definitions of non-const static members are only allowed if you add the inline keyword, as we did in this solution. See Chapter 12 for more explanation, and for the alternative of defining the member out-of-class. */ // See Chapter 12 for an explanation of this function auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 1.0, max }; // Generate in [1, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const double limit {99.0}; // Upper limit on Box dimensions auto random = createUniformPseudoRandomNumberGenerator(limit); Truckload load; const size_t boxCount {16}; // Number of Box object to be created // Create boxCount Box objects for (size_t i {}; i < boxCount; ++i) load.addBox(std::make_shared(random(), random(), random())); std::cout << "The boxes in the Truckload are:\n"; std::cout << load; // Find the largest Box in the Truckload double maxVolume {}; size_t maxIndex {}; size_t i {}; while (load[i]) { if (load[i]->volume() > maxVolume) { maxIndex = i; maxVolume = load[i]->volume(); } ++i; } std::cout << "\nThe largest box is: "; std::cout << *load[maxIndex] << std::endl; load.removeBox(load[maxIndex]); std::cout << "\nAfter deleting the largest box, the Truckload contains:\n"; std::cout << load; load[0] = load[1]; // Copy 2nd element to the 1st std::cout << "\nAfter copying the 2nd element to the 1st, the list contains:\n"; std::cout << load; load[1] = std::make_shared(*load[2] + *load[3]); std::cout << "\nAfter making the 2nd element a pointer to the 3rd plus 4th," " the list contains:\n"; std::cout << load; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_11/Truckload.cpp ================================================ module truckload; import ; // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox& Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } return nullBox; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_11/Truckload.cppm ================================================ export module truckload; import box; import ; import ; import ; export using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list // Caution: inline is required to allow for this definition to appear in-class. // In the text we forgot to add this. See Chapter 12 for more explanation, // and for the alternative of defining the member out-of-class. static inline SharedBox nullBox{}; // Pointer to nullptr }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; export std::ostream& operator<<(std::ostream& stream, const Truckload& load); ================================================ FILE: Examples/Modules/Chapter 13/Ex13_12/Ex13_12.cpp ================================================ // Defining a copy assignment operator import message; import ; int main() { Message beware{ "Careful" }; Message warning; warning = beware; // Call assignment operator std::cout << "After assignment beware is: " << beware.getText() << std::endl; std::cout << "After assignment warning is: " << warning.getText() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_12/Message.cpp ================================================ module; #include // For std::strlen() and std::strcpy() module message; Message& Message::operator=(const Message& message) { if (&message != this) { delete[] m_text; // Delete the previous char array m_text = new char[std::strlen(message.m_text) + 1]; // Replace it with a new array std::strcpy(m_text, message.m_text); // Copy the text (mind the order!) } return *this; // Return the left operand } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_12/Message.cppm ================================================ module; #include // For std::strlen() and std::strcpy() export module message; export class Message { public: explicit Message(const char* text = "") : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! { std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! } ~Message() { delete[] m_text; } Message& operator=(const Message& message); // Assignment operator const char* getText() const { return m_text; } private: char* m_text; }; ================================================ FILE: Examples/Modules/Chapter 13/Ex13_12A/Ex13_12A.cpp ================================================ // Always define both copy members together // (see also 'Rule of Five' in Chapter 18!) import message; import ; int main() { Message beware{ "Careful" }; Message warning; warning = beware; // Call assignment operator Message caution{ warning }; std::cout << "After assignment beware is: " << beware.getText() << std::endl; std::cout << "After assignment warning is: " << warning.getText() << std::endl; std::cout << "As a copy of warning, caution is: " << caution.getText() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_12A/Message.cpp ================================================ module; #include // For std::strlen() and std::strcpy() module message; Message::Message(const Message& message) : Message{ message.m_text } // By far easiest and preferred option: forward to existing constructor! { } Message& Message::operator=(const Message& message) { if (&message != this) { delete[] m_text; // Delete the previous char array m_text = new char[std::strlen(message.m_text) + 1]; // Replace it with a new array std::strcpy(m_text, message.m_text); // Copy the text (mind the order!) } return *this; // Return the left operand } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_12A/Message.cppm ================================================ module; #include // For std::strlen() and std::strcpy() export module message; export class Message { public: explicit Message(const char* text = "") : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! { std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! } ~Message() { delete[] m_text; } Message(const Message& message); // Copy constructor Message& operator=(const Message& message); // Copy assignment operator const char* getText() const { return m_text; } private: char* m_text; }; ================================================ FILE: Examples/Modules/Chapter 13/Ex13_12B/Ex13_12B.cpp ================================================ // Sneak preview: use copy-and-swap for copy assignment operator // (see Chapter 17 for details, including the noexcept keyword...) // In short, you eliminate duplicating the logic of copying an object // by expressing the copy assignment operator in terms of the copy constructor. // Less duplication means less room for error, less maintenance overhead, etc. // Note: this solution is hinted at in a Note in the text, but not explicitly named. import message; import ; int main() { Message beware{ "Careful" }; Message warning; warning = beware; // Call assignment operator Message caution{ warning }; std::cout << "After assignment beware is: " << beware.getText() << std::endl; std::cout << "After assignment warning is: " << warning.getText() << std::endl; std::cout << "As a copy of warning, caution is: " << caution.getText() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_12B/Message.cpp ================================================ module message; import ; // For std::swap() Message::Message(const Message& message) : Message{ message.m_text } // By far easiest and preferred option: forward to existing constructor! { } Message& Message::operator=(const Message& message) { // Note: self-assignment test no longer required (or even recommended) auto copy{ message }; // Copy-... swap(copy); // ...and-swap! return *this; // Always return reference to left operand } void Message::swap(Message& other) noexcept { std::swap(m_text, other.m_text); } ================================================ FILE: Examples/Modules/Chapter 13/Ex13_12B/Message.cppm ================================================ module; #include // For std::strlen() and std::strcpy() export module message; export class Message { public: explicit Message(const char* text = "") : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! { std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! } ~Message() { delete[] m_text; } Message(const Message& message); // Copy constructor Message& operator=(const Message& message); // Copy assignment operator void swap(Message& other) noexcept; const char* getText() const { return m_text; } private: char* m_text; }; export void swap(Message& one, Message& other) noexcept { return one.swap(other); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_01/Box.cppm ================================================ // Box.cppm - defines Box class export module box; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_01/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as base export module carton; import ; import ; import box; export class Carton : public Box { public: explicit Carton(std::string_view material = "Cardboard") // Constructor : m_material{material} {} private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_01/Ex14_01.cpp ================================================ // Defining and using a derived class import ; import box; // For the Box class import carton; // For the Carton class int main() { // Create a Box object and two Carton objects Box box {40.0, 30.0, 20.0}; Carton carton; Carton chocolateCarton {"Solid bleached board"}; // Good old SBB // Check them out - sizes first of all std::cout << "box occupies " << sizeof box << " bytes" << std::endl; std::cout << "carton occupies " << sizeof carton << " bytes" << std::endl; std::cout << "candyCarton occupies " << sizeof chocolateCarton << " bytes" << std::endl; // Now volumes... std::cout << "box volume is " << box.volume() << std::endl; std::cout << "carton volume is " << carton.volume() << std::endl; std::cout << "chocolateCarton volume is " << chocolateCarton.volume() << std::endl; std::cout << "chocolateCarton length is " << chocolateCarton.getLength() << std::endl; // Uncomment any of the following for an error... // box.m_length = 10.0; // chocolateCarton.m_length = 10.0; } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_01A/Box.cppm ================================================ // Box.cppm - defines Box class export module box; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_01A/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as private base export module carton; import ; import ; import box; export class Carton : private Box { public: explicit Carton(std::string_view mat = "Cardboard") : m_material {mat} {} using Box::volume; // Inherit as public private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_01A/Ex14_01A.cpp ================================================ // Changing the access specification of inherited members using using import ; import box; // For the Box class import carton; // For the Carton class int main() { // Create a Box object and two Carton objects Box box {40.0, 30.0, 20.0}; Carton carton; Carton chocolateCarton {"Solid bleached board"}; // Good old SBB // Check them out - sizes first of all std::cout << "box occupies " << sizeof box << " bytes" << std::endl; std::cout << "carton occupies " << sizeof carton << " bytes" << std::endl; std::cout << "candyCarton occupies " << sizeof chocolateCarton << " bytes" << std::endl; // Now volumes... std::cout << "box volume is " << box.volume() << std::endl; std::cout << "carton volume is " << carton.volume() << std::endl; std::cout << "chocolateCarton volume is " << chocolateCarton.volume() << std::endl; // Uncomment any of the following for an error... // std::cout << "chocolateCarton length is " << chocolateCarton.getLength() << std::endl; // // box.m_length = 10.0; // chocolateCarton.m_length = 10.0; } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_02/Box.cppm ================================================ // Box.cppm - defines Box class export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_02/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as base export module carton; import ; import ; import ; import box; export class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } /* // This constructor won't compile! Carton::Carton(double l, double w, double h, std::string_view material) : m_length{ l }, m_width{ w }, m_height{ h }, m_material{ material } { std::cout << "Carton(double,double,double,string_view) called.\n"; } */ /* // Constructor that will compile! Carton::Carton(double l, double w, double h, std::string_view material) : m_material{material} { m_length = l; // These should normally be initialized in a base class constructor... m_width = w; m_height = h; std::cout << "Carton(double,double,double,string_view) called.\n"; } */ private: std::string m_material {"Cardboard"}; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_02/Ex14_02.cpp ================================================ // Calling base class constructors in a derived class constructor import ; import carton; // For the Carton class int main() { // Create four Carton objects Carton carton1; std::cout << std::endl; Carton carton2 {"White-lined chipboard"}; std::cout << std::endl; Carton carton3 {4.0, 5.0, 6.0, "PET"}; std::cout << std::endl; Carton carton4 {2.0, "Folding boxboard"}; std::cout << std::endl; std::cout << "carton1 volume is " << carton1.volume() << std::endl; std::cout << "carton2 volume is " << carton2.volume() << std::endl; std::cout << "carton3 volume is " << carton3.volume() << std::endl; std::cout << "carton4 volume is " << carton4.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_03/Box.cppm ================================================ // Box.cppm - defines Box class (with additional copy constuctor) export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor // Copy constructor Box(const Box& b) : m_length{ b.m_length }, m_width{ b.m_width }, m_height{ b.m_height } { std::cout << "Box copy constructor" << std::endl; } double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_03/Carton.cppm ================================================ // Carton.cppm - defines the Carton class (with additional copy constuctor) export module carton; import ; import ; import ; import box; export class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } // Copy constructor (wrong) Carton(const Carton& carton) : m_material {carton.m_material} { std::cout << "Carton copy constructor" << std::endl; } /* // Copy constructor (correct) Carton(const Carton& carton) : Box{ carton }, m_material{ carton.m_material } { std::cout << "Carton copy constructor" << std::endl; } */ private: std::string m_material {"Cardboard"}; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_03/Ex14_03.cpp ================================================ // Using a derived class copy constructor import ; import carton; int main() { // Declare and initialize a Carton object Carton carton{ 20.0, 30.0, 40.0, "Expanded polystyrene" }; std::cout << std::endl; Carton cartonCopy{ carton }; // Use copy constructor std::cout << std::endl; std::cout << "Volume of carton is " << carton.volume() << std::endl << "Volume of cartonCopy is " << cartonCopy.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04/Box.cppm ================================================ // Box.cppm - defines Box class export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as base export module carton; import ; import ; import ; import box; export class Carton : public Box { public: Carton() = default; Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } private: std::string m_material {"Cardboard"}; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04/Ex14_04.cpp ================================================ // Defaulting the default constructor in a derived class (working version) import ; import box; // For the Box class import carton; // For the Carton class int main() { // Create three Carton objects Carton carton1; std::cout << std::endl; Carton carton2 {4.0, 5.0, 6.0, "PET"}; std::cout << std::endl; Carton carton3 {2.0, "Folding boxboard"}; std::cout << std::endl; std::cout << "carton1 volume is " << carton1.volume() << std::endl; std::cout << "carton2 volume is " << carton2.volume() << std::endl; std::cout << "carton3 volume is " << carton3.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04A/Box.cppm ================================================ // Box.cppm - defines Box class export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } // Box() { std::cout << "Box() called.\n"; } // Default constructor removed! double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04A/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as base export module carton; import ; import ; import ; import box; export class Carton : public Box { public: Carton() = default; Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } private: std::string m_material {"Cardboard"}; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04A/Ex14_04A.cpp ================================================ // Defaulting the default constructor in a derived class // without a default constructor in the base class // is equivalent to deleting the default constructor. import ; import box; // For the Box class import carton; // For the Carton class /* Note: this example will fail to compile! */ int main() { // Create three Carton objects Carton carton1; std::cout << std::endl; Carton carton2 {4.0, 5.0, 6.0, "PET"}; std::cout << std::endl; Carton carton3 {2.0, "Folding boxboard"}; std::cout << std::endl; std::cout << "carton1 volume is " << carton1.volume() << std::endl; std::cout << "carton2 volume is " << carton2.volume() << std::endl; std::cout << "carton3 volume is " << carton3.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04B/Box.cppm ================================================ // Box.cppm - defines Box class export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() = delete; // Default constructor explicitly deleted double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04B/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as base export module carton; import ; import ; import ; import box; export class Carton : public Box { public: Carton() = default; Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } private: std::string m_material {"Cardboard"}; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04B/Ex14_04B.cpp ================================================ // Defaulting the default constructor in a derived class // with a deleted default constructor in the base class // is equivalent to deleting the default constructor. import ; import box; // For the Box class import carton; // For the Carton class /* Note: this example will fail to compile! */ int main() { // Create three Carton objects Carton carton1; std::cout << std::endl; Carton carton2 {4.0, 5.0, 6.0, "PET"}; std::cout << std::endl; Carton carton3 {2.0, "Folding boxboard"}; std::cout << std::endl; std::cout << "carton1 volume is " << carton1.volume() << std::endl; std::cout << "carton2 volume is " << carton2.volume() << std::endl; std::cout << "carton3 volume is " << carton3.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04C/Box.cppm ================================================ // Box.cppm - defines Box class export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } private: Box() { std::cout << "Box() called.\n"; } // Default constructor (made private) double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04C/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as base export module carton; import ; import ; import ; import box; export class Carton : public Box { public: Carton() = default; Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } private: std::string m_material {"Cardboard"}; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_04C/Ex14_04C.cpp ================================================ // Defaulting the default constructor in a derived class // with a private constructor in the base class // is equivalent to deleting the default constructor. import ; import box; // For the Box class import carton; // For the Carton class /* Note: this example will fail to compile! */ int main() { // Create three Carton objects Carton carton1; std::cout << std::endl; Carton carton2 {4.0, 5.0, 6.0, "PET"}; std::cout << std::endl; Carton carton3 {2.0, "Folding boxboard"}; std::cout << std::endl; std::cout << "carton1 volume is " << carton1.volume() << std::endl; std::cout << "carton2 volume is " << carton2.volume() << std::endl; std::cout << "carton3 volume is " << carton3.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_05/Box.cppm ================================================ // Box.cppm - defines Box class export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_05/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as base export module carton; import ; import ; import ; import box; export class Carton : public Box { using Box::Box; // Inherit Box class constructors public: Carton() = default; // Required by Visual C++ Carton(double length, double width, double height, std::string_view mat) : Box{length, width, height}, m_material{mat} { std::cout << "Carton(double,double,double,string_view) called.\n"; } private: std::string m_material {"Cardboard"}; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_05/Ex14_05.cpp ================================================ // Inheriting constructors import ; import carton; // For the Carton class int main() { Carton cart; // Calls inherited default constructor Carton cube { 4.0 }; // Calls inherited constructor Carton copy { cube }; // Calls default copy constructor Carton carton {1.0, 2.0, 3.0}; // Calls inherited constructor Carton cerealCarton (50.0, 30.0, 20.0, "Chipboard"); // Calls Carton class constructor } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_06/Box.cppm ================================================ // Box.cppm - defines Box class export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor // Copy constructor Box(const Box& b) : m_length{ b.m_length }, m_width{ b.m_width }, m_height{ b.m_height } { std::cout << "Box copy constructor" << std::endl; } // Destructor ~Box() { std::cout << "Box destructor" << std::endl; } double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_06/Carton.cppm ================================================ // Carton.cppm - defines the Carton class export module carton; import ; import ; import ; import box; export class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } // Copy constructor (correct) Carton(const Carton& carton) : Box{ carton }, m_material{ carton.m_material } { std::cout << "Carton copy constructor" << std::endl; } // Destructor ~Carton() { std::cout << "Carton destructor. Material = " << m_material << std::endl; } private: std::string m_material {"Cardboard"}; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_06/Ex14_06.cpp ================================================ // Destructors in a class hierarchy import ; import carton; // For the Carton class int main() { Carton carton; Carton candyCarton{50.0, 30.0, 20.0, "SBB"}; // Solid bleached board std::cout << "carton volume is " << carton.volume() << std::endl; std::cout << "candyCarton volume is " << candyCarton.volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07/Box.cppm ================================================ // Box.cppm - defines Box class export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as base export module carton; import ; import ; import ; import box; export class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } // One new constructor Carton(double l, double w, double h, std::string_view m, double density, double thickness) : Carton{l, w, h, m} { m_thickness = thickness; m_density = density; std::cout << "Carton(double,double,double,string_view,double,double) called.\n"; } // Copy constructor Carton(const Carton& carton) : Box{carton}, m_material{carton.m_material}, m_thickness{carton.m_thickness}, m_density{carton.m_density} { std::cout << "Carton copy constructor" << std::endl; } // Destructor ~Carton() { std::cout << "Carton destructor. Material = " << m_material << std::endl; } double getWeight() const { return 2.0 * (m_length * m_width + m_width * m_height + m_height * m_length) * m_thickness * m_density; } private: std::string m_material {"Cardboard"}; double m_thickness {0.125}; // Material thickness in inch double m_density {0.2}; // Material density in pounds/cubic inch }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07/CerealPack.cppm ================================================ // Cerealpack.cppm - Class defining a carton of cereal export module cereal; import ; import carton; import food; export class CerealPack : public Carton, public FoodContainer { public: CerealPack(double length, double width, double height, std::string_view cerealType) : Carton {length, width, height, "Chipboard"}, FoodContainer {cerealType} { std::cout << "CerealPack constructor" << std::endl; FoodContainer::volume = 0.9 * Carton::volume(); // Set food container's volume } ~CerealPack() { std::cout << "CerealPack destructor" << std::endl; } }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07/Ex14_07.cpp ================================================ // Ex14_07 - doesn't compile! // Using multiple inheritance can lead to ambiguity if members with // the same name are inherited from different base classes. import ; import cereal; // For the CerealPack class int main() { CerealPack cornflakes{ 8.0, 3.0, 10.0, "Cornflakes" }; std::cout << "cornflakes volume is " << cornflakes.volume() << std::endl << "cornflakes weight is " << cornflakes.getWeight() << std::endl; /* // Here is one way to make this work (see Ex14_07A and B for alternatives) std::cout << "cornflakes volume is " << cornflakes.Carton::volume() << std::endl << "cornflakes weight is " << cornflakes.FoodContainer::getWeight() << std::endl; */ } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07/FoodContainer.cppm ================================================ export module food; import ; import ; import ; export class FoodContainer { public: FoodContainer() { std::cout << "FoodContainer() called.\n"; } FoodContainer(std::string_view name) : name {name} { std::cout << "FoodContainer(string_view) called.\n"; } FoodContainer(std::string_view name, double density, double volume) : name {name}, density {density}, volume {volume} { std::cout << "FoodContainer(string_view,double,double) called.\n"; } ~FoodContainer() { std::cout << "FoodContainer destructor" << std::endl; } double getWeight() const { return volume * density; } protected: // Protected to make initialization in CerealPack constructor work (book says private) std::string name {"cereal"}; // Food type double volume {}; // Cubic inches double density {0.03}; // Pounds per cubic inch }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07A/Box.cppm ================================================ // Box.cppm - defines Box class export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07A/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as base export module carton; import ; import ; import ; import box; export class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } // One new constructor Carton(double l, double w, double h, std::string_view m, double density, double thickness) : Carton{l, w, h, m} { m_thickness = thickness; m_density = density; std::cout << "Carton(double,double,double,string_view,double,double) called.\n"; } // Copy constructor Carton(const Carton& carton) : Box{carton}, m_material{carton.m_material}, m_thickness{carton.m_thickness}, m_density{carton.m_density} { std::cout << "Carton copy constructor" << std::endl; } // Destructor ~Carton() { std::cout << "Carton destructor. Material = " << m_material << std::endl; } double getWeight() const { return 2.0 * (m_length * m_width + m_width * m_height + m_height * m_length) * m_thickness * m_density; } private: std::string m_material {"Cardboard"}; double m_thickness {0.125}; // Material thickness in inch double m_density {0.2}; // Material density in pounds/cubic inch }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07A/CerealPack.cppm ================================================ // Cerealpack.cppm - Class defining a carton of cereal export module cereal; import ; import carton; import food; export class CerealPack : public Carton, public FoodContainer { public: CerealPack(double length, double width, double height, std::string_view cerealType) : Carton {length, width, height, "Chipboard"}, FoodContainer {cerealType} { std::cout << "CerealPack constructor" << std::endl; FoodContainer::volume = 0.9 * Carton::volume(); // Set food container's volume } ~CerealPack() { std::cout << "CerealPack destructor" << std::endl; } }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07A/Ex14_07A.cpp ================================================ // Ex14_07A - Disambiguating ambiguous member inheritance through casting import ; import cereal; // For the CerealPack class int main() { CerealPack cornflakes{ 8.0, 3.0, 10.0, "Cornflakes" }; std::cout << "cornflakes volume is " << static_cast(cornflakes).volume() << std::endl << "cornflakes weight is " << static_cast(cornflakes).getWeight() << std::endl; /* // Alternate solution by explicitly qualifying the base class name (see also Ex14_07) std::cout << "cornflakes volume is " << cornflakes.Carton::volume() << std::endl << "cornflakes weight is " << cornflakes.FoodContainer::getWeight() << std::endl; */ } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07A/FoodContainer.cppm ================================================ export module food; import ; import ; import ; export class FoodContainer { public: FoodContainer() { std::cout << "FoodContainer() called.\n"; } FoodContainer(std::string_view name) : name {name} { std::cout << "FoodContainer(string_view) called.\n"; } FoodContainer(std::string_view name, double density, double volume) : name {name}, density {density}, volume {volume} { std::cout << "FoodContainer(string_view,double,double) called.\n"; } ~FoodContainer() { std::cout << "FoodContainer destructor" << std::endl; } double getWeight() const { return volume * density; } protected: // Protected to make initialization in CerealPack constructor work (book says private) std::string name {"cereal"}; // Food type double volume {}; // Cubic inches double density {0.03}; // Pounds per cubic inch }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07B/Box.cppm ================================================ // Box.cppm - defines Box class export module box; import ; // For standard streams import ; // For string formatting export class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects export std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07B/Carton.cppm ================================================ // Carton.cppm - defines the Carton class with the Box class as base export module carton; import ; import ; import ; import box; export class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } // One new constructor Carton(double l, double w, double h, std::string_view m, double density, double thickness) : Carton{l, w, h, m} { m_thickness = thickness; m_density = density; std::cout << "Carton(double,double,double,string_view,double,double) called.\n"; } // Copy constructor Carton(const Carton& carton) : Box{carton}, m_material{carton.m_material}, m_thickness{carton.m_thickness}, m_density{carton.m_density} { std::cout << "Carton copy constructor" << std::endl; } // Destructor ~Carton() { std::cout << "Carton destructor. Material = " << m_material << std::endl; } double getWeight() const { return 2.0 * (m_length * m_width + m_width * m_height + m_height * m_length) * m_thickness * m_density; } private: std::string m_material {"Cardboard"}; double m_thickness {0.125}; // Material thickness in inch double m_density {0.2}; // Material density in pounds/cubic inch }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07B/CerealPack.cppm ================================================ // Cerealpack.cppm - Class defining a carton of cereal export module cereal; import ; import carton; import food; export class CerealPack : public Carton, public FoodContainer { public: CerealPack(double length, double width, double height, std::string_view cerealType) : Carton {length, width, height, "Chipboard"}, FoodContainer {cerealType} { std::cout << "CerealPack constructor" << std::endl; FoodContainer::volume = 0.9 * Carton::volume(); // Set food container's volume } ~CerealPack() { std::cout << "CerealPack destructor" << std::endl; } using Carton::volume; using FoodContainer::getWeight; }; ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07B/Ex14_07B.cpp ================================================ // Ex14_07B - Disambiguating ambiguous member through // using declarations in the derived class (CerealPack) import ; import cereal; // For the CerealPack class int main() { CerealPack cornflakes{ 8.0, 3.0, 10.0, "Cornflakes" }; std::cout << "cornflakes volume is " << cornflakes.volume() << std::endl << "cornflakes weight is " << cornflakes.getWeight() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 14/Ex14_07B/FoodContainer.cppm ================================================ export module food; import ; import ; import ; export class FoodContainer { public: FoodContainer() { std::cout << "FoodContainer() called.\n"; } FoodContainer(std::string_view name) : name {name} { std::cout << "FoodContainer(string_view) called.\n"; } FoodContainer(std::string_view name, double density, double volume) : name {name}, density {density}, volume {volume} { std::cout << "FoodContainer(string_view,double,double) called.\n"; } ~FoodContainer() { std::cout << "FoodContainer destructor" << std::endl; } double getWeight() const { return volume * density; } protected: // Protected to make initialization in CerealPack constructor work (book says private) std::string name {"cereal"}; // Food type double volume {}; // Cubic inches double density {0.03}; // Pounds per cubic inch }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_01/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_01/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_01/Ex15_01.cpp ================================================ // Behavior of inherited functions in a derived class import boxes; int main() { Box box {20.0, 30.0, 40.0}; // Create a box ToughPack hardcase {20.0, 30.0, 40.0}; // Create a tough pack - same size box.showVolume(); // Display volume of base box (calls volume() for box) hardcase.showVolume(); // Display volume of derived box (call volume() for hardcase) //std::cout << "hardcase volume is " << hardcase.volume() << std::endl; //Box* box_pointer{ &hardcase }; //std::cout << "hardcase volume through a Box* pointer is " // << box_pointer->volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_01/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; export class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const { return 0.85 * m_length * m_width * m_height; } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_02/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_02/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; export import :carton; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_02/Carton.cppm ================================================ export module boxes:carton; import ; // For std::max() import ; import ; import :box; export class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_02/Ex15_02.cpp ================================================ // Using virtual functions import ; import boxes; int main() { Box box {20.0, 30.0, 40.0}; ToughPack hardcase {20.0, 30.0, 40.0}; // A derived box - same size Carton carton {20.0, 30.0, 40.0, "Plastic"}; // A different derived box box.showVolume(); // Volume of Box hardcase.showVolume(); // Volume of ToughPack carton.showVolume(); // Volume of Carton // Now using a base pointer... Box* base {&box}; // Points to type Box std::cout << "\nbox volume through base pointer is " << base->volume() << std::endl; base ->showVolume(); base = &hardcase; // Points to type ToughPack std::cout << "hardcase volume through base pointer is " << base->volume() << std::endl; base->showVolume(); base = &carton; // Points to type Carton std::cout << "carton volume through base pointer is " << base->volume() << std::endl; base->showVolume(); } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_02/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; export class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const { return 0.85 * m_length * m_width * m_height; } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_03/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_03/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; export import :carton; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_03/Carton.cppm ================================================ export module boxes:carton; import ; // For std::max() import ; import ; import :box; export class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_03/Ex15_03.cpp ================================================ // Access specifiers and virtual functions import ; import boxes; int main() { Box box {20.0, 30.0, 40.0}; ToughPack hardcase {20.0, 30.0, 40.0}; // A derived box - same size Carton carton {20.0, 30.0, 40.0, "Plastic"}; // A different derived box box.showVolume(); // Volume of Box hardcase.showVolume(); // Volume of ToughPack carton.showVolume(); // Volume of Carton // Uncomment the following statement for an error // std::cout << "\nhardcase volume is " << hardcase.volume() << std::endl; // Now using a base pointer... Box* base {&box}; // Points to type Box std::cout << "\nbox volume through base pointer is " << base->volume() << std::endl; base->showVolume(); base = &hardcase; // Points to type ToughPack std::cout << "hardcase volume through base pointer is " << base->volume() << std::endl; base->showVolume(); base = &carton; // Points to type Carton std::cout << "carton volume through base pointer is " << base->volume() << std::endl; base->showVolume(); } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_03/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; export class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_04/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume(int i = 5) const { std::cout << "(Box argument = " << i << ") "; return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_04/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; export import :carton; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_04/Carton.cppm ================================================ export module boxes:carton; import ; // For std::max() import ; import ; import :box; export class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume(int i = 50) const override { std::cout << "(Carton argument = " << i << ") "; return std::max((m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5), 0.0); } private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_04/Ex15_04.cpp ================================================ // Default parameter values in virtual functions import ; import boxes; int main() { Box box{ 20.0, 30.0, 40.0 }; ToughPack hardcase{ 20.0, 30.0, 40.0 }; // A derived box - same size Carton carton{ 20.0, 30.0, 40.0, "Plastic" }; // A different derived box box.showVolume(); // Volume of Box hardcase.showVolume(); // Volume of ToughPack carton.showVolume(); // Volume of Carton std::cout << "\nhardcase volume is " << hardcase.volume() << std::endl; // Now using a base pointer... Box* base{ &box }; // Points to type Box std::cout << "\nbox volume through base pointer is " << base->volume() << std::endl; base->showVolume(); base = &hardcase; // Points to type ToughPack std::cout << "hardcase volume through base pointer is " << base->volume() << std::endl; base->showVolume(); base = &carton; // Points to type Carton std::cout << "carton volume through base pointer is " << base->volume() << std::endl; base->showVolume(); } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_04/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; export class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; // Function to calculate volume of a ToughPack allowing 15% for packing double volume(int i = 500) const override { std::cout << "(ToughPack argument = " << i << ") "; return 0.85 * m_length * m_width * m_height; } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_05/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_05/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; export import :carton; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_05/Carton.cppm ================================================ export module boxes:carton; import ; // For std::max() import ; import ; import :box; export class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_05/Ex15_05.cpp ================================================ // Using a reference parameter to call virtual function import ; import boxes; // Global function to display the volume of a box void showVolume(const Box& box) { std::cout << "Box usable volume is " << box.volume() << std::endl; } int main() { Box box {20.0, 30.0, 40.0}; // A base box ToughPack hardcase {20.0, 30.0, 40.0}; // A derived box - same size Carton carton {20.0, 30.0, 40.0, "Plastic"}; // A different derived box showVolume(box); // Display volume of base box showVolume(hardcase); // Display volume of derived box showVolume(carton); // Display volume of derived box } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_05/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; export class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_06/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_06/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; export import :carton; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_06/Carton.cppm ================================================ export module boxes:carton; import ; // For std::max() import ; import ; import :box; export class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_06/Ex15_06.cpp ================================================ // Polymorphic vectors of smart pointers import ; import ; // For smart pointers import ; // For vector import boxes; int main() { // Careful: this first attempt at a mixed collection is a bad idea (object slicing!) std::vector boxes; boxes.push_back(Box{20.0, 30.0, 40.0}); boxes.push_back(ToughPack{20.0, 30.0, 40.0}); boxes.push_back(Carton{20.0, 30.0, 40.0, "plastic"}); for (const auto& box : boxes) box.showVolume(); std::cout << std::endl; // Next, we create a proper polymorphic vector<>: std::vector> polymorphicBoxes; polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0, "plastic")); for (const auto& box : polymorphicBoxes) box->showVolume(); } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_06/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; export class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_07/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Uncomment virtual to ensure destructors of derived classes are called correctly /*virtual*/ ~Box() { std::cout << "Box destructor called" << std::endl; } // More typical declaration of the destructor of a base class // virtual ~Box() = default; // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_07/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; export import :carton; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_07/Carton.cppm ================================================ export module boxes:carton; import ; // For std::max() import ; import ; import ; import :box; export class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} ~Carton() { std::cout << "Carton destructor called" << std::endl; } // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_07/Ex15_07.cpp ================================================ // Polymorphic vectors of smart pointers import ; import ; // For smart pointers import ; // For vector import boxes; int main() { // Careful: this first attempt at a mixed collection is a bad idea (object slicing!) std::vector boxes; boxes.push_back(Box{20.0, 30.0, 40.0}); boxes.push_back(ToughPack{20.0, 30.0, 40.0}); boxes.push_back(Carton{20.0, 30.0, 40.0, "plastic"}); for (const auto& p : boxes) p.showVolume(); std::cout << std::endl; // Next, we create a proper polymorphic vector<>: std::vector> polymorphicBoxes; polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0, "plastic")); for (const auto& p : polymorphicBoxes) p->showVolume(); } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_07/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; import ; export class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; ~ToughPack() { std::cout << "ToughPack destructor called" << std::endl; } protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_07A/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} virtual ~Box() = default; // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_07A/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; export import :carton; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_07A/Carton.cppm ================================================ export module boxes:carton; import ; // For std::max() import ; import ; import :box; export class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} ~Carton() override = default; // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_07A/Ex15_07A.cpp ================================================ // Calling the base class version of a virtual function (see ToughPack::volume()) import ; import ; // For smart pointers import ; // For vector import boxes; int main() { // Careful: this first attempt at a mixed collection is a bad idea (object slicing!) std::vector boxes; boxes.push_back(Box{20.0, 30.0, 40.0}); boxes.push_back(ToughPack{20.0, 30.0, 40.0}); boxes.push_back(Carton{20.0, 30.0, 40.0, "plastic"}); for (const auto& p : boxes) p.showVolume(); std::cout << std::endl; // Next, we create a proper polymorphic vector<>: std::vector> polymorphicBoxes; polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0, "plastic")); for (const auto& p : polymorphicBoxes) p->showVolume(); } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_07A/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; export class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * Box::volume(); } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_08/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box(double length, double width, double height) : m_length {length}, m_width {width}, m_height {height} { std::cout << "Box constructor called for a Box of volume " << volume() << std::endl; } virtual ~Box() { std::cout << "Box destructor called for a Box of volume " << volume() << std::endl; } // Function to calculate volume of a Box virtual double volume() const { return m_length * m_width * m_height; } void showVolume() const { std::cout << "The volume from inside Box::showVolume() is " << volume() << std::endl; } private: double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_08/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_08/Ex14_08.cpp ================================================ // Calling virtual functions from constructors and destructors import boxes; int main() { ToughPack toughPack{ 1.0, 2.0, 3.0 }; toughPack.showVolume(); // Should show a volume equal to 85% of 1x2x3, or 5.1 } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_08/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; import ; export class ToughPack : public Box { public: ToughPack(double length, double width, double height) : Box{length, width, height} { std::cout << "ToughPack constructor called for a Box of volume " << volume() << std::endl; } virtual ~ToughPack() { std::cout << "ToughPack destructor called for a Box of volume " << volume() << std::endl; } // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * Box::volume(); } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_09/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} virtual ~Box() = default; // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_09/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :carton; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_09/Carton.cppm ================================================ export module boxes:carton; import ; // For std::max() import ; import ; import :box; export class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_09/Ex15_09.cpp ================================================ // Using the typeid() operator import ; import ; // For the std::type_info class import boxes; // Define trivial non-polymorphic base and derived classes: class NonPolyBase {}; class NonPolyDerived : public NonPolyBase {}; Box& getSomeBox(); // Function returning a reference to a polymorphic type NonPolyBase& getSomeNonPoly(); // Function returning a reference to a non-polymorphic type int main() { // Part 1: typeid() on types and == operator std::cout << "Type double has name " << typeid(double).name() << std::endl; std::cout << "1 is " << (typeid(1) == typeid(int)? "an int" : "no int") << std::endl; // Part 2: typeid() on polymorphic references Carton carton{ 1, 2, 3, "paperboard" }; Box& boxReference{ carton }; std::cout << "Type of carton is " << typeid(carton).name() << std::endl; std::cout << "Type of boxReference is " << typeid(boxReference).name() << std::endl; std::cout << "These are " << (typeid(carton) == typeid(boxReference)? "" : "not ") << "equal" << std::endl; // Part 3: typeid() on polymorphic pointers Box* boxPointer{ &carton }; std::cout << "Type of &carton is " << typeid(&carton).name() << std::endl; std::cout << "Type of boxPointer is " << typeid(boxPointer).name() << std::endl; std::cout << "Type of *boxPointer is " << typeid(*boxPointer).name() << std::endl; // Part 4: typeid() with non-polymorphic classes NonPolyDerived derived; NonPolyBase& baseRef{ derived }; std::cout << "Type of baseRef is " << typeid(baseRef).name() << std::endl; // Part 5: typeid() on expressions const auto& type_info1{ typeid(getSomeBox()) }; // function call evaluated const auto& type_info2{ typeid(getSomeNonPoly()) }; // function call not evaluated std::cout << "Type of getSomeBox() is " << type_info1.name() << std::endl; std::cout << "Type of getSomeNonPoly() is " << type_info2.name() << std::endl; } Box& getSomeBox() { std::cout << "getSomeBox() called..." << std::endl; static Carton carton{ 2, 3, 5, "duplex" }; return carton; } NonPolyBase& getSomeNonPoly() { std::cout << "getSomeNonPoly() called..." << std::endl; static NonPolyDerived derived; return derived; } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_10/Box.cppm ================================================ export module boxes:box; import ; export class Box { public: Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} virtual ~Box() = default; // Virtual destructor virtual double volume() const = 0; // Function to calculate the volume protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_10/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; export import :carton; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_10/Carton.cppm ================================================ export module boxes:carton; import ; // For std::max() import ; import ; import :box; export class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_10/Ex15_10.cpp ================================================ // Using an abstract class import ; import boxes; int main() { // Box box{20.0, 30.0, 40.0}; // Uncomment for compiler error ToughPack hardcase {20.0, 30.0, 40.0}; // A derived box - same size Carton carton {20.0, 30.0, 40.0, "plastic"}; // A different derived box Box*pBox {&hardcase}; // Base pointer - derived address std::cout << "hardcase volume is " << pBox->volume() << std::endl; pBox = &carton; // New derived address std::cout << "carton volume is " << pBox->volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_10/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; export class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_11/Box.cppm ================================================ export module boxes:box; import vessel; export class Box : public Vessel { public: Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} double volume() const override { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_11/Boxes.cppm ================================================ // Boxes.cppm: primary module interface file export module boxes; export import :box; // Export all partitions export import :tough_pack; export import :carton; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_11/Can.cppm ================================================ // Can.cppm Class defining a cylindrical can of a given height and diameter export module can; import vessel; import ; export class Can : public Vessel { public: Can(double diameter, double height) : m_diameter {diameter}, m_height {height} {} double volume() const override { return std::numbers::pi * m_diameter * m_diameter * m_height / 4; } private: double m_diameter, m_height; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_11/Carton.cppm ================================================ export module boxes:carton; import ; // For std::max() import ; import ; import :box; export class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_11/Ex15_11.cpp ================================================ // Using an interface class and indirect base classes import ; import ; // For the vector container import boxes; import vessel; import can; int main() { Box box {40, 30, 20}; Can can {10, 3}; Carton carton {40, 30, 20, "Plastic"}; ToughPack hardcase {40, 30, 20}; std::vector vessels {&box, &can, &carton, &hardcase}; for (const auto* vessel : vessels) std::cout << "Volume is " << vessel->volume() << std::endl; } ================================================ FILE: Examples/Modules/Chapter 15/Ex15_11/ToughPack.cppm ================================================ export module boxes:tough_pack; import :box; export class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; ================================================ FILE: Examples/Modules/Chapter 15/Ex15_11/Vessel.cppm ================================================ // Vessel.cppm Abstract class defining a vessel export module vessel; export class Vessel { public: virtual ~Vessel() = default; // As always: a virtual destructor! virtual double volume() const = 0; }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_01/Ex16_01.cpp ================================================ // Throwing and catching exceptions import ; int main() { for (size_t i {}; i < 5; ++i) { try { if (i < 2) throw i; std::cout << "i not thrown - value is " << i << std::endl; if (i > 3) throw "Here is another!"; std::cout << "End of the try block." << std::endl; } catch (size_t i) // Catch exceptions of type size_t { std::cout << "i caught - value is " << i << std::endl; } catch (const char* message) // Catch exceptions of type char* { std::cout << "message caught - value is \"" << message << '"' << std::endl; } std::cout << "End of the for loop body (after the catch blocks)" << " - i is " << i << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_02/Ex16_02.cpp ================================================ // Throw an exception object import ; import ; // for operator<< import troubles; void trySomething(int i); int main() { for (int i {}; i < 2; ++i) { try { trySomething(i); } catch (const Trouble& t) { // What seems to be the trouble? std::cout << "Exception: " << t.what() << std::endl; } } } void trySomething(int i) { // There's always trouble when trying something... if (i == 0) throw Trouble {}; else throw Trouble {"Nobody knows the trouble I've seen..."}; } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_02/Troubles.cppm ================================================ // Exception class definition export module troubles; import ; import ; export class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} std::string_view what() const { return m_message; } private: std::string m_message; }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_03/Ex16_03.cpp ================================================ // Throwing and catching objects in a hierarchy import ; import ; // for operator<< import troubles; int main() { for (int i {}; i < 7; ++i) { try { if (i == 3) throw Trouble{}; else if (i == 5) throw MoreTrouble{}; else if (i == 6) throw BigTrouble{}; } catch (const BigTrouble& t) { std::cout << "BigTrouble object caught: " << t.what() << std::endl; } catch (const MoreTrouble& t) { std::cout << "MoreTrouble object caught: " << t.what() << std::endl; } catch (const Trouble& t) { std::cout << "Trouble object caught: " << t.what() << std::endl; } std::cout << "End of the for loop (after the catch blocks) - i is " << i << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_03/Troubles.cppm ================================================ // Troubles.cppm Exception classes export module troubles; import ; import ; export class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class export class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class export class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_04/Ex16_04.cpp ================================================ // Catching exceptions with a base class handler import ; import ; // for the type_info type returned by the typeid operator import ; // for operator<< import troubles; int main() { for (int i {}; i < 7; ++i) { try { if (i == 3) throw Trouble{}; else if (i == 5) throw MoreTrouble{}; else if (i == 6) throw BigTrouble{}; } catch (const Trouble& t) { //std::cout << "Trouble object caught: " << t.what() << std::endl; std::cout << typeid(t).name() << " object caught: " << t.what() << std::endl; } std::cout << "End of the for loop (after the catch blocks) - i is " << i << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_04/Troubles.cppm ================================================ // Troubles.cppm Exception classes export module troubles; import ; import ; export class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class export class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class export class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_05/Ex16_05.cpp ================================================ // Rethrowing exceptions import ; import ; import ; // for operator<< import troubles; int main() { for (int i {}; i < 7; ++i) { try { try { if (i == 3) throw Trouble{}; else if (i == 5) throw MoreTrouble{}; else if (i == 6) throw BigTrouble{}; } catch (const Trouble& t) { if (typeid(t) == typeid(Trouble)) std::cout << "Trouble object caught in inner block: " << t.what() << std::endl; else throw; // Rethrow current exception } } catch (const Trouble& t) { std::cout << typeid(t).name() << " object caught in outer block: " << t.what() << std::endl; } std::cout << "End of the for loop (after the catch blocks) - i is " << i << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_05/Troubles.cppm ================================================ // Troubles.cppm Exception classes export module troubles; import ; import ; export class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class export class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class export class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_06/Ex16_06.cpp ================================================ // Catching any exception import ; import ; // For use of typeid() import ; // for operator<< import troubles; int main() { for (int i {}; i < 7; ++i) { try { try { if (i == 3) throw Trouble{}; else if (i == 5) throw MoreTrouble{}; else if (i == 6) throw BigTrouble{}; } catch (const BigTrouble& bt) { std::cout << "Oh dear, big trouble. Let's handle it here and now." << std::endl; // Do not rethrow... } catch (...) // Catch any other exception { std::cout << "We caught something else! Let's rethrow it. " << std::endl; throw; // Rethrow current exception } } catch (const Trouble& t) { std::cout << typeid(t).name() << " object caught in outer block: " << t.what() << std::endl; } std::cout << "End of the for loop (after the catch blocks) - i is " << i << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_06/Troubles.cppm ================================================ // Troubles.cppm Exception classes export module troubles; import ; import ; export class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class export class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class export class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07/Ex16_07.cpp ================================================ // Exceptions may result in resource leaks! import ; import troubles; #include // For std::sqrt() double computeValue(size_t x); // A function to compute a single value double* computeValues(size_t howMany); // A function to compute an array of values int main() { try { double* values{ computeValues(10'000) }; // Unfortunately we won't be making it this far... delete[] values; } catch (const Trouble&) { std::cout << "No worries: I've caught it!" << std::endl; } } double* computeValues(size_t howMany) { double* values{ new double[howMany] }; for (size_t i{}; i < howMany; ++i) values[i] = computeValue(i); return values; } double computeValue(size_t x) { if (x < 100) return std::sqrt(x); // Return the square root of the input argument else throw Trouble{ "The trouble with trouble is, it starts out as fun!" }; } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07/Troubles.cppm ================================================ // Troubles.cppm Exception classes export module troubles; import ; import ; export class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class export class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class export class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07A/Ex16_07A.cpp ================================================ // Use of a try-catch block to fix the memory leak! import ; import troubles; #include // For std::sqrt() double computeValue(size_t x); // A function to compute a single value double* computeValues(size_t howMany); // A function to compute an array of values int main() { try { double* values{ computeValues(10'000) }; // Unfortunately we won't be making it this far... delete[] values; } catch (const Trouble&) { std::cout << "No worries: I've caught it!" << std::endl; } } double* computeValues(size_t howMany) { double* values{ new double[howMany] }; try { for (size_t i {}; i < howMany; ++i) values[i] = computeValue(i); return values; } catch (const Trouble&) { std::cout << "I sense trouble... Freeing memory..." << std::endl; delete[] values; throw; } } double computeValue(size_t x) { if (x < 100) return std::sqrt(x); // Return the square root of the input argument else throw Trouble{ "The trouble with trouble is, it starts out as fun!" }; } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07A/Troubles.cppm ================================================ // Troubles.cppm Exception classes export module troubles; import ; import ; export class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class export class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class export class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07B/DoubleArrayRAII.cppm ================================================ // A custom RAII class to manage a dynamic double[] array resource export module raii; import ; export class DoubleArrayRAII final { public: explicit DoubleArrayRAII(size_t size) : m_resource{ new double[size] } {} ~DoubleArrayRAII() { std::cout << "Freeing memory..." << std::endl; delete[] m_resource; } // Delete copy constructor and assignment operator DoubleArrayRAII(const DoubleArrayRAII&) = delete; DoubleArrayRAII& operator=(const DoubleArrayRAII&) = delete; // Array subscript operator double& operator[](size_t index) noexcept { return m_resource[index]; } const double& operator[](size_t index) const noexcept { return m_resource[index]; } // Function to access the encapsulated resource double* get() const noexcept { return m_resource; } // Function to instruct the RAII object to hand over the resource. // Once called, the RAII object shall no longer release the resource // upon destruction anymore. Returns the resource in the process. double* release() noexcept { double* result = m_resource; m_resource = nullptr; return result; } private: double* m_resource; }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07B/Ex16_07B.cpp ================================================ // Exceptions may result in resource leaks! import ; import troubles; import raii; #include // For std::sqrt() double computeValue(size_t x); // A function to compute a single value double* computeValues(size_t howMany); // A function to compute an array of values int main() { try { double* values{ computeValues(10'000) }; // Unfortunately we won't be making it this far... delete[] values; } catch (const Trouble&) { std::cout << "No worries: I've caught it!" << std::endl; } } double* computeValues(size_t howMany) { DoubleArrayRAII values{ howMany }; for (size_t i{}; i < howMany; ++i) values[i] = computeValue(i); return values.release(); } double computeValue(size_t x) { if (x < 100) return std::sqrt(x); // Return the square root of the input argument else throw Trouble{ "The trouble with trouble is, it starts out as fun!" }; } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07B/Troubles.cppm ================================================ // Troubles.cppm Exception classes export module troubles; import ; import ; export class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class export class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class export class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07C/Ex16_07C.cpp ================================================ // Avoid resource leaks due to exceptions using std::unique_ptr<> // Note: this example is given but not named in the text. // Instead of a custom RAII class DoubleArrayRAII, it uses std::unique_ptr<>. // Unlike the former, the latter can be returned from computeValues() as well. import ; import ; import troubles; #include // For std::sqrt() double computeValue(size_t x); // A function to compute a single value std::unique_ptr computeValues(size_t howMany); // A function to compute an array of values int main() { try { auto values{ computeValues(10'000) }; // Cannot leak either: resource is managed by RAII object! } catch (const Trouble&) { std::cout << "No worries: I've caught it!" << std::endl; } } std::unique_ptr computeValues(size_t howMany) { auto values{ std::make_unique(howMany) }; for (size_t i{}; i < howMany; ++i) values[i] = computeValue(i); return values; } double computeValue(size_t x) { if (x < 100) return std::sqrt(x); // Return the square root of the input argument else throw Trouble{ "The trouble with trouble is, it starts out as fun!" }; } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07C/Troubles.cppm ================================================ // Troubles.cppm Exception classes export module troubles; import ; import ; export class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class export class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class export class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07D/Ex16_07D.cpp ================================================ // Avoid resource leaks due to exceptions using std::unique_ptr<> // Note: this example is given but not named in the text. // Instead of a custom RAII class DoubleArrayRAII, it uses std::vector<>. // Unlike the former, the latter can of course be returned from computeValues() as well. import ; import ; import troubles; #include // For std::sqrt() double computeValue(size_t x); // A function to compute a single value std::vector computeValues(size_t howMany); // A function to compute an array of values int main() { try { auto values{ computeValues(10'000) }; // Cannot leak either: resource is managed by RAII object! } catch (const Trouble&) { std::cout << "No worries: I've caught it!" << std::endl; } } std::vector computeValues(size_t howMany) { std::vector values; for (size_t i{}; i < howMany; ++i) values.push_back(computeValue(i)); return values; } double computeValue(size_t x) { if (x < 100) return std::sqrt(x); // Return the square root of the input argument else throw Trouble{ "The trouble with trouble is, it starts out as fun!" }; } ================================================ FILE: Examples/Modules/Chapter 16/Ex16_07D/Troubles.cppm ================================================ // Troubles.cppm Exception classes export module troubles; import ; import ; export class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class export class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class export class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_09/Box.cppm ================================================ export module box; import ; // For std::min() function template import dimension_error; export class Box { public: Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } { if (l <= 0.0 || w <= 0.0 || h <= 0.0) throw DimensionError{ std::min({l, w, h}) }; } double volume() const { return m_length * m_width * m_height; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_09/DimensionError.cppm ================================================ export module dimension_error; import ; // For derived exception classes such as std::out_of_range import ; // For std::to_string() and the std::string type export class DimensionError : public std::out_of_range { public: explicit DimensionError(double value) : std::out_of_range{ "Zero or negative dimension: " + std::to_string(value) } , m_value{ value } {} // Function to obtain the invalid dimension value double getValue() const noexcept { return m_value; } private: double m_value; }; ================================================ FILE: Examples/Modules/Chapter 16/Ex16_09/Ex16_09.cpp ================================================ // Using an exception class import ; import box; // For the Box class import dimension_error; // For the dimension_error class int main() { try { Box box1 {1.0, 2.0, 3.0}; std::cout << "box1 volume is " << box1.volume() << std::endl; Box box2 {1.0, -2.0, 3.0}; std::cout << "box2 volume is " << box2.volume() << std::endl; } catch (const std::exception& ex) { std::cout << "Exception caught in main(): " << ex.what() << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_01/Array.cppm ================================================ // This solution uses the const-and-back-again idiom to avoid code duplication // between the non-const and const overloads of the array subscript operators. // It does not yet use the copy-and-swap idiom for the copy assignment operator // template, though: see Ex17_01A. export module array; import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() export template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Copy assignment operator T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Copy assignment operator template (not exception safe) template Array& Array::operator=(const Array& rhs) { if (&rhs != this) // If lhs != rhs... { // ...do the assignment... delete[] m_elements; // Release any free store memory m_size = rhs.m_size; // Copy the members of rhs into lhs m_elements = new T[m_size]; for (size_t i {}; i < m_size; ++i) m_elements[i] = rhs.m_elements[i]; } return *this; // ... return lhs } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_01/Box.cppm ================================================ export module box; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {}; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } private: double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 17/Ex17_01/Ex17_01.cpp ================================================ // Using a class template import box; import array; import ; import ; int main() { try { const size_t numValues {20}; Array values {numValues}; for (unsigned i {}; i < numValues; ++i) values[i] = i + 1; std::cout << "Sums of pairs of elements:"; size_t lines {}; for (size_t i {numValues - 1}; i >= 0; --i) { std::cout << (lines++ % 5 == 0 ? "\n" : "") << std::format("{:5g}", values[i] + values[i-1]); } } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } try { const size_t numBoxes {5}; Array boxes {numBoxes}; for (size_t i {} ; i <= numBoxes ; ++i) std::cout << "Box volume is " << boxes[i].volume() << std::endl; } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_01A/Array.cppm ================================================ // This solution uses // 1) the const-and-back-again idiom to avoid code duplication // between the non-const and const overloads of the array subscript operators. // 2) the copy-and-swap idiom for a thread-safe copy assignment operator export module array; import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() export template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Copy assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_01A/Box.cppm ================================================ export module box; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {}; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } private: double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 17/Ex17_01A/Ex17_01A.cpp ================================================ // Using a class template import box; import array; import ; import ; int main() { try { const size_t numValues {20}; Array values {numValues}; for (unsigned i {}; i < numValues; ++i) values[i] = i + 1; std::cout << "Sums of pairs of elements:"; size_t lines {}; for (size_t i {numValues - 1}; i >= 0; --i) { std::cout << (lines++ % 5 == 0 ? "\n" : "") << std::format("{:5g}", values[i] + values[i-1]); } } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } try { const size_t numBoxes {5}; Array boxes {numBoxes}; for (size_t i {} ; i <= numBoxes ; ++i) std::cout << "Box volume is " << boxes[i].volume() << std::endl; } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_02/Array.cppm ================================================ export module array; import ; // For standard exception types import ; // For to_string() import ; // For std::as_const() export template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Assignment operator void swap(Array& other) noexcept; // noexcept swap() function T& operator[](int index); // Subscript operator const T& operator[](int index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements{ new T[size] {} }, m_size{ size } {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](int index) const { if (index < startIndex) throw std::out_of_range{ "Index too small: " + std::to_string(index) }; if (index > startIndex + static_cast(m_size) - 1) throw std::out_of_range{ "Index too large: " + std::to_string(index) }; return m_elements[index - startIndex]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](int index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (can only swap arrays with identical startIndex) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_02/Box.cppm ================================================ export module box; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {}; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } private: double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 17/Ex17_02/Ex17_02.cpp ================================================ // Using a class template with a non-type parameter import box; import array; import ; import ; import ; // For use of typeid() int main() { try { try { const size_t size {21}; // Number of array elements const int start {-10}; // Index for first element const int end {start + static_cast(size) - 1}; // Index for last element Array values {size}; // Define array of double values for (int i {start}; i <= end; ++i) // Initialize the elements values[i] = i - start + 1; std::cout << "Sums of pairs of elements: "; size_t lines {}; for (int i {end}; i >= start; --i) { std::cout << (lines++ % 5 == 0 ? "\n" : "") << std::format("{:5g}", values[i] + values[i-1]); } } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } // Create array of Box objects const int numBoxes {9}; Array boxes { static_cast(numBoxes) }; for (int i { -numBoxes / 2 }; i <= numBoxes/2 + numBoxes%2; ++i) std::cout << std::format("Volume of Box[{}] is {}\n", i, boxes[i].volume()); } catch (const std::exception& ex) { std::cerr << typeid(ex).name() << " exception caught in main()! " << ex.what() << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_02A/Array.cppm ================================================ export module array; import ; // For standard exception types import ; // For to_string() import ; // For std::as_const() export template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Assignment operator void swap(Array& other) noexcept; // noexcept swap() function T& operator[](int index); // Subscript operator const T& operator[](int index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements{ new T[size] {} }, m_size{ size } {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template (improved readability!) template const T& Array::operator[](int index) const { // Subtract startIndex to obtain the actual index into the m_elements array. // If startIndex is 0, conventional 0-based array indexing is used. const int actualIndex{ index - startIndex }; if (actualIndex < 0) throw std::out_of_range {"Index too small: " + std::to_string(index)}; if (actualIndex >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[actualIndex]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](int index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (can only swap arrays with identical startIndex) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_02A/Box.cppm ================================================ export module box; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {}; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } private: double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 17/Ex17_02A/Ex17_02A.cpp ================================================ // Using a class template with a non-type parameter // In this variant the code readability of the array subscript operator template // was improved (see Array<> source code). import box; import array; import ; import ; import ; // For use of typeid() int main() { try { try { const size_t size {21}; // Number of array elements const int start {-10}; // Index for first element const int end {start + static_cast(size) - 1}; // Index for last element Array values {size}; // Define array of double values for (int i {start}; i <= end; ++i) // Initialize the elements values[i] = i - start + 1; std::cout << "Sums of pairs of elements: "; size_t lines {}; for (int i {end}; i >= start; --i) { std::cout << (lines++ % 5 == 0 ? "\n" : "") << std::format("{:5g}", values[i] + values[i-1]); } } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } // Create array of Box objects const int numBoxes {9}; Array boxes { static_cast(numBoxes) }; for (int i { -numBoxes / 2 }; i <= numBoxes/2 + numBoxes%2; ++i) std::cout << std::format("Volume of Box[{}] is {}\n", i, boxes[i].volume()); } catch (const std::exception& ex) { std::cerr << typeid(ex).name() << " exception caught in main()! " << ex.what() << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_03/Array.cppm ================================================ export module array; import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; // For the std::initializer_list<> template export template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(std::initializer_list elements); // Initializer list constructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Copy assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Initializer list constructor template template Array::Array(std::initializer_list elements) : m_elements{ new T[elements.size()] }, m_size{ elements.size() } { // std::initializer_list<> has no operator[], but can be used in range-based for loop. // The possibility to add variable initializations such as "size_t i {};" // to a range-based for loop is new in C++20. for (size_t i{}; const T & element : elements) m_elements[i++] = element; } // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_03/Box.cppm ================================================ export module box; export class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {}; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } private: double m_length, m_width, m_height; }; ================================================ FILE: Examples/Modules/Chapter 17/Ex17_03/Ex17_03.cpp ================================================ // Illustrating Class Template Argument Deduction (CTAD) // by adding an initializer list constructor to our Array<> template. import box; import array; import ; int main() { // Class Template Argument Deduction (CTAD) in action: Array integers{ 1, 2, 3, 4, 5 }; // Deduced type: Array Array doubles{ 1.0, 2.0, 3.0, 4.0, 5.0 }; // Deduced type: Array values{ numValues }; // Now uses the initializer list constructor! std::cout << "Wrong constructor used, so " << values.getSize() << " != " << numValues << std::endl; std::cout << "Single value contained in Array<> is " << values[0] << std::endl; } // Workaround: do not use uniform initialization (or "near uniform", as is thus more appropriate...) { const size_t numValues{ 50 }; Array values(numValues); // Uses Array(size_t) constructor as before std::cout << "Intended constructor used, so " << values.getSize() << " == " << numValues << std::endl; std::cout << "All values are equal to " << values[numValues / 2] << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_04/Ex17_04.cpp ================================================ // Using a stack defined by nested class templates import stack; import ; import ; int main() { std::string words[]{ "The", "quick", "brown", "fox", "jumps" }; Stack wordStack; // A stack of strings for (const auto& word : words) wordStack.push(word); Stack newStack{ wordStack }; // Create a copy of the stack // Display the words in reverse order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; // Reverse wordStack onto newStack while (!wordStack.isEmpty()) newStack.push(wordStack.pop()); // Display the words in original order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; std::cout << std::endl << "Enter a line of text:" << std::endl; std::string text; std::getline(std::cin, text); // Read a line into the string object Stack characters; // A stack for characters for (size_t i{}; i < text.length(); ++i) characters.push(text[i]); // Push the string characters onto the stack std::cout << std::endl; while (!characters.isEmpty()) std::cout << characters.pop(); // Pop the characters off the stack std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_04/Stack.cppm ================================================ // Stack.cppm Templates to define stacks export module stack; import ; export template class Stack { public: Stack() = default; // Default constructor ~Stack(); // Destructor Stack(const Stack & stack); // Copy constructor Stack& operator=(const Stack & rhs); // Copy assignment operator void swap(Stack & other) noexcept; // noexcept swap() function void push(const T & item); // Push an object onto the stack T pop(); // Pop an object off the stack bool isEmpty() const; // Empty test private: // Nested class class Node { public: Node(const T& item) : m_item{ item } {} // Create a node from an object T m_item; // The object stored in this node Node* m_next{}; // Pointer to next node }; Node* m_head{}; // Points to the top of the stack }; // Copy constructor template Stack::Stack(const Stack& stack) { if (stack.m_head) { m_head = new Node {*stack.m_head}; // Copy the top node of the original Node* oldNode {stack.m_head}; // Points to the top node of the original Node* newNode {m_head}; // Points to the node in the new stack while (oldNode = oldNode->m_next) // If m_next was nullptr, the last node was copied { newNode->m_next = new Node{*oldNode}; // Duplicate it newNode = newNode->m_next; // Move to the node just created } } } // Destructor template Stack::~Stack() { while (m_head) { // While current pointer is not null auto* next{ m_head->m_next }; // Get the pointer to the next node delete m_head; // Delete the current head m_head = next; // Make m_head point to the next node } } // Copy assignment operator template Stack& Stack::operator=(const Stack& rhs) { auto copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Push an object onto the stack template void Stack::push(const T& item) { Node* node{ new Node{item} }; // Create the new node node->m_next = m_head; // Point to the old top node m_head = node; // Make the new node the top } // Pop an object off the stack template T Stack::pop() { if (isEmpty()) // If it's empty pop() is not valid so throw exception throw std::logic_error {"Stack empty"}; auto* next {m_head->m_next}; // Save pointer to the next node T item {m_head->m_item}; // Save the T value to return later delete m_head; // Delete the current head m_head = next; // Make head point to the next node return item; // Return the top object } template bool Stack::isEmpty() const { return m_head == nullptr; } // noexcept swap member function template void Stack::swap(Stack& other) noexcept { std::swap(m_head, other.m_head); } // Conventional noexcept swap non-member function export template void swap(Stack& one, Stack& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_04A/Ex17_04A.cpp ================================================ // Using a stack defined by nested class templates // (with improvement suggested in the "A Better Stack" section: see Stack<> source) import stack; import ; import ; int main() { std::string words[]{ "The", "quick", "brown", "fox", "jumps" }; Stack wordStack; // A stack of strings for (const auto& word : words) wordStack.push(word); Stack newStack{ wordStack }; // Create a copy of the stack // Display the words in reverse order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; // Reverse wordStack onto newStack while (!wordStack.isEmpty()) newStack.push(wordStack.pop()); // Display the words in original order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; std::cout << std::endl << "Enter a line of text:" << std::endl; std::string text; std::getline(std::cin, text); // Read a line into the string object Stack characters; // A stack for characters for (size_t i{}; i < text.length(); ++i) characters.push(text[i]); // Push the string characters onto the stack std::cout << std::endl; while (!characters.isEmpty()) std::cout << characters.pop(); // Pop the characters off the stack std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_04A/Stack.cppm ================================================ // Stack.cppm Templates to define stacks // (with improvement suggested in the "A Better Stack" section) export module stack; import ; export template class Stack { public: Stack() = default; // Default constructor ~Stack(); // Destructor Stack(const Stack & stack); // Copy constructor Stack& operator=(const Stack & rhs); // Copy assignment operator void swap(Stack & other) noexcept; // noexcept swap() function void push(const T & item); // Push an object onto the stack T pop(); // Pop an object off the stack bool isEmpty() const; // Empty test private: // Nested class class Node { public: Node(const T& item) : m_item{ item } {} // Create a node from an object T m_item; // The object stored in this node Node* m_next{}; // Pointer to next node }; Node* m_head{}; // Points to the top of the stack }; // Copy constructor template Stack::Stack(const Stack& stack) { if (stack.m_head) { m_head = new Node {*stack.m_head}; // Copy the top node of the original Node* oldNode {stack.m_head}; // Points to the top node of the original Node* newNode {m_head}; // Points to the node in the new stack while (oldNode = oldNode->m_next) // If m_next was nullptr, the last node was copied { newNode->m_next = new Node{*oldNode}; // Duplicate it newNode = newNode->m_next; // Move to the node just created } } } // Destructor template Stack::~Stack() { while (!isEmpty()) pop(); } // Copy assignment operator template Stack& Stack::operator=(const Stack& rhs) { auto copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Push an object onto the stack template void Stack::push(const T& item) { Node* node{ new Node{item} }; // Create the new node node->m_next = m_head; // Point to the old top node m_head = node; // Make the new node the top } // Pop an object off the stack template T Stack::pop() { if (isEmpty()) // If it's empty pop() is not valid so throw exception throw std::logic_error {"Stack empty"}; auto* next {m_head->m_next}; // Save pointer to the next node T item {m_head->m_item}; // Save the T value to return later delete m_head; // Delete the current head m_head = next; // Make head point to the next node return item; // Return the top object } template bool Stack::isEmpty() const { return m_head == nullptr; } // noexcept swap member function template void Stack::swap(Stack& other) noexcept { std::swap(m_head, other.m_head); } // Conventional noexcept swap non-member function export template void swap(Stack& one, Stack& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_04B/Ex17_04B.cpp ================================================ // Using a stack defined by nested class templates // (using std::unique_ptr<>: see Stack<> source) // Note: this is a bonus example that is only hinted at in the text (and not explicitly named). // It requires the use of std::move(), seen only in Chapter 18. import stack; import ; import ; int main() { std::string words[]{ "The", "quick", "brown", "fox", "jumps" }; Stack wordStack; // A stack of strings for (const auto& word : words) wordStack.push(word); Stack newStack{ wordStack }; // Create a copy of the stack // Display the words in reverse order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; // Reverse wordStack onto newStack while (!wordStack.isEmpty()) newStack.push(wordStack.pop()); // Display the words in original order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; std::cout << std::endl << "Enter a line of text:" << std::endl; std::string text; std::getline(std::cin, text); // Read a line into the string object Stack characters; // A stack for characters for (size_t i{}; i < text.length(); ++i) characters.push(text[i]); // Push the string characters onto the stack std::cout << std::endl; while (!characters.isEmpty()) std::cout << characters.pop(); // Pop the characters off the stack std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_04B/Stack.cppm ================================================ // Stack.cppm Templates to define stacks // (using std::unique_ptr<> instead of raw pointer) export module stack; import ; import ; // For std::unique_ptr<> /* The required changes are minimal, but they do require std::move() which we only cover in Chapter 18. Other changes include: - no need for the destructor anymore (all memory is managed by smart pointers) - Nodes can no longer be copied, so you need to construct all Nodes with the Node(const T&) constructor */ export template class Stack { public: Stack() = default; // Default constructor Stack(const Stack & stack); // Copy constructor Stack& operator=(const Stack & rhs); // Copy assignment operator void swap(Stack & other) noexcept; // noexcept swap() function void push(const T & item); // Push an object onto the stack T pop(); // Pop an object off the stack bool isEmpty() const; // Empty test private: // Nested class class Node { public: Node(const T& item) : m_item{ item } {} // Create a node from an object T m_item; // The object stored in this node std::unique_ptr m_next{}; // Pointer to next node }; std::unique_ptr m_head; // Points to the top of the stack }; // Copy constructor template Stack::Stack(const Stack& stack) { if (stack.m_head) { m_head = std::make_unique(stack.m_head->m_item); // Copy the top node of the original Node* oldNode {stack.m_head.get()}; // Points to the top node of the original Node* newNode {m_head.get()}; // Points to the node in the new stack while (oldNode = oldNode->m_next.get()) // If m_next was nullptr, the last node was copied { newNode->m_next = std::make_unique(oldNode->m_item); // Duplicate it newNode = newNode->m_next.get(); // Move to the node just created } } } // Copy assignment operator template Stack& Stack::operator=(const Stack& rhs) { auto copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Push an object onto the stack template void Stack::push(const T& item) { // See Chapter 18 for std::move() auto node{ std::make_unique(item) }; // Create the new node node->m_next = std::move(m_head); // Point to the old top node m_head = std::move(node); // Make the new node the top } // Pop an object off the stack template T Stack::pop() { if (isEmpty()) // If it's empty pop() is not valid so throw exception throw std::logic_error {"Stack empty"}; // See Chapter 18 for std::move() auto next {std::move(m_head->m_next)}; // Save pointer to the next node T item {m_head->m_item}; // Save the T value to return later m_head.reset(); // Delete the current head m_head = std::move(next); // Make head point to the next node return item; // Return the top object } template bool Stack::isEmpty() const { return m_head == nullptr; } // noexcept swap member function template void Stack::swap(Stack& other) noexcept { std::swap(m_head, other.m_head); } // Conventional noexcept swap non-member function export template void swap(Stack& one, Stack& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_05/Ex17_05A.cpp ================================================ // Disambiguating dependant names: this code will not compile. template class Outer { public: class Nested { /* ... */ }; // Or a type alias of form 'using Nested = ...;' // ... }; // Uncomment the typename keyword to turn Outer::Nested into // a dependent type name and fix the compilation template void someFunction() { /*typename*/ Outer::Nested* nested; /* ... */ } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_05/Ex17_05B.cpp ================================================ // Disambiguating dependant names // Note: not all compilers may implement the C++20 rules already, // any may still require additional typename keywords in front of T::Derived template class Outer { public: class Nested { /* ... */ }; // Or a type alias of form 'using Nested = ...;' // ... }; template // T assumed to define nested Base and Derived types / aliases void someOtherFunction() { typename T::Base* b{ new T::Derived{} }; // Or: auto* b{ ... } const typename T::Derived& d{ static_cast(*b) }; // Or: const auto& d{ ... } /* ... */ } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_05/Ex17_05C.cpp ================================================ // Disambiguating dependant names // Note: not all compilers may implement the C++20 rules already, // any may still require additional typename keywords in front of Outer::Nested template class Outer { public: class Nested { /* ... */ }; // Or a type alias of form 'using Nested = ...;' // ... }; template class MyClass { public: Outer::Nested memberFunction(const Outer::Nested& nested); private: T::Nested m_member_variable; }; template T::Nested nonMemberFunction(const typename T::Nested* nested); template Outer::Nested MyClass::memberFunction(const typename Outer::Nested& nested) { return nested; } ================================================ FILE: Examples/Modules/Chapter 17/Ex17_06/Ex17_06.cpp ================================================ // Disambiguating dependant base class names // (this code does not compile without changes) import ; template class Base { public: void baseFun() { /* ... */ } protected: int m_base_var {}; }; template class Derived : public Base { public: void derivedFun(); // Option 3: using declarations (remove to see error messages) // using Base::baseFun; // using Base::m_base_var; }; template void Derived::derivedFun() { // These two lines should give compiler errors. // Uncomment the using declarations in the Derived<> template // to make them work. Alternative solutions are illustrated below. baseFun(); std::cout << m_base_var << std::endl; // Option 1: add this-> this->baseFun(); std::cout << this->m_base_var << std::endl; // Option 2: add Base:: Base::baseFun(); std::cout << Base::m_base_var << std::endl; } int main() { } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_01/Array.cppm ================================================ export module array; import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; export template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Copy assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_01/Ex18_01.cpp ================================================ // Copying objects into a vector import array; import ; import ; // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { const size_t numArrays{ 10 }; // Fill 10 Arrays with 1,000 strings each const size_t numStringsPerArray{ 1000 }; std::vector> vectorOfArrays; vectorOfArrays.reserve(numArrays); // Inform the vector<> how many Arrays we'll be adding for (size_t i {}; i < numArrays; ++i) { vectorOfArrays.push_back(buildStringArray(numStringsPerArray)); } } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_02/Array.cppm ================================================ export module array; import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; export template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_02/Ex18_02.cpp ================================================ // Moving objects into a vector import array; import ; import ; // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { const size_t numArrays{ 10 }; // Fill 10 Arrays with 1,000 strings each const size_t numStringsPerArray{ 1000 }; std::vector> vectorOfArrays; vectorOfArrays.reserve(numArrays); // Inform the vector<> how many Arrays we'll be adding for (size_t i {}; i < numArrays; ++i) { vectorOfArrays.push_back(buildStringArray(numStringsPerArray)); } } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_03/Array.cppm ================================================ export module array; import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; export template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs); // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_03/Ex18_03.cpp ================================================ // Defining and using a move assignment operator import array; import ; // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array strings { 123 }; strings = buildStringArray(1'000); // Assign an rvalue to strings Array more_strings{ 2'000 }; strings = more_strings; // Assign an lvalue to strings } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_04/Array.cppm ================================================ export module array; import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; export template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs); // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_04/Ex18_04.cpp ================================================ // Using std::move() to force the move assignment of a named variable import array; import ; // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array strings { 123 }; strings = buildStringArray(1'000); // Assign an rvalue to strings Array more_strings{ 2'000 }; strings = std::move(more_strings); // Move more_strings into strings /* Caution: once moved, an object should not be used anymore! */ // std::cout << more_strings[101] << std::endl; // ??? } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_05A/Array.cppm ================================================ export module array; // Compared to Ex18_04, this variant adds two overloads of push_back(). // The Array<>() default constructor is new as well. import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; export template class Array { public: Array(); // Default constructor explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs); // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size void push_back(const T& element); // Add copy of given element to the back of the array void push_back(T&& element); // Move element to the back of the array private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template void Array::push_back(const T& element) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i{}; i < m_size; ++i) // Move all existing elements... newArray[i] = std::move(m_elements[i]); newArray[m_size] = element; // Copy the new one... swap(newArray); // ... and swap! } // push_back() overload for rvalue references template void Array::push_back(T&& element) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i{}; i < m_size; ++i) // Move all existing elements... newArray[i] = std::move(m_elements[i]); newArray[m_size] = std::move(element); // Move the new one... swap(newArray); // ... and swap! } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_05A/Ex18_05A.cpp ================================================ // Exercising two overloads of push_back(): one for lvalue arguments, and one for rvalue arguments import array; import ; import ; // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array> array_of_arrays; Array array{ buildStringArray(1'000) }; array_of_arrays.push_back(array); // Push an lvalue array.push_back("One more for good measure"); std::cout << std::endl; array_of_arrays.push_back(std::move(array)); // Push an rvalue } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_05B/Array.cppm ================================================ export module array; // This variant merges the two overloads of push_back() of Ex18_05 into one single member. // The push_back() function accepts a new element by value. import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; export template class Array { public: Array(); // Default constructor explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs); // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size void push_back(T element); // Copy or move element to the back of the array private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template void Array::push_back(T element) // Pass by value (copy of lvalue, or moved rvalue!) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i{}; i < m_size; ++i) // Move all existing elements... newArray[i] = std::move(m_elements[i]); newArray[m_size] = std::move(element); // Move the new one (could itself be a copy already)... swap(newArray); // ... and swap! } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_05B/Ex18_05B.cpp ================================================ // Exercising a single overload of push_back() // that can be used to either to add a copy of, or move, a new value into an Array<>. // The caller decides whether the element is copied or moved. import array; import ; import ; // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array> array_of_arrays; Array array{ buildStringArray(1'000) }; array_of_arrays.push_back(array); // Push an lvalue array.push_back("One more for good measure"); std::cout << std::endl; array_of_arrays.push_back(std::move(array)); // Push an rvalue } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_06/Array.cppm ================================================ export module array; // Compared to Ex18_05B, this variant adds noexcept specifiers to all move members // and implements a strongly exception safe push_back(). // It uses some mild template meta programming in move_assign_if_noexcept() to accomplish the latter. import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; import ; template std::conditional_t, T&&, const T&> move_assign_if_noexcept(T& x) noexcept { return std::move(x); } export template class Array { public: Array(); // Default constructor explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array) noexcept; // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs) noexcept; // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size void push_back(T element); // Copy or move element to the back of the array private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) noexcept : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) noexcept { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template void Array::push_back(T element) // Pass by value (copy of lvalue, or moved rvalue!) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i {}; i < m_size; ++i) // Move existing elements (copy if not noexcept)... newArray[i] = move_assign_if_noexcept(m_elements[i]); newArray[m_size] = move_assign_if_noexcept(element); // Move (or copy) the new one... swap(newArray); // ... and swap! } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_06/Ex18_06.cpp ================================================ // Exercising a single, exception safe overload of push_back() // Remove the noexcept specifiers from Array<> to observe // that copying is then used instead of moving // (moving would be unsafe if an exception occurs). import array; import ; import ; // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array> array_of_arrays; Array array{ buildStringArray(1'000) }; array_of_arrays.push_back(array); // Push an lvalue array.push_back("One more for good measure"); std::cout << std::endl; array_of_arrays.push_back(std::move(array)); // Push an rvalue } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_07/Array.cppm ================================================ export module array; // Exact same as template as Ex18_05B (that is: without noexcept specifiers!) import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; export template class Array { public: Array(); // Default constructor explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array) /*noexcept*/; // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs) /*noexcept*/; // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size void push_back(T element); // Copy or move element to the back of the array private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) /*noexcept*/ : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) /*noexcept*/ { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template void Array::push_back(T element) // Pass by value (copy of lvalue, or moved rvalue!) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i{}; i < m_size; ++i) // Move all existing elements... newArray[i] = std::move(m_elements[i]); newArray[m_size] = std::move(element); // Move the new one (could itself be a copy already)... swap(newArray); // ... and swap! } ================================================ FILE: Examples/Modules/Chapter 18/Ex18_07/Ex18_07.cpp ================================================ // The effect of not adding noexcept to move members // Uncomment the noexcept specifiers in the Array<> template source // to avoid copying when a std::vector<> grows its dynamic array. import array; import ; import ; import ; // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { std::vector> v; v.push_back(buildStringArray(1'000)); std::cout << std::endl; v.push_back(buildStringArray(2'000)); } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_01/Ex19_01.cpp ================================================ // Exercising pointers to functions import ; long sum(long a, long b); // Function prototype long product(long a, long b); // Function prototype int main() { long(*fun_ptr)(long, long) {}; // Pointer to function fun_ptr = product; std::cout << "3 * 5 = " << fun_ptr(3, 5) << std::endl; // Call product() thru fun_ptr fun_ptr = sum; // Reassign pointer to sum() std::cout << "3 * (4+5) + 6 = " // Call sum() thru fun_ptr twice << fun_ptr(product(3, fun_ptr(4, 5)), 6) << std::endl; } // Function to multiply two values long product(long a, long b) { return a * b; } // Function to add two values long sum(long a, long b) { return a + b; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_02/Ex19_02.cpp ================================================ // Exercising the use of function pointers as callback functions import ; import ; import ; import optimum; // Comparison function prototypes: bool less(const int&, const int&); template bool greater(const T&, const T&); bool longer(const std::string&, const std::string&); int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *findOptimum(numbers, less) << std::endl; std::cout << "Maximum element: " << *findOptimum(numbers, greater) << std::endl; std::vector names{ "Moe", "Larry", "Shemp", "Curly", "Joe", "Curly Joe" }; std::cout << "Alphabetically last name: " << *findOptimum(names, greater) << std::endl; std::cout << "Longest name: " << *findOptimum(names, longer) << std::endl; } bool less(const int& one, const int& other) { return one < other; } template bool greater(const T& one, const T& other) { return one > other; } bool longer(const std::string& one, const std::string& other) { return one.length() > other.length(); } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_02/Optimum.cppm ================================================ // Optimum.cppm - a function template to determine the optimum element in a given vector export module optimum; import ; export template const T* findOptimum(const std::vector& values, bool (*compare)(const T&, const T&)) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_03/Ex19_03.cpp ================================================ // Exercising the use of a functor as callback functions import ; import ; import optimum; import less; template bool greater(const T& one, const T& other) { return one > other; } int main() { Less less; // Create a 'less than' functor std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *findOptimum(numbers, less) << std::endl; std::cout << "Maximum element: " << *findOptimum(numbers, greater) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_03/Less.cppm ================================================ // Less.cppm - A basic class of functor objects export module less; export class Less { public: bool operator()(int a, int b) const { return a < b; } }; ================================================ FILE: Examples/Modules/Chapter 19/Ex19_03/Optimum.cppm ================================================ // Optimum.cppm - a function template to determine the optimum element in a given vector export module optimum; import ; export template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_03A/Ex19_03A.cpp ================================================ // Exercising the use of standard functors import ; import ; import ; import optimum; int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *findOptimum(numbers, std::less<>{}) << std::endl; std::cout << "Maximum element: " << *findOptimum(numbers, std::greater<>{}) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_03A/Optimum.cppm ================================================ // Optimum.cppm - a function template to determine the optimum element in a given vector export module optimum; import ; export template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_04/Ex19_04.cpp ================================================ // Exercising a function object with a member variable import ; import ; import ; import optimum; import nearer; int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; int number_to_search_for {}; std::cout << "Please enter a number: "; std::cin >> number_to_search_for; std::cout << std::format("The number nearest to {} is {}", number_to_search_for, *findOptimum(numbers, Nearer{ number_to_search_for })) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_04/Nearer.cppm ================================================ // A class of function objects that compare two values based on how close they are // to some third value that was provided to the functor at construction time. module; #include // For std::abs() export module nearer; export class Nearer { public: explicit Nearer(int value) : m_value{ value } {} bool operator()(int x, int y) const { return std::abs(x - m_value) < std::abs(y - m_value); } private: int m_value; }; ================================================ FILE: Examples/Modules/Chapter 19/Ex19_04/Optimum.cppm ================================================ // Optimum.cppm - a function template to determine the optimum element in a given vector export module optimum; import ; export template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_05/Ex19_05.cpp ================================================ // Exercising the use of stateless lambda expressions as callback functions import ; import ; import ; import ; import optimum; int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *findOptimum(numbers, [](int x, int y) { return x < y; }) << std::endl; std::cout << "Maximum element: " << *findOptimum(numbers, [](int x, int y) { return x > y; }) << std::endl; // Define two anonymous comparison functions for strings: auto alpha{ [](std::string_view x, std::string_view y) { return x > y; } }; auto longer{ [](std::string_view x, std::string_view y) { return x.length() > y.length(); } }; std::vector names{ "Moe", "Larry", "Shemp", "Curly", "Joe", "Curly Joe" }; std::cout << "Alphabetically last name: " << *findOptimum(names, alpha) << std::endl; std::cout << "Longest name: " << *findOptimum(names, longer) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_05/Optimum.cppm ================================================ // Optimum.cppm - a function template to determine the optimum element in a given vector export module optimum; import ; export template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_05A/Ex19_05A.cpp ================================================ // If possible, using the standard functors instead of lambda expressions // often leads to more compact and elegant code. import ; import ; import ; import ; import ; import optimum; int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *findOptimum(numbers, std::less<>{}) << std::endl; std::cout << "Maximum element: " << *findOptimum(numbers, std::greater<>{}) << std::endl; // Define two anonymous comparison functions for strings: auto alpha{ std::greater<>{} }; auto longer{ [](std::string_view x, std::string_view y) { return x.length() > y.length(); } }; std::vector names{ "Moe", "Larry", "Shemp", "Curly", "Joe", "Curly Joe" }; std::cout << "Alphabetically last name: " << *findOptimum(names, alpha) << std::endl; std::cout << "Longest name: " << *findOptimum(names, longer) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_05A/Optimum.cppm ================================================ // Optimum.cppm - a function template to determine the optimum element in a given vector export module optimum; import ; export template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_06/Ex19_06.cpp ================================================ // Using a default capture-by-value clause to access a local variable // from within the body of a lambda expression. import ; import ; import optimum; int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; int number_to_search_for {}; std::cout << "Please enter a number: "; std::cin >> number_to_search_for; auto nearer { [=](int x, int y) { return std::abs(x - number_to_search_for) < std::abs(y - number_to_search_for); }}; std::cout << "The number nearest to " << number_to_search_for << " is " << *findOptimum(numbers, nearer) << std::endl; //unsigned count{}; //auto counter{ [&](int x, int y) { ++count; return x < y; } }; //findOptimum(numbers, counter); //std::cout << "Number of comparisons: " << count; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_06/Optimum.cppm ================================================ // Optimum.cppm - a function template to determine the optimum element in a given vector export module optimum; import ; export template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_07/Ex19_07.cpp ================================================ // Using a lambda expression from inside a member function (see Finder.cpp) import ; import ; import optimum; import finder; int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; int number_to_search_for {}; std::cout << "Please enter a number: "; std::cin >> number_to_search_for; Finder finder; finder.setNumberToSearchFor(number_to_search_for); std::cout << "The number nearest to " << finder.getNumberToSearchFor() << " is " << *finder.findNearest(numbers) << std::endl; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_07/Finder.cpp ================================================ // Exercising capturing the this pointer module; #include // For std::abs() module finder; import optimum; std::optional Finder::findNearest(const std::vector& values) const { if (values.empty()) return std::nullopt; else return *findOptimum(values, [this](double x, double y) { return std::abs(x - m_number_to_search_for) < std::abs(y - m_number_to_search_for); }); } double Finder::getNumberToSearchFor() const { return m_number_to_search_for; } void Finder::setNumberToSearchFor(double value) { m_number_to_search_for = value; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_07/Finder.cppm ================================================ // Finder.cppm - A small class to illustrate the use of lambda expression in member functions export module finder; import ; import ; export class Finder { public: double getNumberToSearchFor() const; void setNumberToSearchFor(double n); std::optional findNearest(const std::vector& values) const; private: double m_number_to_search_for {}; }; ================================================ FILE: Examples/Modules/Chapter 19/Ex19_07/Optimum.cppm ================================================ // Optimum.cppm - a function template to determine the optimum element in a given vector export module optimum; import ; export template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } ================================================ FILE: Examples/Modules/Chapter 19/Ex19_08/Ex19_08.cpp ================================================ // Using the std::function<> template import ; import ; #include // for std::abs() // A global less() function bool less(int x, int y) { return x < y; } int main() { int a{ 18 }, b{ 8 }; std::cout << std::boolalpha; // Print true/false rather than 1/0 std::function compare; compare = less; // Store a function pointer into compare std::cout << a << " < " << b << ": " << compare(a, b) << std::endl; compare = std::greater<>{}; // Store a function object into compare std::cout << a << " > " << b << ": " << compare(a, b) << std::endl; int n{ 10 }; // Store a lambda closure into compare compare = [n](int x, int y) { return std::abs(x - n) < std::abs(y - n); }; std::cout << a << " nearer to " << n << " than " << b << ": " << compare(a, b); // Check whether a function<> object is tied to an actual function std::function empty; if (empty) // Or, equivalently: 'if (empty != nullptr)' { std::cout << "Calling a default-constructed std::function<>?" << std::endl; empty(a); } } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_01/Ex20_01.cpp ================================================ // Working with std::deque<> import ; import ; int main() { std::deque my_deque; // A deque<> allows efficient insertions my_deque.push_back(2); // to both ends of the sequence my_deque.push_back(4); my_deque.push_front(1); my_deque[2] = 3; // A deque<> is a random-access sequence container std::cout << "There are " << my_deque.size() << " elements in my_deque: "; for (int element : my_deque) // A deque<>, like all containers, is a range std::cout << element << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_02/Ex20_02.cpp ================================================ // Working with stacks and queues import ; import ; import ; int main() { std::stack stack; for (int i {}; i < 10; ++i) stack.push(i); std::cout << "The elements coming off the top of the stack: "; while (!stack.empty()) { std::cout << stack.top() << ' '; stack.pop(); // pop() is a void function! } std::cout << std::endl; std::queue queue; for (int i {}; i < 10; ++i) queue.push(i); std::cout << "The elements coming from the front of the queue: "; while (!queue.empty()) { std::cout << queue.front() << ' '; queue.pop(); // pop() is a void function! } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_03/Ex20_03.cpp ================================================ // Working with sets import ; import ; // For the std::set<> container template void printSet(const std::set& my_set); // Print the contents of a set to std::cout int main() { std::set my_set; // Insert elements 1 through 4 in arbitrary order: my_set.insert(1); my_set.insert(4); my_set.insert(3); my_set.insert(3); // Elements 3 and 1 are added twice my_set.insert(1); my_set.insert(2); printSet(my_set); std::cout << "The element 1 occurs " << my_set.count(1) << " time(s)" << std::endl; my_set.erase(1); // Remove the element 1 once printSet(my_set); my_set.clear(); // Remove all elements printSet(my_set); } void printSet(const std::set& my_set) { std::cout << "There are " << my_set.size() << " elements in my_set: "; for (int element : my_set) // A set, like all containers, is a range std::cout << element << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_04/Ex20_04.cpp ================================================ // Basic use of std::map<> import ; import ; import ; int main() { std::map phone_book; phone_book["Donald"] = 202'456'1111; phone_book["Melania"] = 202'456'1111; phone_book["Francis"] = 39'06'6982; phone_book["Elizabeth"] = 44'020'7930'4832; std::cout << "The pope's number is " << phone_book["Francis"] << std::endl; for (const auto& [name, number] : phone_book) std::cout << name << " can be reached at " << number << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_05/Ex20_05.cpp ================================================ // Working with maps import ; import ; import ; import ; import ; import ; // Type aliases using Words = std::vector; using WordCounts = std::map; // Function prototypes Words extractWords(std::string_view text, std::string_view separators = " ,.!?\"\n"); WordCounts countWords(const Words& words); void showWordCounts(const WordCounts& wordCounts); size_t maxWordLength(const WordCounts& wordCounts); int main() { std::string text; // The string to count words in // Read a string from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); const Words words{ extractWords(text) }; if (words.empty()) { std::cout << "No words in text." << std::endl; return 0; } WordCounts wordCounts = countWords(words); showWordCounts(wordCounts); } Words extractWords(std::string_view text, std::string_view separators) { Words words; size_t start{ text.find_first_not_of(separators) }; // Start 1st word size_t end{}; // Index for the end of a word while (start != std::string_view::npos) { end = text.find_first_of(separators, start + 1); // Find end separator if (end == std::string_view::npos) // End of text? end = text.length(); // Yes, so set to last+1 words.push_back(text.substr(start, end - start)); start = text.find_first_not_of(separators, end + 1); // Find next word } return words; } WordCounts countWords(const Words& words) { WordCounts result; for (auto& word : words) ++result[word]; return result; } size_t maxWordLength(const WordCounts& wordCounts) { size_t max{}; for (const auto& [word, count] : wordCounts) if (count >= 2 && max < word.length()) max = word.length(); return max; } void showWordCounts(const WordCounts& wordCounts) { const size_t field_width{maxWordLength(wordCounts) + 1}; const size_t words_per_line{5}; size_t words_in_line{}; // Number of words in the current line char previous_initial{}; for (const auto& [word, count] : wordCounts) { if (count < 2) continue; // Skip words that appear only once // Output newline when initial letter changes or after 5 per line if ( (previous_initial && word[0] != previous_initial) || words_in_line++ == words_per_line) { words_in_line = 0; std::cout << std::endl; } // Output "word (count)", where word has a dynamic field width std::cout << std::format("{:>{}} ({:2})", word, field_width, count); previous_initial = word[0]; } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_06/Ex20_06.cpp ================================================ // Creating and working with Standard iterators import ; import ; int main() { std::vector letters{ 'a', 'b', 'c', 'd', 'e' }; auto my_iter{ letters.begin() }; std::cout << *my_iter << std::endl; // a *my_iter = 'x'; std::cout << letters[0] << std::endl; // x ++my_iter; // Move my_iter to the next element std::cout << *my_iter << std::endl; // b my_iter += 2; // Move my_iter two elements further std::cout << *my_iter-- << std::endl; // d std::cout << *my_iter << std::endl; // c (iterator altered using the post-decrement // operator in the previous statement) auto copy{ my_iter }; // Create a copy of my_iter (pointing at c) my_iter += 2; // Move my_iter two elements further std::cout << *copy << std::endl; // c (copy not affected by moving my_iter) std::cout << *my_iter << std::endl; // e std::cout << my_iter - copy << std::endl; // 2 } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_07/Ex20_07.cpp ================================================ // Iterating over the elements of a list<> import ; import ; int main() { std::cout << "Enter a sequence of positive numbers, terminated by -1: "; std::list numbers; while (true) { signed number{ -1 }; std::cin >> number; if (number == -1) break; numbers.push_back(static_cast(number)); } std::cout << "You entered the following numbers: "; for (auto iter{ numbers.begin() }; iter != numbers.end(); ++iter) { std::cout << *iter << ' '; } std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_08/Box.cppm ================================================ export module box; // Class to represent a box export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } {} double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } void setLength(double length) { if (length > 0) m_length = length; } void setWidth(double width) { if (width > 0) m_width = width; } void setHeight(double height) { if (height > 0) m_height = height; } // Function to calculate the volume of a box double volume() const { return m_length * m_width * m_height; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; ================================================ FILE: Examples/Modules/Chapter 20/Ex20_08/Ex20_08.cpp ================================================ // Altering elements through a mutable iterator import ; import ; import box; // From Ex11_04 int main() { std::vector boxes{ Box{ 1.0, 2.0, 3.0 } }; // A vector containing 1 Box auto iter{ boxes.begin() }; std::cout << iter->volume() << std::endl; // 6 == 1.0 * 2.0 * 3.0 *iter = Box{ 2.0, 3.0, 4.0 }; std::cout << iter->volume() << std::endl; // 24 == 2.0 * 3.0 * 4.0 iter->setHeight(7.0); std::cout << iter->volume() << std::endl; // 42 == 2.0 * 3.0 * 7.0 } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_09/Ex20_09.cpp ================================================ // Inserting in and erasing from sequence containers import ; import ; void printVector(const std::vector& v); int main() { std::vector numbers{ 2, 4, 5 }; // Deduced type: std::vector numbers.insert(numbers.begin(), 1); // Add single element to the beginning of the sequence printVector(numbers); // 1 2 4 5 numbers.insert(numbers.begin() + numbers.size() / 2, 3); // Add in the middle printVector(numbers); // 1 2 3 4 5 std::vector more_numbers{ 6, 7, 8 }; numbers.insert(numbers.end(), more_numbers.begin(), more_numbers.end()); printVector(numbers); // 1 2 3 4 5 6 7 8 numbers.erase(numbers.end() - 3, numbers.end()); // Erase last 3 elements numbers.erase(numbers.begin() + numbers.size() / 2); // Erase the middle element numbers.erase(numbers.begin()); // Erase the first element printVector(numbers); // 2 4 5 } void printVector(const std::vector& v) { for (auto i : v) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_10/Ex20_10.cpp ================================================ // Removing all elements that satisfy a certain condition // while iterating over a container import ; import ; import ; std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); void removeEvenNumbers(auto& numbers) /* Correct! */ { for (auto iter{ numbers.begin() }; iter != numbers.end(); ) { if (*iter % 2 == 0) iter = numbers.erase(iter); else ++iter; } } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers; for (int i{ 1 }; i <= N; ++i) numbers.push_back(i); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_11/Ex20_11.cpp ================================================ // Your first algorithms: std::min_element() and max_element() import ; import ; import ; #include // For std::abs() int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *std::min_element(begin(numbers), end(numbers)) << std::endl; std::cout << "Maximum element: " << *std::max_element(begin(numbers), end(numbers)) << std::endl; int number_to_search_for {}; std::cout << "Please enter a number: "; std::cin >> number_to_search_for; auto nearer { [=](int x, int y) { return std::abs(x - number_to_search_for) < std::abs(y - number_to_search_for); }}; std::cout << "The number nearest to " << number_to_search_for << " is " << *std::min_element(begin(numbers), end(numbers), nearer) << std::endl; std::cout << "The number furthest from " << number_to_search_for << " is " << *std::max_element(begin(numbers), end(numbers), nearer) << std::endl; /* const auto [nearest, furthest] { std::minmax_element(begin(numbers), end(numbers), nearer) }; std::cout << "The number nearest to " << number_to_search_for << " is " << *nearest << std::endl; std::cout << "The number furthest from " << number_to_search_for << " is " << *furthest << std::endl; */ } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_11A/Ex20_11A.cpp ================================================ // Your first algorithms: std::min_element() and max_element(), // this time using the range-based versions. import ; import ; import ; #include // For std::abs() int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *std::ranges::min_element(numbers) << std::endl; std::cout << "Maximum element: " << *std::ranges::max_element(numbers) << std::endl; int number_to_search_for {}; std::cout << "Please enter a number: "; std::cin >> number_to_search_for; auto nearer { [=](int x, int y) { return std::abs(x - number_to_search_for) < std::abs(y - number_to_search_for); }}; std::cout << "The number nearest to " << number_to_search_for << " is " << *std::ranges::min_element(numbers, nearer) << std::endl; std::cout << "The number furthest from " << number_to_search_for << " is " << *std::ranges::max_element(numbers, nearer) << std::endl; /* const auto [nearest, furthest] { std::ranges::minmax_element(numbers, nearer) }; std::cout << "The number nearest to " << number_to_search_for << " is " << *nearest << std::endl; std::cout << "The number furthest from " << number_to_search_for << " is " << *furthest << std::endl; */ } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_12/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; ================================================ FILE: Examples/Modules/Chapter 20/Ex20_12/Ex20_12.cpp ================================================ // Finding boxes. import ; import ; import ; import box; // From Ex13_03A int main() { std::vector boxes{ Box{1,2,3}, Box{5,2,3}, Box{9,2,1}, Box{3,2,1} }; // Define a lambda functor to print the result of find() or find_if(): auto print_result{ [&boxes] (auto result) { if (result == end(boxes)) std::cout << "No box found." << std::endl; else std::cout << "Found matching box at position " << (result - begin(boxes)) << std::endl; }}; // Find an exact box Box box_to_find{ 3,2,1 }; auto result{ std::find(begin(boxes), end(boxes), box_to_find) }; print_result(result); // Find a box with a volume larger than that of box_to_find const auto required_volume{ box_to_find.volume() }; result = std::find_if(begin(boxes), end(boxes), [required_volume](const Box& box) { return box > required_volume; }); print_result(result); } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_12A/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; ================================================ FILE: Examples/Modules/Chapter 20/Ex20_12A/Ex20_12A.cpp ================================================ // Finding boxes, this time using range-based algorithms. import ; import ; import ; import box; // From Ex13_03A int main() { std::vector boxes{ Box{1,2,3}, Box{5,2,3}, Box{9,2,1}, Box{3,2,1} }; // Define a lambda functor to print the result of find() or find_if(): auto print_result{ [&boxes](auto result) { if (result == end(boxes)) std::cout << "No box found." << std::endl; else std::cout << "Found matching box at position " << (result - begin(boxes)) << std::endl; }}; // Find an exact box Box box_to_find{ 3,2,1 }; auto result{ std::ranges::find(boxes, box_to_find) }; print_result(result); // Find a box with a volume larger than that of box_to_find const auto required_volume{ box_to_find.volume() }; result = std::ranges::find_if(boxes, [required_volume](const Box& box) { return box > required_volume; }); print_result(result); } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_13/Ex20_13.cpp ================================================ // Extracting all odd numbers. import ; import ; import ; import ; std::set fillSet_1toN(size_t N); // Fill a set with 1, 2, ..., N void printVector(const std::vector& v); // Print the contents of a vector to std::cout int main() { const size_t num_numbers{20}; const auto numbers{ fillSet_1toN(num_numbers) }; std::vector odd_numbers( numbers.size() ); // Caution: not { numbers.size() } here! auto end_odd_numbers{ std::copy_if(begin(numbers), end(numbers), begin(odd_numbers), [](int n) { return n % 2 == 1; }) }; odd_numbers.erase(end_odd_numbers, end(odd_numbers)); printVector(odd_numbers); } std::set fillSet_1toN(size_t N) // Fill a set with 1, 2, ..., N { std::set numbers; for (int i{ 1 }; i <= N; ++i) numbers.insert(i); return numbers; } void printVector(const std::vector& v) { for (auto i : v) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_13A/Ex20_13A.cpp ================================================ // Extracting all odd numbers using std::back_inserter(). import ; import ; import ; import ; import ; // For std::back_inserter() std::set fillSet_1toN(size_t N); // Fill a set with 1, 2, ..., N void printVector(const std::vector& v); // Print the contents of a vector to std::cout int main() { const size_t num_numbers{20}; const auto numbers{ fillSet_1toN(num_numbers) }; std::vector odd_numbers; std::copy_if(begin(numbers), end(numbers), back_inserter(odd_numbers), [](int n) { return n % 2 == 1; }); printVector(odd_numbers); } std::set fillSet_1toN(size_t N) // Fill a set with 1, 2, ..., N { std::set numbers; for (int i{ 1 }; i <= N; ++i) numbers.insert(i); return numbers; } void printVector(const std::vector& v) { for (auto i : v) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_13B/Ex20_13B.cpp ================================================ // Extracting all odd numbers using std::back_inserter(). // This time using the range-based version of std::copy_if(). import ; import ; import ; import ; import ; // For std::back_inserter() std::set fillSet_1toN(size_t N); // Fill a set with 1, 2, ..., N void printVector(const std::vector& v); // Print the contents of a vector to std::cout int main() { const size_t num_numbers{ 20 }; const auto numbers{ fillSet_1toN(num_numbers) }; std::vector odd_numbers; std::ranges::copy_if(numbers, back_inserter(odd_numbers), [](int n) { return n % 2 == 1; }); printVector(odd_numbers); } std::set fillSet_1toN(size_t N) // Fill a set with 1, 2, ..., N { std::set numbers; for (int i{ 1 }; i <= N; ++i) numbers.insert(i); return numbers; } void printVector(const std::vector& v) { for (auto i : v) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_14/Ex20_14.cpp ================================================ // Removing all elements that satisfy a certain condition // usign the remove-erase idiom import ; import ; import ; import ; std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); void removeEvenNumbers(std::vector& numbers) { // Use the remove_if() algorithm to remove all even numbers auto first_to_erase{ std::remove_if(begin(numbers), end(numbers), [](int number) { return number % 2 == 0; }) }; // Erase all elements including and beyond first_to_erase numbers.erase(first_to_erase, end(numbers)); } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers; for (int i{ 1 }; i <= N; ++i) numbers.push_back(i); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_14A/Ex20_14A.cpp ================================================ // Removing all elements that satisfy a certain condition usign std::erase_if() import ; import ; import ; std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); void removeEvenNumbers(std::vector& numbers) { std::erase_if(numbers, [](int number) { return number % 2 == 0; }); } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers; for (int i{ 1 }; i <= N; ++i) numbers.push_back(i); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_14B/Ex20_14B.cpp ================================================ // Removing all elements that satisfy a certain condition // usign the remove-erase idiom and the range-based version of std::remove_if. // Unlike the iterator-based version, std::ranges::erase_if() returns a subrange, // and not an iterator. // Note also that in this case std::erase_if() is even more compact (see Ex20_14A). import ; import ; import ; import ; std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); void removeEvenNumbers(std::vector& numbers) { // Use the remove_if() algorithm to remove all even numbers auto [first_to_erase, last_to_erase] { std::ranges::remove_if(numbers, [](int number) { return number % 2 == 0; }) }; // Erase all elements including and beyond first_to_erase numbers.erase(first_to_erase, last_to_erase); } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers; for (int i{ 1 }; i <= N; ++i) numbers.push_back(i); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_15/Ex20_15.cpp ================================================ // Sorting strings import ; import ; import ; import ; int main() { std::vector names{"Frodo Baggins", "Gandalf the Gray", "Aragon", "Samwise Gamgee", "Peregrin Took", "Meriadoc Brandybuck", "Gimli", "Legolas Greenleaf", "Boromir"}; // Sort the names lexicographically std::sort(begin(names), end(names)); std::cout << "Names sorted lexicographically:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl << std::endl; // Sort the names by length std::sort(begin(names), end(names), [](const auto& left, const auto& right) {return left.length() < right.length(); }); std::cout << "Names sorted by length:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_15A/Ex20_15A.cpp ================================================ // Sorting strings using range-based sort() import ; import ; import ; import ; int main() { std::vector names{"Frodo Baggins", "Gandalf the Gray", "Aragon", "Samwise Gamgee", "Peregrin Took", "Meriadoc Brandybuck", "Gimli", "Legolas Greenleaf", "Boromir"}; // Sort the names lexicographically std::ranges::sort(names); std::cout << "Names sorted lexicographically:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl << std::endl; // Sort the names by length std::ranges::sort(names, [](const auto& left, const auto& right) {return left.length() < right.length(); }); std::cout << "Names sorted by length:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_16/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; ================================================ FILE: Examples/Modules/Chapter 20/Ex20_16/Ex20_16.cpp ================================================ // Using range adaptors import ; import ; import ; import ; // For std::back_inserter() import ; import box; using namespace std::ranges::views; int main() { // Our challenge: given a sequence of boxes, // gather pointers to all boxes larger than required_volume std::vector boxes{ { 1, 2, 3 }, { 4, 5, 6}, { 7, 8, 9 }, { 10, 11, 12 } }; const double required_volume{ 200 }; // Without range-based algorithms and range adaptors (in two steps): std::vector box_pointers; std::transform(begin(boxes), end(boxes), back_inserter(box_pointers), [](Box& box) { return &box; }); std::vector large_boxes; std::copy_if(begin(box_pointers), end(box_pointers), back_inserter(large_boxes), [=](const Box* box) { return *box >= required_volume; }); std::cout << "There are " << large_boxes.size() << " large boxes." << std::endl; // With range-based algorithm copy() and a filter-transform pipeline std::vector large_boxes_ranges_copy; std::ranges::copy( boxes | filter([=](const Box& box) { return box >= required_volume; }) | transform([](Box& box) { return &box; }), back_inserter(large_boxes_ranges_copy) ); // With range-based algorithm copy_if() and transform adaptor std::vector large_boxes_ranges_copy_if; std::ranges::copy_if( /* Transform using adaptor before filtering in copy_if() */ boxes | transform([](Box& box) { return &box; }), // Input view of boxes back_inserter(large_boxes_ranges_copy_if), // Output iterator [=](const Box* box) { return *box >= required_volume; } // Condition for copy_if() ); // With range-based algorithm transform() and a filter adaptor std::vector large_boxes_ranges_transform; std::ranges::transform( /* Filter using adaptor before transforming using algorithm */ boxes | filter([=](const Box& box) { return box >= required_volume; }), back_inserter(large_boxes_ranges_transform), // Output iterator [](Box& box) { return &box; } // Transform functor of transform() ); if (large_boxes_ranges_copy != large_boxes || large_boxes_ranges_copy_if != large_boxes || large_boxes_ranges_transform != large_boxes) { std::cerr << "Oh dear... One of the range-based algorithms gave a different result!" << std::endl; } } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_17/Ex20_17.cpp ================================================ // Range factories and range adaptors import ; import ; // For views, range factories, and range adaptors using namespace std::ranges::views; bool isEven(int i) { return i % 2 == 0; } int squared(int i) { return i * i; } int main() { for (int i : iota(1, 10)) // Lazily generate range [1,10) std::cout << i << ' '; std::cout << std::endl; for (int i : iota(1, 1000) | filter(isEven) | transform(squared) | drop(2) | take(5) | reverse) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 20/Ex20_18/Ex20_18.cpp ================================================ // Writing through a view import ; import ; import ; // For views, range factories, and range adaptors bool isEven(int i) { return i % 2 == 0; } int main() { std::vector numbers{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; for (int& i : numbers | std::ranges::views::filter(isEven)) i *= i; for (int i : numbers) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/Modules/Chapter 21/Ex21_01/Ex21_01.cpp ================================================ // Class template instantiation errors import ; import ; import ; import ; class MyClass { /* just a dummy class */ }; int main() { std::vector v; MyClass one, other; auto biggest{ std::max(one, other) }; std::set objects; objects.insert(MyClass{}); std::list numbers{ 4, 1, 3, 2 }; std::sort(begin(numbers), end(numbers)); } ================================================ FILE: Examples/Modules/Chapter 21/Ex21_02/Ex21_02.cpp ================================================ // Asserting that a type models a concept import ; // For the std::same_as<> and std::convertible_to<> concepts import ; // For std::ranges::range<> concept import ; // For the std::remove_cv<> type trait import ; import ; import ; template concept BidirectionalIterator = true; // Feel free to further work out all requirements... template concept RandomAccessIterator = BidirectionalIterator && requires(const Iter i, const Iter j, Iter k, const int n) { { i - n } -> std::same_as; { i + n } -> std::same_as; { n + i } -> std::same_as; { k += n }-> std::same_as; { k -= n }-> std::same_as; { i[n] } -> std::same_as; { i < j } -> std::convertible_to; { i > j } -> std::convertible_to; { i <= j } -> std::convertible_to; { i >= j } -> std::convertible_to; }; template concept NoExceptDestructible = requires (T & value) { { value.~T() } noexcept; }; template concept Character = std::same_as, char> || std::same_as, char8_t> || std::same_as, char16_t> || std::same_as, char32_t> || std::same_as, wchar_t>; template concept String = std::ranges::range && requires(S & s, const S & cs) { typename S::value_type; requires Character; { cs.length() } -> std::integral; { s[0] } -> std::same_as; { cs[0] } -> std::convertible_to; { s.data() } -> std::same_as; { cs.data() } -> std::same_as; // ... }; static_assert(NoExceptDestructible); static_assert(NoExceptDestructible); static_assert(String); static_assert(!String>); static_assert(Character); static_assert(Character); static_assert(RandomAccessIterator::iterator>); static_assert(!RandomAccessIterator::iterator>); static_assert(RandomAccessIterator); int main() { } ================================================ FILE: Examples/Modules/Chapter 21/Ex21_03/Array.cppm ================================================ export module array; import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; export template requires std::default_initializable && std::destructible class Array { public: Array(); explicit Array(size_t size); ~Array(); Array(const Array& array) requires std::copyable; Array& operator=(const Array& rhs) requires std::copyable; Array(Array&& array) noexcept requires std::movable; Array& operator=(Array&& rhs) noexcept requires std::movable; void swap(Array& other) noexcept; T& operator[](size_t index); const T& operator[](size_t index) const; size_t getSize() const { return m_size; } void push_back(T element) requires std::movable; private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template requires std::default_initializable && std::destructible Array::Array() : Array{0} {} // Constructor template template requires std::default_initializable && std::destructible Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template requires std::default_initializable && std::destructible Array::Array(const Array& array) requires std::copyable : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template requires std::default_initializable && std::destructible Array::Array(Array&& moved) noexcept requires std::movable : m_size{moved.m_size}, m_elements{moved.m_elements} { moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template requires std::default_initializable && std::destructible Array::~Array() { delete[] m_elements; } // const subscript operator template template requires std::default_initializable && std::destructible const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template requires std::default_initializable && std::destructible T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template requires std::default_initializable && std::destructible Array& Array::operator=(const Array& rhs) requires std::copyable { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template requires std::default_initializable && std::destructible Array& Array::operator=(Array&& rhs) noexcept requires std::movable { if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template requires std::default_initializable && std::destructible void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template requires std::default_initializable && std::destructible void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template requires std::default_initializable && std::destructible void Array::push_back(T element) requires std::movable { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i{}; i < m_size; ++i) // Move all existing elements... newArray[i] = std::move(m_elements[i]); newArray[m_size] = std::move(element); // Move the new one (could itself be a copy already)... swap(newArray); // ... and swap! } ================================================ FILE: Examples/Modules/Chapter 21/Ex21_03/Ex21_03.cpp ================================================ // Violating constraints of uninstantiated class members import array; import ; // For std::unique_ptr<> // Assert that Array> is a valid type static_assert(requires { typename Array>; }); int main() { Array> tenSmartPointers(10); Array> target; // target = tenSmartPointers; /* Constraint not satisfied: copyable */ target = std::move(tenSmartPointers); target.push_back(std::make_unique(123)); } ================================================ FILE: Examples/Modules/Chapter 21/Ex21_04/Ex21_04.cpp ================================================ // Constraint based specialization import ; // For the std::equality_comparable<> concept import ; // The iterator concepts and the iter_difference_t<>() trait import ; import ; import ; // Precondition: incrementing first eventually leads to last template requires std::equality_comparable auto distanceBetween(Iter first, Iter last) { std::cout << "Distance determined by linear traversal: "; std::iter_difference_t result{}; while (first != last) { ++first; ++result; } return result; } template auto distanceBetween(Iter first, Iter last) { std::cout << "Distance determined in constant time: "; return last - first; } int main() { std::list l{ 'a', 'b', 'c' }; std::vector v{ 1, 2, 3, 4, 5 }; float a[] { 1.2f, 3.4f, 4.5f }; std::cout << distanceBetween(cbegin(l), cend(l)) << std::endl; std::cout << distanceBetween(begin(v), end(v)) << std::endl; std::cout << distanceBetween(a, a + std::size(a)) << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_01/ExA_01.cpp ================================================ // Defining object-like macros #include #define POINTER_SIZE sizeof(int*) * BITS_PER_BYTE #define BITS_PER_BYTE 8 int main() { std::cout << POINTER_SIZE << std::endl; // 32 or 64, normally } // The next macro (while providing a perfectly valid definition for "main") // is never applied, because it is not defined yet when the preprocessor // processes the line "int main()". #define main Of chief or leading importance; prime, principal. ================================================ FILE: Examples/NoModules/Appendix A/ExA_02/ExA_02.cpp ================================================ // Working with preprocessor operators #include #define DEFINE_PRINT_FUNCTION(NAME, COUNT, VALUE) \ void NAME##COUNT() { std::cout << #VALUE << std::endl; } DEFINE_PRINT_FUNCTION(fun, 123, Test 1 "2" 3) int main() { fun123(); } ================================================ FILE: Examples/NoModules/Appendix A/ExA_02A/ExA_02A.cpp ================================================ // Fun with line continuations #include #define DEFINE_PRINT_FUNCT\ ION(NAME, COUNT, VALUE) vo\ id NAME##COUNT() { std::co\ ut << #VALUE << std::endl; } DEFINE_PRINT_FUNCTION(fun, 123, Test 1 "2" 3) \ i\ nt\ ma\ in()\ {fun1\ 23();}\ ================================================ FILE: Examples/NoModules/Appendix A/ExA_03/ExA_03.cpp ================================================ // Using std::source_location, C++'s answer to __LINE__ and __FILE__ #include #include #include #include void logError(std::string_view message, std::source_location location = std::source_location::current()) { std::cerr << std::format("{}:{}:{} - An unexpected error occurred in {}: {}", location.file_name(), location.line(), location.column(), location.function_name(), message) << std::endl; } int main() { logError("OOPS!"); // The line number in the log message will refer to // this line in main(), not to the where logError() is defined. } ================================================ FILE: Examples/NoModules/Appendix A/ExA_04/ExA_04.cpp ================================================ // Demonstrating assertions #include #include // Uncomment to disable the assertion in main() (needs to be defined before including ) //#define NDEBUG #include int main() { int y{ 5 }; for (int x{}; x < 20; ++x) { std::cout << std::format("x = {}\ty = {}", x, y) << std::endl; assert(x < y); } } // A static assertion (comment out to build as a 32-bit program) static_assert(sizeof(int*) > 4, "32-bit compilation is not supported."); ================================================ FILE: Examples/NoModules/Appendix A/ExA_05/ExA_05.cpp ================================================ // ExA_05.cpp // Introducing the #include directive #include int main() { #include "inclusivity.quote" } ================================================ FILE: Examples/NoModules/Appendix A/ExA_05/inclusivity.quote ================================================ std::cout << "We are trying to construct a more inclusive society.\n"; std::cout << "We are going to make a country in which no one is left out.\n" << "\t\t\t\t- Franklin D. Roosevelt" << std::endl; ================================================ FILE: Examples/NoModules/Appendix A/ExA_06/ExA_06.cpp ================================================ // Defining the same function twice (won't compile!) #include #include double power(double x, int n); double power(double x, int n); // Redundant declaration (harmless) int main() { for (int i{ -3 }; i <= 3; ++i) // Calculate powers of 8 from -3 to +3 std::cout << std::format("{:10}\n", power(8.0, i)); std::cout << std::endl; } double power(double x, int n) // A first definition { if (n == 0) return 1.0; else if (n > 0) return x * power(x, n - 1); else /* n < 0 */ return 1.0 / power(x, -n); } double power(double x, int n); // Another redundant declaration (harmless) double power(double x, int n) // A second, more efficient definition (error!) { if (n < 0) return 1.0 / power(x, -n); // Deal with negative exponents if (n == 0) return 1.0; // Base case of the recursion const double y{ power(x, n / 2) }; // See Exercise 8-8 for an explanation return y * y * (n % 2 == 1 ? x : 1.0); } ================================================ FILE: Examples/NoModules/Appendix A/ExA_07/ExA_07.cpp ================================================ // Calling external functions #include #include double power(double x, int n); // Declaration of an external power() function int main() { for (int i{ -3 }; i <= 3; ++i) // Calculate powers of 8 from -3 to +3 std::cout << std::format("{:10}\n", power(8.0, i)); std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_07/Power.cpp ================================================ // The power function called from ExA_07.cpp is defined in a different translation unit double power(double x, int n) { if (n < 0) return 1.0 / power(x, -n); // Deal with negative exponents if (n == 0) return 1.0; // Recursion base case const double y{ power(x, n / 2) }; // See Exercise 8-8 for an explanation return y * y * (n % 2 == 1 ? x : 1.0); } ================================================ FILE: Examples/NoModules/Appendix A/ExA_07A/ExA_07A.cpp ================================================ // Calling external functions that are declared in a header file #include #include #include "Power.h" int main() { for (int i{ -3 }; i <= 3; ++i) // Calculate powers of 8 from -3 to +3 std::cout << std::format("{:10}\n", power(8.0, i)); std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_07A/Power.cpp ================================================ // The power function called from ExA_07.cpp is defined in a different translation unit double power(double x, int n) { if (n < 0) return 1.0 / power(x, -n); // Deal with negative exponents if (n == 0) return 1.0; // Recursion base case const double y{ power(x, n / 2) }; // See Exercise 8-8 for an explanation return y * y * (n % 2 == 1 ? x : 1.0); } ================================================ FILE: Examples/NoModules/Appendix A/ExA_07A/Power.h ================================================ // Your first header file containing a declaration of an external power() function double power(double x, int n); ================================================ FILE: Examples/NoModules/Appendix A/ExA_08/ExA_08.cpp ================================================ // Using an externally defined variable #include #include /*extern*/ double power(double x, int n); // Declaration of an external power() function /*extern*/ int power_range; // Not an unreasonable first attempt, right? int main() { for (int i{ -power_range }; i <= power_range; ++i) // Calculate powers of 8 std::cout << std::format("{:10}\n", power(8.0, i)); std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_08/Power.cpp ================================================ // The power function called from ExA_07.cpp is defined in a different translation unit double power(double x, int n) { if (n < 0) return 1.0 / power(x, -n); // Deal with negative exponents if (n == 0) return 1.0; // Recursion base case const double y{ power(x, n / 2) }; // See Exercise 8-8 for an explanation return y * y * (n % 2 == 1 ? x : 1.0); } ================================================ FILE: Examples/NoModules/Appendix A/ExA_08/Range.cpp ================================================ int power_range{ 3 }; // A global variable with external linkage ================================================ FILE: Examples/NoModules/Appendix A/ExA_08A/ExA_08A.cpp ================================================ // Using an externally defined constant #include #include extern double power(double x, int n); // Declaration of an external power() function extern const int power_range; // Declaration of an external global constant int main() { for (int i{ -power_range }; i <= power_range; ++i) // Calculate powers of 8 std::cout << std::format("{:10}\n", power(8.0, i)); std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_08A/Power.cpp ================================================ // The power function called from ExA_07.cpp is defined in a different translation unit double power(double x, int n) { if (n < 0) return 1.0 / power(x, -n); // Deal with negative exponents if (n == 0) return 1.0; // Recursion base case const double y{ power(x, n / 2) }; // See Exercise 8-8 for an explanation return y * y * (n % 2 == 1 ? x : 1.0); } ================================================ FILE: Examples/NoModules/Appendix A/ExA_08A/Range.cpp ================================================ extern const int power_range{ 3 }; // Definition of a global constant with external linkage ================================================ FILE: Examples/NoModules/Appendix A/ExA_09/ExA_09.cpp ================================================ // Using a local helper function with internal linkage (see Power.cpp) #include #include extern double power(double x, int n); // Declaration of an external power() function int main() { for (int i{ -3 }; i <= 3; ++i) // Calculate powers of 8 std::cout << std::format("{:10}\n", power(8.0, i)); std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_09/Power.cpp ================================================ // A local helper function with internal linkage namespace { double localHelper(double x, unsigned n) // localHelper() has internal linkage { if (n == 0) return 1.0; // Recursion base case const double y{ localHelper(x, n / 2) }; // See Exercise 8-8 for an explanation return y * y * (n % 2 == 1 ? x : 1.0); } } double power(double x, int n) // power() has external linkage { return n >= 0 ? localHelper(x, static_cast(n)) : 1.0 / localHelper(x, static_cast(-n)); } ================================================ FILE: Examples/NoModules/Appendix A/ExA_09A/ExA_09A.cpp ================================================ // Attempting to call a function with internal linkage from a different translation unit #include #include double localHelper(double x, unsigned n); // Declares an external localHelper() function int main() { for (unsigned i{ 0 }; i <= 5; ++i) // Calculate positive powers of 8 std::cout << std::format("{:10}\n", localHelper(8.0, i)); std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_09A/Power.cpp ================================================ // A local helper function with internal linkage namespace { double localHelper(double x, unsigned n) // localHelper() has internal linkage { if (n == 0) return 1.0; // Recursion base case const double y{ localHelper(x, n / 2) }; // See Exercise 8-8 for an explanation return y * y * (n % 2 == 1 ? x : 1.0); } } double power(double x, int n) // power() has external linkage { return n >= 0 ? localHelper(x, static_cast(n)) : 1.0 / localHelper(x, static_cast(-n)); } ================================================ FILE: Examples/NoModules/Appendix A/ExA_10/BadMath.h ================================================ auto square(const auto& x) { return x * x; } // An abbreviated function template const double lambda{ 1.303577269034296391257 }; // Conway's constant enum class Oddity { Even, Odd }; bool isOdd(int x) { return x % 2 != 0; } auto getOddity(int x) { return isOdd(x) ? Oddity::Odd : Oddity::Even; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_10/ExA_10.cpp ================================================ #include #include "BadMath.h" #include "BadMath.h" int main() { std::cout << square(1.234) << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_10A/BetterMath.h ================================================ // A second, better attempt at creating a header file #ifndef BETTER_MATH_H #define BETTER_MATH_H auto square(const auto& x) { return x * x; } // An abbreviated function template const double lambda{ 1.303577269034296391257 }; // Conway's constant enum class Oddity { Even, Odd }; bool isOdd(int x) { return x % 2 != 0; } auto getOddity(int x) { return isOdd(x) ? Oddity::Odd : Oddity::Even; } #endif ================================================ FILE: Examples/NoModules/Appendix A/ExA_10A/ExA_10A.cpp ================================================ #include #include "BetterMath.h" #include "BetterMath.h" int main() { std::cout << square(1.234) << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_11/BetterMath.h ================================================ // A second, better attempt at creating a header file #ifndef BETTER_MATH_H #define BETTER_MATH_H auto square(const auto& x) { return x * x; } // An abbreviated function template const double lambda{ 1.303577269034296391257 }; // Conway's constant enum class Oddity { Even, Odd }; bool isOdd(int x) { return x % 2 != 0; } auto getOddity(int x) { return isOdd(x) ? Oddity::Odd : Oddity::Even; } #endif ================================================ FILE: Examples/NoModules/Appendix A/ExA_11/ExA_11.cpp ================================================ // Creating multiple definitions of the same functions in a program, // even though #include guards are present. // (The guards only prevent multiple definitions within one translation unit!) #include #include "Hypot.h" #include "Pow4.h" int main() { std::cout << math::hypot(3, 4) << '\t' << math::pow4(5) << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_11/Hypot.cpp ================================================ // Hypot.cpp - Definition of the math::hypot() function #include "BetterMath.h" // For the square() function template definition #include "Hypot.h" // Missing from the code listing in the book! #include // For std::sqrt() // Caution: See Chapter 11 for why you should always use std::hypot() // over this nave definition (based on the Pythagorean Theorem)! double math::hypot(double x, double y) { return std::sqrt(square(x) + square(y)); } ================================================ FILE: Examples/NoModules/Appendix A/ExA_11/Hypot.h ================================================ // Declaration of an external math::hypot() function #ifndef HYPOT_H #define HYPOT_H namespace math { // Computes the length of the hypotenuse of a right-angle triangle double hypot(double x, double y); } #endif ================================================ FILE: Examples/NoModules/Appendix A/ExA_11/Pow4.cpp ================================================ // Definition of the math::pow4() function #include "BetterMath.h" // For the square() function template definition namespace math { double pow4(double x) { return square(square(x)); } } ================================================ FILE: Examples/NoModules/Appendix A/ExA_11/Pow4.h ================================================ // Declaration of a math::pow4() function #ifndef POW4_H #define POW4_H namespace math { // Same as std::pow(x, 4) double pow4(double x); } #endif ================================================ FILE: Examples/NoModules/Appendix A/ExA_12/ExA_12.cpp ================================================ // Using inline definitions to prevent ODR violations when // including a header in multiple translation units of the same program. #include #include "Hypot.h" #include "Pow4.h" int main() { std::cout << math::hypot(3, 4) << '\t' << math::pow4(5) << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_12/Hypot.cpp ================================================ // Hypot.cpp - Definition of the math::hypot() function #include "ProperMath.h" // For the square() function template definition #include "Hypot.h" // Missing from the code listing in the book! #include // For std::sqrt() // Caution: See Chapter 11 for why you should always use std::hypot() // over this nave definition (based on the Pythagorean Theorem)! double math::hypot(double x, double y) { return std::sqrt(square(x) + square(y)); } ================================================ FILE: Examples/NoModules/Appendix A/ExA_12/Hypot.h ================================================ // Declaration of an external math::hypot() function #ifndef HYPOT_H #define HYPOT_H namespace math { // Computes the length of the hypotenuse of a right-angle triangle double hypot(double x, double y); } #endif ================================================ FILE: Examples/NoModules/Appendix A/ExA_12/Pow4.cpp ================================================ // Definition of the math::pow4() function #include "ProperMath.h" // For the square() function template definition namespace math { double pow4(double x) { return square(square(x)); } } ================================================ FILE: Examples/NoModules/Appendix A/ExA_12/Pow4.h ================================================ // Declaration of a math::pow4() function #ifndef POW4_H #define POW4_H namespace math { // Same as std::pow(x, 4) double pow4(double x); } #endif ================================================ FILE: Examples/NoModules/Appendix A/ExA_12/ProperMath.h ================================================ // Your first, proper header file #ifndef PROPER_MATH_H #define PROPER_MATH_H auto square(const auto& x) { return x * x; } // An abbreviated function template const inline double lambda{ 1.303577269034296391257 }; // Conway's constant enum class Oddity { Even, Odd }; inline bool isOdd(int x) { return x % 2 != 0; } inline auto getOddity(int x) { return isOdd(x) ? Oddity::Odd : Oddity::Even; } #endif ================================================ FILE: Examples/NoModules/Appendix A/ExA_13/Box.cpp ================================================ #include "Box.h" Box::Box() : Box{ 1.0, 1.0, 1.0 } {} Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } {} double Box::getLength() const { return m_length; } double Box::getWidth() const { return m_width; } double Box::getHeight() const { return m_height; } double Box::volume() const { return m_length * m_width * m_height; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_13/Box.h ================================================ // Your first header file with a class definition #ifndef BOX_H #define BOX_H class Box { public: Box(); Box(double length, double width, double height); double getLength() const; double getWidth() const; double getHeight() const; double volume() const; private: double m_length; double m_width; double m_height; }; #endif ================================================ FILE: Examples/NoModules/Appendix A/ExA_13/ExA_13.cpp ================================================ // Including a class definition from a header #include #include "Box.h" int main() { Box boxy{ 1, 2, 3 }; std::cout << boxy.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Appendix A/ExA_13A/Box.h ================================================ // Inline class member definitions #ifndef BOX_H #define BOX_H #include // Caution: do not use import ; here! class Box { public: Box() = default; // In-class definition, and thus implicitly inline Box(double length, double width, double height); // In-class member definitions are implicitly inline double getLength() const { return m_length; }; double getWidth() const { return m_width; }; double getHeight() const { return m_height; }; double volume() const; private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; // Out-of-class member definitions must be explicitly marked as inline inline Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } {} inline double Box::volume() const { return m_length * m_width * m_height; } // Definitions of non-member functions of course must be inline as well inline std::ostream& operator<<(std::ostream& out, const Box& box) { return out << "Box(" << box.getLength() << ", " << box.getWidth() << ", " << box.getHeight() << ')'; } #endif ================================================ FILE: Examples/NoModules/Appendix A/ExA_13A/ExA_13A.cpp ================================================ // Inline class member definitions (see Box.h) #include #include "Box.h" int main() { Box boxy{ 1, 2, 3 }; std::cout << boxy.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 01/Ex1_01.cpp ================================================ // A complete C++ program #include int main() { int answer {42}; // Defines answer with value 42 std::cout << "The answer to life, the universe, and everything is " << answer << std::endl; return 0; } ================================================ FILE: Examples/NoModules/Chapter 01/Ex1_02.cpp ================================================ // Using escape sequences #include int main() { std::cout << "\"Least \'said\' \\\n\t\tsoonest \'mended\'.\"" << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_01.cpp ================================================ // Writing values of variables to cout #include // For user input and output through std::cin / cout int main() { int apple_count {15}; // Number of apples int orange_count {5}; // Number of oranges int total_fruit {apple_count + orange_count}; // Total number of fruit std::cout << "The value of apple_count is " << apple_count << std::endl; std::cout << "The value of orange_count is " << orange_count << std::endl; std::cout << "The value of total_fruit is " << total_fruit << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_02.cpp ================================================ // Converting distances #include // For user input and output through std::cin / cout int main() { unsigned int yards {}, feet {}, inches {}; // Convert a distance in yards, feet, and inches to inches std::cout << "Enter a distance as yards, feet, and inches " << "with the three values separated by spaces: "; std::cin >> yards >> feet >> inches; const unsigned feet_per_yard {3}; const unsigned inches_per_foot {12}; unsigned total_inches {}; total_inches = inches + inches_per_foot * (yards*feet_per_yard + feet); std::cout << "The distances corresponds to " << total_inches << " inches.\n"; // Convert a distance in inches to yards feet and inches std::cout << "Enter a distance in inches: "; std::cin >> total_inches; feet = total_inches / inches_per_foot; inches = total_inches % inches_per_foot; yards = feet / feet_per_yard; feet = feet % feet_per_yard; std::cout << "The distances corresponds to " << yards << " yards " << feet << " feet " << inches << " inches." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_03.cpp ================================================ // Sizing a pond for happy fish #include #include // For the pi constant #include // For the square root function int main() { // 2 square feet pond surface for every 6 inches of fish const double fish_factor { 2.0/0.5 }; // Area per unit length of fish const double inches_per_foot { 12.0 }; double fish_count {}; // Number of fish double fish_length {}; // Average length of fish std::cout << "Enter the number of fish you want to keep: "; std::cin >> fish_count; std::cout << "Enter the average fish length in inches: "; std::cin >> fish_length; fish_length /= inches_per_foot; // Convert to feet std::cout << '\n'; // Calculate the required surface area const double pond_area {fish_count * fish_length * fish_factor}; // Calculate the pond diameter from the area const double pond_diameter {2.0 * std::sqrt(pond_area / std::numbers::pi)}; std::cout << "Pond diameter required for " << fish_count << " fish is " << pond_diameter << " feet.\n"; } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_03A.cpp ================================================ // Expressions with mixed variables types // (The difference with the original example // is the type of fish_count and inches_per_foot) #include #include // For the pi constant #include // For the square root function int main() { // 2 square feet pond surface for every 6 inches of fish const double fish_factor { 2.0/0.5 }; // Area per unit length of fish const unsigned int inches_per_foot { 12 }; // <-- Used to be of type double unsigned int fish_count {}; // Number of fish (used to be of type double as well) double fish_length {}; // Average length of fish std::cout << "Enter the number of fish you want to keep: "; std::cin >> fish_count; std::cout << "Enter the average fish length in inches: "; std::cin >> fish_length; fish_length /= inches_per_foot; // Convert to feet std::cout << '\n'; // Calculate the required surface area const double pond_area {fish_count * fish_length * fish_factor}; // Calculate the pond diameter from the area const double pond_diameter {2.0 * std::sqrt(pond_area / std::numbers::pi)}; std::cout << "Pond diameter required for " << fish_count << " fish is " << pond_diameter << " feet.\n"; } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_03B.cpp ================================================ // Formatting text using std::format() #include #include #include // For the pi constant #include // For the square root function int main() { // 2 square feet pond surface for every 6 inches of fish const double fish_factor{ 2.0 / 0.5 }; // Area per unit length of fish const unsigned int inches_per_foot{ 12 }; unsigned int fish_count{}; // Number of fish double fish_length{}; // Average length of fish std::cout << "Enter the number of fish you want to keep: "; std::cin >> fish_count; std::cout << "Enter the average fish length in inches: "; std::cin >> fish_length; fish_length /= inches_per_foot; // Convert to feet std::cout << '\n'; // Calculate the required surface area const double pond_area{ fish_count * fish_length * fish_factor }; // Calculate the pond diameter from the area const double pond_diameter{ 2.0 * std::sqrt(pond_area / std::numbers::pi) }; std::cout << std::format("Pond diameter required for {} fish is {} feet.\n", fish_count, pond_diameter); } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_03C.cpp ================================================ // Format specifiers for std::format() #include #include #include // For the pi constant #include // For the square root function int main() { // 2 square feet pond surface for every 6 inches of fish const double fish_factor{ 2.0 / 0.5 }; // Area per unit length of fish const unsigned int inches_per_foot{ 12 }; unsigned int fish_count{}; // Number of fish double fish_length{}; // Average length of fish std::cout << "Enter the number of fish you want to keep: "; std::cin >> fish_count; std::cout << "Enter the average fish length in inches: "; std::cin >> fish_length; fish_length /= inches_per_foot; // Convert to feet std::cout << '\n'; // Calculate the required surface area const double pond_area{ fish_count * fish_length * fish_factor }; // Calculate the pond diameter from the area const double pond_diameter{ 2.0 * std::sqrt(pond_area / std::numbers::pi) }; std::cout << std::format("Pond diameter required for {} fish is {:.2} feet.\n", fish_count, pond_diameter); } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_03D.cpp ================================================ // Debugging format specifiers for std::format() using try/catch #include #include #include // For the pi constant #include // For the square root function int main() { // 2 square feet pond surface for every 6 inches of fish const double fish_factor{ 2.0 / 0.5 }; // Area per unit length of fish const unsigned int inches_per_foot{ 12 }; unsigned int fish_count{}; // Number of fish double fish_length{}; // Average length of fish std::cout << "Enter the number of fish you want to keep: "; std::cin >> fish_count; std::cout << "Enter the average fish length in inches: "; std::cin >> fish_length; fish_length /= inches_per_foot; // Convert to feet std::cout << '\n'; // Calculate the required surface area const double pond_area{ fish_count * fish_length * fish_factor }; // Calculate the pond diameter from the area const double pond_diameter{ 2.0 * std::sqrt(pond_area / std::numbers::pi) }; try { std::cout << std::format("Pond diameter required for {:.2} fish is {:.2} feet.\n", fish_count, pond_diameter); } catch (const std::format_error& error) { std::cout << error.what(); // Outputs "precision not allowed for this argument type" } } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_04.cpp ================================================ // Using explicit type conversions #include int main() { const unsigned feet_per_yard{ 3 }; const unsigned inches_per_foot{ 12 }; const unsigned inches_per_yard{ feet_per_yard * inches_per_foot }; double length{}; // Length as decimal yards unsigned int yards{}; // Whole yards unsigned int feet{}; // Whole feet unsigned int inches{}; // Whole inches std::cout << "Enter a length in yards as a decimal: "; std::cin >> length; // Get the length as yards, feet, and inches yards = static_cast(length); feet = static_cast((length - yards) * feet_per_yard); inches = static_cast(length * inches_per_yard) % inches_per_foot; std::cout << length << " yards converts to " << yards << " yards " << feet << " feet " << inches << " inches." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_05.cpp ================================================ // The width, alignment, fill, and 0 formatting options of std::format() #include #include int main() { // Default alignment: right for numbers, left otherwise std::cout << std::format("{:7}|{:7}|{:7}|{:7}|{:7}\n", 1, -.2, "str", 'c', true); // Left and right alignment + custom fill character std::cout << std::format("{:*<7}|{:*<7}|{:*>7}|{:*>7}|{:*>7}\n", 1, -.2, "str", 'c', true); // Centered alignment + 0 formatting option for numbers std::cout << std::format("{:^07}|{:^07}|{:^7}|{:^7}|{:^7}\n", 1, -.2, "str", 'c', true); } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_06.cpp ================================================ // Formatting numeric values with std::format() #include #include #include int main() { const double pi{ std::numbers::pi }; std::cout << std::format("Default: {:.2}, fixed: {:.2f}, scientific: {:.2e}, " "general: {:.2g}\n", pi, pi, pi, pi); std::cout << std::format("Default: {}, binary: {:b}, hex.: {:x}\n", 314, 314, 314); std::cout << std::format("Default: {}, decimal: {:d}, hex.: {:x}\n", 'c', 'c', 'c'); std::cout << std::format("Alternative hex.: {:#x}, binary: {:#b}, HEX.: {:#X}\n", 314, 314, 314); std::cout << std::format("Forced sign: {:+}, space sign: {: }\n", 314, 314); std::cout << std::format("All together: {:*<+10.4f}, {:+#09x}\n", pi, 314); } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_06B.cpp ================================================ // Argument indices for std::format() #include #include #include int main() { const double pi{ std::numbers::pi }; std::cout << std::format("Default: {:.2}, fixed: {:.2f}, scientific: {:.2e}, " "general: {:.2g}\n", pi, pi, pi, pi); std::cout << std::format("Default: {0}, binary: {0:b}, hex.: {0:x}\n", 314); std::cout << std::format("Default: {0}, decimal: {0:d}, hex.: {0:x}\n", 'c'); std::cout << std::format("Alternative hex.: {0:#x}, binary: {0:#b}, HEX.: {0:#X}\n", 314); std::cout << std::format("Forced sign: {0:+}, space sign: {0: }\n", 314); std::cout << std::format("All together: {:*<+10.4f}, {:+#09x}\n", pi, 314); } ================================================ FILE: Examples/NoModules/Chapter 02/Ex2_07.cpp ================================================ // Finding maximum and minimum values for data types #include #include #include int main() { std::cout << std::format("The range for type short is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()) << std::format("The range for type unsigned int is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()) << std::format("The range for type long is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()) << std::format("The positive range for type float is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()) << std::format("The full range for type float is from {} to {}\n", std::numeric_limits::lowest(), std::numeric_limits::max()) << std::format("The positive range for type double is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()) << std::format("The positive range for type long double is from {} to {}\n", std::numeric_limits::min(), std::numeric_limits::max()); } ================================================ FILE: Examples/NoModules/Chapter 03/Ex3_01.cpp ================================================ // Using the bitwise operators #include #include int main() { const unsigned int red{ 0xFF0000u }; // Color red const unsigned int white{ 0xFFFFFFu }; // Color white - RGB all maximum std::cout << "Try out bitwise complement, AND and OR operators:\n"; std::cout << std::format("Initial value: red = {:08X}\n", red); std::cout << std::format("Complement: ~red = {:08X}\n", ~red); std::cout << std::format("Initial value: white = {:08X}\n", white); std::cout << std::format("Complement: ~white = {:08X}\n", ~white); std::cout << std::format("Bitwise AND: red & white = {:08X}\n", red & white); std::cout << std::format("Bitwise OR: red | white = {:08X}\n", red | white); std::cout << "\nNow try successive exclusive OR operations:\n"; unsigned int mask{ red ^ white }; std::cout << std::format("mask = red ^ white = {:08X}\n", mask); std::cout << std::format(" mask ^ red = {:08X}\n", mask ^ red); std::cout << std::format(" mask ^ white = {:08X}\n", mask ^ white); unsigned int flags{ 0xFF }; // Flags variable unsigned int bit1mask{ 0x1 }; // Selects bit 1 unsigned int bit6mask{ 0b100000 }; // Selects bit 6 unsigned int bit20mask{ 1u << 19 }; // Selects bit 20 std::cout << "Use masks to select or set a particular flag bit:\n"; std::cout << std::format("Select bit 1 from flags : {:08X}\n", flags & bit1mask); std::cout << std::format("Select bit 6 from flags : {:08X}\n", flags & bit6mask); std::cout << std::format("Switch off bit 6 in flags: {:08X}\n", flags &= ~bit6mask); std::cout << std::format("Switch on bit 20 in flags: {:08X}\n", flags |= bit20mask); } ================================================ FILE: Examples/NoModules/Chapter 03/Ex3_02.cpp ================================================ // Demonstrating scope, lifetime, and global variables #include long count1{999L}; // Global count1 double count2{3.14}; // Global count2 int count3; // Global count3 - default initialization int main() { /* Function scope starts here */ int count1{10}; // Hides global count1 int count3{50}; // Hides global count3 std::cout << "Value of outer count1 = " << count1 << std::endl; std::cout << "Value of global count1 = " << ::count1 << std::endl; std::cout << "Value of global count2 = " << count2 << std::endl; { /* New block scope starts here... */ int count1{20}; // This is a new variable that hides the outer count1 int count2{30}; // This hides global count2 std::cout << "\nValue of inner count1 = "<< count1 << std::endl; std::cout << "Value of global count1 = " << ::count1 << std::endl; std::cout << "Value of inner count2 = " << count2 << std::endl; std::cout << "Value of global count2 = " << ::count2 << std::endl; count1 = ::count1 + 3; // This sets inner count1 to global count1+3 ++::count1; // This changes global count1 std::cout << "\nValue of inner count1 = " << count1 << std::endl; std::cout << "Value of global count1 = " << ::count1 << std::endl; count3 += count2; // Increments outer count3 by inner count2; int count4 {}; } /* ...and ends here. */ // std::cout << count4 << std::endl; // count4 does not exist in this scope! std::cout << "\nValue of outer count1 = "<< count1 << std::endl << "Value of outer count3 = " << count3 << std::endl; std::cout << "Value of global count3 = " << ::count3 << std::endl; std::cout << "Value of global count2 = " << count2 << std::endl; } /* Function scope ends here */ ================================================ FILE: Examples/NoModules/Chapter 03/Ex3_03.cpp ================================================ // Operations with enumerations #include #include int main() { enum class Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }; Day yesterday{ Day::Monday }, today{ Day::Tuesday }, tomorrow{ Day::Wednesday }; const Day poets_day{ Day::Friday }; enum class Punctuation : char { Comma = ',', Exclamation = '!', Question = '?' }; Punctuation ch{ Punctuation::Comma }; std::cout << std::format("yesterday's value is {}{} but poets_day's is {}{}\n", static_cast(yesterday), static_cast(ch), static_cast(poets_day), static_cast(Punctuation::Exclamation)); today = Day::Thursday; // Assign new ... ch = Punctuation::Question; // ... enumerator values tomorrow = poets_day; // Copy enumerator value std::cout << std::format("Is today's value({}) the same as poets_day({}){}\n", static_cast(today), static_cast(poets_day), static_cast(ch)); // ch = tomorrow; /* Uncomment any of these for an error */ // tomorrow = Friday; // today = 6; } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_01.cpp ================================================ // Comparing data values #include int main() { char first {}; // Stores the first character char second {}; // Stores the second character std::cout << "Enter a character: "; std::cin >> first; std::cout << "Enter a second character: "; std::cin >> second; // std::cout << std::boolalpha; /* Output true/false instead of 1/0 */ std::cout << "The value of the expression " << first << '<' << second << " is " << (first < second) << std::endl; std::cout << "The value of the expression " << first << "==" << second << " is " << (first == second) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_01A.cpp ================================================ // Comparing data values (output using std::format()) #include #include int main() { char first {}; // Stores the first character char second {}; // Stores the second character std::cout << "Enter a character: "; std::cin >> first; std::cout << "Enter a second character: "; std::cin >> second; std::cout << std::format("The value of the expression {} < {} is {}\n", first, second, first < second); std::cout << std::format("The value of the expression {} == {} is {}\n", first, second, first == second); } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_02.cpp ================================================ // Three-way comparison of integers #include // Required when using operator <=> (even for fundamental types) #include #include int main() { std::cout << "Please enter a number: "; int value; std::cin >> value; std::strong_ordering ordering{ value <=> 0 }; std::cout << std::format("value < 0: {}\n", ordering == std::strong_ordering::less); std::cout << std::format("value > 0: {}\n", ordering == std::strong_ordering::greater); std::cout << std::format("value == 0: {}\n", ordering == std::strong_ordering::equal); } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_02A.cpp ================================================ // Using the named comparison functions #include // Required when using operator <=> (even for fundamental types) #include #include int main() { std::cout << "Please enter a number: "; int value; std::cin >> value; std::strong_ordering ordering{ value <=> 0 }; std::cout << std::format("value < 0: {}\n", std::is_lt(ordering)); // is less than std::cout << std::format("value > 0: {}\n", std::is_gt(ordering)); // is greater than std::cout << std::format("value == 0: {}\n", std::is_eq(ordering)); // is equivalent } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_03.cpp ================================================ // Using an if statement #include int main() { std::cout << "Enter an integer between 50 and 100: "; int value {}; std::cin >> value; if (value) std::cout << "You have entered a value that is different from zero." << std::endl; if (value < 50) std::cout << "The value is invalid - it is less than 50." << std::endl; if (value > 100) std::cout << "The value is invalid - it is greater than 100." << std::endl; std::cout << "You entered " << value << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_04.cpp ================================================ // Using a nested if #include int main() { char letter {}; // Store input here std::cout << "Enter a letter: "; // Prompt for the input std::cin >> letter; if (letter >= 'A') { // letter is 'A' or larger if (letter <= 'Z') { // letter is 'Z' or smaller std::cout << "You entered an uppercase letter." << std::endl; return 0; } } if (letter >= 'a') // Test for 'a' or larger if (letter <= 'z') { // letter is >= 'a' and <= 'z' std::cout << "You entered a lowercase letter." << std::endl; return 0; } std::cout << "You did not enter a letter." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_04A.cpp ================================================ // Using the std::isupper() / islower() character classification functions #include #include int main() { char letter {}; // Store input here std::cout << "Enter a letter: "; // Prompt for the input std::cin >> letter; if (std::isupper(letter)) { std::cout << "You entered an uppercase letter." << std::endl; return 0; } if (std::islower(letter)) { std::cout << "You entered a lowercase letter." << std::endl; return 0; } std::cout << "You did not enter a letter." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_05.cpp ================================================ // Using the if-else statement #include int main() { long number {}; // Stores input std::cout << "Enter an integer less than 2 billion: "; std::cin >> number; if (number % 2) // Test remainder after division by 2 { // Here if remainder is 1 std::cout << "Your number is odd." << std::endl; } else { // Here if remainder is 0 std::cout << "Your number is even." << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_06.cpp ================================================ // Combining logical operators for loan approval #include int main() { int age {}; // Age of the prospective borrower int income {}; // Income of the prospective borrower int balance {}; // Current bank balance // Get the basic data for assessing the loan std::cout << "Please enter your age in years: "; std::cin >> age; std::cout << "Please enter your annual income in dollars: "; std::cin >> income; std::cout << "What is your current account balance in dollars: "; std::cin >> balance; // We only lend to people who are at least 21 years of age, // who make over $25,000 per year, // or have over $100,000 in their account, or both. if (age >= 21 && (income > 25'000 || balance > 100'000)) { // OK, you are good for the loan - but how much? // This will be the lesser of twice income and half balance int loan {}; // Stores maximum loan amount if (2*income < balance/2) { loan = 2*income; } else { loan = balance/2; } std::cout << "\nYou can borrow up to $" << loan << std::endl; } else // No loan for you... { std::cout << "\nUnfortunately, you don't qualify for a loan." << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_07.cpp ================================================ // Using the conditional operator to select output. #include #include int main() { int mice {}; // Count of all mice int brown {}; // Count of brown mice int white {}; // Count of white mice std::cout << "How many brown mice do you have? "; std::cin >> brown; std::cout << "How many white mice do you have? "; std::cin >> white; mice = brown + white; std::cout << std::format("You have {} {} in total.\n", mice, mice == 1 ? "mouse" : "mice"); } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_08.cpp ================================================ // Using the switch statement #include int main() { std::cout << "Your electronic recipe book is at your service.\n" << "You can choose from the following delicious dishes:\n" << "1. Boiled eggs\n" << "2. Fried eggs\n" << "3. Scrambled eggs\n" << "4. Coddled eggs\n\n" << "Enter your selection number: "; int choice {}; // Stores selection value std::cin >> choice; switch (choice) { case 1: std::cout << "Boil some eggs." << std::endl; break; case 2: std::cout << "Fry some eggs." << std::endl; break; case 3: std::cout << "Scramble some eggs." << std::endl; break; case 4: std::cout << "Coddle some eggs." << std::endl; break; default: std::cout << "You entered a wrong number - try raw eggs." << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_09.cpp ================================================ // Multiple case actions #include #include int main() { char letter {}; std::cout << "Enter a letter: "; std::cin >> letter; if (std::isalpha(letter)) { switch (std::tolower(letter)) { case 'a': case 'e': case 'i': case 'o': case 'u': std::cout << "You entered a vowel." << std::endl; break; default: std::cout << "You entered a consonant." << std::endl; break; } } else { std::cout << "You did not enter a letter." << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 04/Ex4_09A.cpp ================================================ // Using a return statement to exit a switch statement #include #include int main() { char letter {}; std::cout << "Enter a letter: "; std::cin >> letter; if (std::isalpha(letter)) { switch (std::tolower(letter)) { case 'a': case 'e': case 'i': case 'o': case 'u': std::cout << "You entered a vowel." << std::endl; return 0; // Ends the program } // We did not exit main() in the above switch, so letter is not a vowel: std::cout << "You entered a consonant." << std::endl; } else { std::cout << "You did not enter a letter." << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_01.cpp ================================================ // Using a for loop with an array #include int main() { const unsigned size {6}; // Array size unsigned height[size] {26, 37, 47, 55, 62, 75}; // An array of heights unsigned total {}; // Sum of heights for (size_t i {}; i < size; ++i) { total += height[i]; } const unsigned average {total/size}; // Calculate average height std::cout << "The average height is " << average << std::endl; unsigned count {}; for (size_t i {}; i < size; ++i) { if (height[i] < average) ++count; } std::cout << count << " people are below average height." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_02.cpp ================================================ // Obtaining the number of array elements #include #include // for std::size() int main() { int values[] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; std::cout << "There are " << std::size(values) << " elements in the array.\n"; int sum {}; const size_t old_school_size{ sizeof(values) / sizeof(values[0]) }; for (size_t i {}; i < old_school_size; ++i) { sum += values[i]; } std::cout << "The sum of the array elements is " << sum << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_03.cpp ================================================ // Floating-point control in a for loop #include #include #include int main() { const size_t values_per_line {3}; // Outputs per line size_t values_current_line {}; // Number of outputs on current line for (double radius {0.2}; radius <= 3.0; radius += 0.2) { const auto area{ std::numbers::pi * radius * radius }; std::cout << std::format("radius = {:4.2f}, area = {:5.2f}; ", radius, area); if (++values_current_line == values_per_line) // When enough values written... { std::cout << std::endl; // ...start a new line... values_current_line = 0; // ...and reset the line counter } } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_03A.cpp ================================================ // Floating-point control in a for loop #include #include #include int main() { const size_t values_per_line {3}; // Outputs per line size_t values_current_line {}; // Number of outputs on current line for (double radius {0.2}; radius < 3.0 + 0.001; radius += 0.2) { const auto area{ std::numbers::pi * radius * radius }; std::cout << std::format("radius = {:4.2f}, area = {:5.2f}; ", radius, area); if (++values_current_line == values_per_line) // When enough values written... { std::cout << std::endl; // ...start a new line... values_current_line = 0; // ...and reset the line counter } } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_04.cpp ================================================ // Multiple initializations in a loop expression #include #include int main() { unsigned int limit {}; std::cout << "This program calculates n! and the sum of the integers " << "up to n for values 1 to limit.\n"; std::cout << "What upper limit for n would you like? "; std::cin >> limit; // The format string for all rows of the table const auto table_format{ "{:>8} {:>8} {:>20}\n" }; // Output column headings std::cout << std::format(table_format, "integer", "sum", "factorial"); for (unsigned long long n {1}, sum {}, factorial {1}; n <= limit; ++n) { sum += n; // Accumulate sum to current n factorial *= n; // Calculate n! for current n std::cout << std::format(table_format, n, sum, factorial); } } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_04A.cpp ================================================ // Multiple calculations in a loop expression's third control expression // by using the comma operator #include #include int main() { unsigned int limit {}; std::cout << "This program calculates n! and the sum of the integers " << "up to n for values 1 to limit.\n"; std::cout << "What upper limit for n would you like? "; std::cin >> limit; // The format string for all rows of the table const auto table_format{ "{:>8} {:>8} {:>20}\n" }; // Output column headings std::cout << std::format(table_format, "integer", "sum", "factorial"); for (unsigned long long n {1}, sum {1}, factorial {1}; n <= limit; ++n, sum += n, factorial *= n) { std::cout << std::format(table_format, n, sum, factorial); } } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_05.cpp ================================================ // Using a while loop to calculate the sum of integers from 1 to n and n! #include #include int main() { unsigned int limit {}; std::cout << "This program calculates n! and the sum of the integers " << "up to n for values 1 to limit.\n"; std::cout << "What upper limit for n would you like? "; std::cin >> limit; // The format string for all rows of the table const auto table_format{ "{:>8} {:>8} {:>20}\n" }; // Output column headings std::cout << std::format(table_format, "integer", "sum", "factorial"); unsigned int n {}; unsigned int sum {}; unsigned long long factorial {1ULL}; while (++n <= limit) { sum += n; // Accumulate sum to current n factorial *= n; // Calculate n! for current n std::cout << std::format(table_format, n, sum, factorial); } } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_06.cpp ================================================ // Using a do-while loop to manage input #include #include // For tolower() function int main() { char reply {}; // Stores response to prompt for input int count {}; // Counts the number of input values double temperature {}; // Stores an input value double total {}; // Stores the sum of all input values do { std::cout << "Enter a temperature reading: "; // Prompt for input std::cin >> temperature; // Read input value total += temperature; // Accumulate total of values ++count; // Increment count std::cout << "Do you want to enter another? (y/n): "; std::cin >> reply; // Get response } while (std::tolower(reply) == 'y'); std::cout << "The average temperature is " << total/count << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_07.cpp ================================================ // Generating multiplication tables using nested loops #include #include #include int main() { size_t table {}; // Table size const size_t table_min {2}; // Minimum table size - at least up to the 2-times const size_t table_max {12}; // Maximum table size char reply {}; // Response to prompt do { std::cout << std::format("What size table would you like ({} to {})? ", table_min, table_max); std::cin >> table; // Get the table size std::cout << std::endl; // Make sure table size is within the limits if (table < table_min || table > table_max) { std::cout << "Invalid table size entered. Program terminated." << std::endl; return 1; } // Create the top line of the table std::cout << std::format("{:>6}", '|'); for (size_t i {1}; i <= table; ++i) { std::cout << std::format(" {:3} |", i); } std::cout << std::endl; // Create the separator row for (size_t i {}; i <= table; ++i) { std::cout << "------"; } std::cout << std::endl; for (size_t i {1}; i <= table; ++i) { // Iterate over rows std::cout << std::format(" {:3} |", i); // Start the row // Output the values in a row for (size_t j {1}; j <= table; ++j) { std::cout << std::format(" {:3} |", i*j); // For each column } std::cout << std::endl; // End the row } // Check if another table is required std::cout << "\nDo you want another table (y or n)? "; std::cin >> reply; } while (std::tolower(reply) == 'y'); } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_07A.cpp ================================================ // Generating multiplication tables using nested loops // In this version an indefinite for loop is used, in combination with break statements. #include #include #include // for std::tolower() int main() { size_t table {}; // Table size const size_t table_min {2}; // Minimum table size - at least up to the 2-times const size_t table_max {12}; // Maximum table size char reply {}; // Response to prompt const size_t max_tries{ 3 }; // Max. number of times a user can try entering a table size do { for (size_t count{ 1 }; ; ++count) // Indefinite loop { std::cout << std::format("What size table would you like ({} to {})? ", table_min, table_max); std::cin >> table; // Get the table size // Make sure table size is within the limits if (table >= table_min && table <= table_max) { break; // Exit the input loop } else if (count < max_tries) { std::cout << "Invalid input - try again.\n"; } else { std::cout << "Invalid table size entered - yet again!\nSorry, only " << max_tries << " allowed - program terminated." << std::endl; return 1; } } // Create the top line of the table std::cout << std::format("{:>6}", '|'); for (size_t i {1}; i <= table; ++i) { std::cout << std::format(" {:3} |", i); } std::cout << std::endl; // Create the separator row for (size_t i {}; i <= table; ++i) { std::cout << "------"; } std::cout << std::endl; for (size_t i {1}; i <= table; ++i) { // Iterate over rows std::cout << std::format(" {:3} |", i); // Start the row // Output the values in a row for (size_t j {1}; j <= table; ++j) { std::cout << std::format(" {:3} |", i*j); // For each column } std::cout << std::endl; // End the row } // Check if another table is required std::cout << "\nDo you want another table (y or n)? "; std::cin >> reply; } while (std::tolower(reply) == 'y'); } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_08.cpp ================================================ // Using the continue statement to display ASCII character codes #include #include #include int main() { const auto header_format{ "{:^11}{:^11}{:^11}\n" }; // 3 cols., 11 wide, centered (^) const auto body_format{ "{0:^11}{0:^11X}{0:^11d}\n" }; // Print same argument three times std::cout << std::format(header_format, "Character", "Hexadecimal", "Decimal"); // Output 7-bit ASCII characters and corresponding codes char ch{}; do { if (!std::isprint(ch)) // If it's not printable... continue; // ...skip this iteration std::cout << std::format(body_format, ch); } while (ch++ < 127); } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_09.cpp ================================================ // Sorting an array in ascending sequence - using an indefinite while loop #include #include int main() { const size_t size {1000}; // Array size double x[size] {}; // Stores data to be sorted size_t count {}; // Number of values in array while (true) { double input {}; // Temporary store for a value std::cout << "Enter a non-zero value, or 0 to end: "; std::cin >> input; if (input == 0) break; x[count] = input; if (++count == size) { std::cout << "Sorry, I can only store " << size << " values.\n"; break; } } if (count == 0) { std::cout << "Nothing to sort..." << std::endl; return 0; } std::cout << "Starting sort..." << std::endl; while (true) { bool swapped{ false }; // Becomes true when not all values are in order for (size_t i {}; i < count - 1; ++i) { if (x[i] > x[i + 1]) // Out of order so swap them { const auto temp{ x[i] }; x[i] = x[i+1]; x[i + 1] = temp; swapped = true; } } if (!swapped) // If there were no swaps break; // ...all values are in order... } // ...otherwise, go round again. std::cout << "Your data in ascending sequence:\n"; const size_t perline {10}; // Number output per line size_t n {}; // Number on current line for (size_t i {}; i < count; ++i) { std::cout << std::format("{:8.1f}", x[i]); if (++n == perline) // When perline have been written... { std::cout << std::endl; // Start a new line and... n = 0; // ...reset count on this line } } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_10.cpp ================================================ // Classifying the letters in a C-style string #include #include int main() { const int max_length {100}; // Array size char text[max_length] {}; // Array to hold input string std::cout << "Enter a line of text:" << std::endl; // Read a line of characters including spaces std::cin.getline(text, max_length); std::cout << "You entered:\n" << text << std::endl; size_t vowels {}; // Count of vowels size_t consonants {}; // Count of consonants for (int i {}; text[i] != '\0'; i++) { if (std::isalpha(text[i])) // If it is a letter... { switch (std::tolower(text[i])) { // ...check lowercase... case 'a': case 'e': case 'i': case 'o': case 'u': ++vowels; // ...it is a vowel break; default: ++consonants; // ...it is a consonant } } } std::cout << "Your input contained " << vowels << " vowels and " << consonants << " consonants." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_11.cpp ================================================ // Working with strings in an array #include #include // for std::size() int main() { const size_t max_length{ 80 }; // Maximum string length (including \0) char stars[][max_length]{ "Fatty Arbuckle", "Clara Bow", "Lassie", "Slim Pickens", "Boris Karloff", "Mae West", "Oliver Hardy", "Greta Garbo" }; size_t choice{}; std::cout << "Pick a lucky star! Enter a number between 1 and " << std::size(stars) << ": "; std::cin >> choice; if (choice >= 1 && choice <= std::size(stars)) { std::cout << "Your lucky star is " << stars[choice - 1] << std::endl; } else { std::cout << "Sorry, you haven't got a lucky star." << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_12.cpp ================================================ // Allocating an array at runtime // This example does not work with some compilers (such as Visual C++) // because dynamic arrays is not standard C++ (it is valid C though). #include #include #ifdef _MSC_VER // See Appendix A for an explanation of preprocessing macros #error Visual Studio does not support variable length arrays (not standard C++) #endif int main() { size_t count {}; std::cout << "How many heights will you enter? "; std::cin >> count; int height[count]; // Create the array of count elements // Read the heights size_t entered {}; while (entered < count) { std::cout <<"Enter a height (in inches): "; std::cin >> height[entered]; if (height[entered] > 0) // Make sure value is positive { ++entered; } else { std::cout << "A height must be positive - try again.\n"; } } // Calculate the sum of the heights unsigned int total {}; for (size_t i {}; i < count; ++i) { total += height[i]; } std::cout << std::format("The average height is {:.1f}\n", static_cast(total) / count); } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_12A.cpp ================================================ // Allocating an array at runtime (for loop merged into preceding while loop) // This example does not work with some compilers (such as Visual C++) // because dynamic arrays is not standard C++ (it is valid C though). #include #include #ifdef _MSC_VER // See Appendix A for an explanation of preprocessing macros #error Visual Studio does not support variable length arrays (not standard C++) #endif int main() { size_t count {}; std::cout << "How many heights will you enter? "; std::cin >> count; int height[count]; // Create the array of count elements // Read the heights unsigned int total {}; size_t entered {}; while (entered < count) { std::cout << "Enter a height (in inches): "; std::cin >> height[entered]; if (height[entered] > 0) // Make sure value is positive { total += height[entered++]; } else { std::cout << "A height must be positive - try again.\n"; } } std::cout << std::format("The average height is {:.1f}\n", static_cast(total) / count); } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_13.cpp ================================================ // Comparing array<> objects and plain arrays #include #include int main() { { std::cout << "First we try out the comparison operators for std::array<> objects:" << std::endl; std::array these {1.0, 2.0, 3.0, 4.0}; std::array those {1.0, 2.0, 3.0, 4.0}; std::array them {1.0, 1.0, 5.0, 5.0}; if (these == those) std::cout << "these and those are equal." << std::endl; if (those != them) std::cout << "those and them are not equal." << std::endl; if (those > them) std::cout << "those are greater than them." << std::endl; if (them < those) std::cout << "them are less than those." << std::endl; } std::cout << std::endl; { std::cout << "Next we repeat exactly the same comparisons with plain C++ arrays:" << std::endl; double these[4] {1.0, 2.0, 3.0, 4.0}; double those[4] {1.0, 2.0, 3.0, 4.0}; double them[4] {1.0, 1.0, 5.0, 5.0}; if (these == those) std::cout << "these and those are equal." << std::endl; if (those != them) std::cout << "those and them are not equal." << std::endl; if (those > them) std::cout << "those are greater than them." << std::endl; if (them < those) std::cout << "them are less than those." << std::endl; } /* The explanation of why this does not work as expected with plain arrays follows in Chapter 6 */ } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_14.cpp ================================================ // Using array to create Body Mass Index (BMI) table // BMI = weight/(height*height) // weight in kilograms, height in meters #include #include #include // For array int main() { const unsigned min_wt {100}; // Minimum weight in table (in pounds) const unsigned max_wt {250}; // Maximum weight in table const unsigned wt_step {10}; const size_t wt_count {1 + (max_wt - min_wt) / wt_step}; const unsigned min_ht {48}; // Minimum height in table (inches) const unsigned max_ht {84}; // Maximum height in table const unsigned ht_step {2}; const size_t ht_count { 1 + (max_ht - min_ht) / ht_step }; const double lbs_per_kg {2.2}; // Pounds per kilogram const double ins_per_m {39.37}; // Inches per meter std::array weight_lbs {}; std::array height_ins {}; // Create weights from 100lbs in steps of 10lbs for (unsigned i{}, w{ min_wt }; i < wt_count; w += wt_step, ++i) { weight_lbs[i] = w; } // Create heights from 48 inches in steps of 2 inches for (unsigned i{}, h{ min_ht }; h <= max_ht; h += ht_step) { height_ins.at(i++) = h; } // Output table headings std::cout << std::format("{:>8}", '|'); for (auto w : weight_lbs) std::cout << std::format("{:^6}|", w); std::cout << std::endl; // Output line below headings for (unsigned i{1}; i < wt_count; ++i) std::cout << "--------"; std::cout << std::endl; const unsigned int inches_per_foot {12U}; for (auto h : height_ins) { const unsigned feet{ h / inches_per_foot }; const unsigned inches{ h % inches_per_foot }; std::cout << std::format("{:2}'{:2}\" |", feet, inches); const double h_m{ h / ins_per_m }; // Height in meter for (auto w : weight_lbs) { const double w_kg = w / lbs_per_kg; // Weight in kilogram const double bmi = w_kg / (h_m * h_m); std::cout << std::format(" {:2.1f} |", bmi); } std::cout << std::endl; } // Output line below table for (size_t i {1}; i < wt_count; ++i) std::cout << "--------"; std::cout << "\nBMI from 18.5 to 24.9 is normal" << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 05/Ex5_15.cpp ================================================ // Sorting an array in ascending sequence - using a vector container #include #include #include int main() { std::vector x; // Stores data to be sorted while (true) { double input {}; // Temporary store for a value std::cout << "Enter a non-zero value, or 0 to end: "; std::cin >> input; if (input == 0) break; x.push_back(input); } if (x.empty()) { std::cout << "Nothing to sort..." << std::endl; return 0; } std::cout << "Starting sort." << std::endl; while (true) { bool swapped{ false }; // Becomes true when not all values are in order for (size_t i {}; i < x.size() - 1; ++i) { if (x[i] > x[i + 1]) // Out of order so swap them { const auto temp{ x[i] }; x[i] = x[i+1]; x[i + 1] = temp; swapped = true; } } if (!swapped) // If there were no swaps break; // ...all values are in order... } // ...otherwise, go round again. std::cout << "Your data in ascending sequence:\n"; const size_t perline {10}; // Number output per line size_t n {}; // Number on current line for (size_t i {}; i < x.size(); ++i) { std::cout << std::format("{:8.1f}", x[i]); if (++n == perline) // When perline have been written... { std::cout << std::endl; // Start a new line and... n = 0; // ...reset count on this line } } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 06/Ex6_01.cpp ================================================ // The size of pointers #include int main() { // Print out the size (in number of bytes) of some data types // and the corresponding pointer types: std::cout << sizeof(double) << " > " << sizeof(char16_t) << std::endl; std::cout << sizeof(double*) << " == " << sizeof(char16_t*) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 06/Ex6_02.cpp ================================================ // Dereferencing pointers // Calculates the purchase price for a given quantity of items #include #include int main() { int unit_price {295}; // Item unit price in cents int count {}; // Number of items ordered int discount_threshold {25}; // Quantity threshold for discount double discount {0.07}; // Discount for quantities over discount_threshold int* pcount {&count}; // Pointer to count std::cout << "Enter the number of items you want: "; std::cin >> *pcount; std::cout << std::format("The unit price is ${:.2f}\n", unit_price / 100.0); // Calculate gross price int* punit_price{ &unit_price }; // Pointer to unit_price int price{ *pcount * *punit_price }; // Gross price via pointers auto* pprice {&price}; // Pointer to gross price // Calculate net price in US$ double net_price{}; double* pnet_price {nullptr}; pnet_price = &net_price; if (*pcount > discount_threshold) { std::cout << std::format("You qualify for a discount of {:.0f} percent.\n", discount * 100); *pnet_price = price*(1 - discount) / 100; } else { net_price = *pprice / 100; } std::cout << std::format("The net price for {} items is ${:.2f}\n", *pcount, net_price); } ================================================ FILE: Examples/NoModules/Chapter 06/Ex6_03.cpp ================================================ // Initializing pointers with strings #include int main() { const char* pstar1 {"Fatty Arbuckle"}; const char* pstar2 {"Clara Bow"}; const char* pstar3 {"Lassie"}; const char* pstar4 {"Slim Pickens"}; const char* pstar5 {"Boris Karloff"}; const char* pstar6 {"Mae West"}; const char* pstar7 {"Oliver Hardy"}; const char* pstar8 {"Greta Garbo"}; const char* pstr {"Your lucky star is "}; std::cout << "Pick a lucky star! Enter a number between 1 and 8: "; size_t choice {}; std::cin >> choice; switch (choice) { case 1: std::cout << pstr << pstar1 << std::endl; break; case 2: std::cout << pstr << pstar2 << std::endl; break; case 3: std::cout << pstr << pstar3 << std::endl; break; case 4: std::cout << pstr << pstar4 << std::endl; break; case 5: std::cout << pstr << pstar5 << std::endl; break; case 6: std::cout << pstr << pstar6 << std::endl; break; case 7: std::cout << pstr << pstar7 << std::endl; break; case 8: std::cout << pstr << pstar8 << std::endl; break; default: std::cout << "Sorry, you haven't got a lucky star." << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 06/Ex6_04.cpp ================================================ // Using an array of pointers #include #include // for std::size() int main() { const char* pstars[] { "Fatty Arbuckle", "Clara Bow", "Lassie", "Slim Pickens", "Boris Karloff", "Mae West", "Oliver Hardy", "Greta Garbo" }; std::cout << "Pick a lucky star! Enter a number between 1 and " << std::size(pstars) << ": "; size_t choice {}; std::cin >> choice; if (choice >= 1 && choice <= std::size(pstars)) { std::cout << "Your lucky star is " << pstars[choice - 1] << std::endl; } else { std::cout << "Sorry, you haven't got a lucky star." << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 06/Ex6_05.cpp ================================================ // Calculating primes using pointer notation #include #include int main() { const size_t max {100}; // Number of primes required long primes[max] {2L}; // First prime defined size_t count {1}; // Count of primes found so far long trial {3L}; // Candidate prime while (count < max) { bool isprime {true}; // Indicates when a prime is found // Try dividing the candidate by all the primes we have for (size_t i {}; i < count && isprime; ++i) { isprime = trial % *(primes + i) > 0; // False for exact division } if (isprime) { // We got one... *(primes + count++) = trial; // ...so save it in primes array } trial += 2; // Next value for checking } // Output primes 10 to a line std::cout << "The first " << max << " primes are:" << std::endl; for (size_t i{}; i < max; ++i) { std::cout << std::format("{:7}", *(primes + i)); if ((i+1) % 10 == 0) // Newline after every 10th prime std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 06/Ex6_06.cpp ================================================ // Calculating primes using dynamic memory allocation #include #include #include // For square root function (std::sqrt()) int main() { size_t max {}; // Number of primes required std::cout << "How many primes would you like? "; std::cin >> max; // Read number required if (max == 0) return 0; // Zero primes: do nothing auto* primes {new unsigned[max]}; // Allocate memory for max primes size_t count {1}; // Count of primes found primes[0] = 2; // Insert first seed prime unsigned trial {3}; // Initial candidate prime while (count < max) { bool isprime {true}; // Indicates when a prime is found const auto limit = static_cast(std::sqrt(trial)); for (size_t i {}; primes[i] <= limit && isprime; ++i) { isprime = trial % primes[i] > 0; // False for exact division } if (isprime) // We got one... primes[count++] = trial; // ...so save it in primes array trial += 2; // Next value for checking } // Output primes 10 to a line for (size_t i{}; i < max; ++i) { std::cout << std::format("{:10}", primes[i]); if ((i + 1) % 10 == 0) // After every 10th prime... std::cout << std::endl; // ...start a new line } std::cout << std::endl; delete[] primes; // Free up memory... primes = nullptr; // ... and reset the pointer } ================================================ FILE: Examples/NoModules/Chapter 06/Ex6_07.cpp ================================================ // Using smart pointers #include #include #include // For smart pointers #include // For std::vector<> container #include // For std::toupper() int main() { std::vector>> records; // Temperature records by days size_t day{ 1 }; // Day number while (true) // Collect temperatures by day { // Vector to store current day's temperatures created in the free store auto day_records{ std::make_shared>() }; records.push_back(day_records); // Save pointer in records vector std::cout << "Enter the temperatures for day " << day++ << " separated by spaces. Enter 1000 to end:\n"; while (true) { // Get temperatures for current day double t{}; // A temperature std::cin >> t; if (t == 1000.0) break; day_records->push_back(t); } std::cout << "Enter another day's temperatures (Y or N)? "; char answer{}; std::cin >> answer; if (std::toupper(answer) != 'Y') break; } day = 1; for (auto record : records) { double total{}; size_t count{}; std::cout << std::format("\nTemperatures for day {}:\n", day++); for (auto temp : *record) { total += temp; std::cout << std::format("{:6.2f}", temp); if (++count % 5 == 0) std::cout << std::endl; } std::cout << std::format("\nAverage temperature: {:.2f}", total / count) << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 07/Ex7_01.cpp ================================================ // Concatenating strings #include #include int main() { std::string first; // Stores the first name std::string second; // Stores the second name std::cout << "Enter your first name: "; std::cin >> first; // Read first name std::cout << "Enter your second name: "; std::cin >> second; // Read second name std::string sentence {"Your full name is "}; // Create basic sentence sentence += first + " " + second + "."; // Augment with names std::cout << sentence << std::endl; // Output the sentence std::cout << "The string contains " // Output its length << sentence.length() << " characters." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 07/Ex7_01A.cpp ================================================ // Concatenating characters and strings // (exactly the same as Ex07_01, except that we use ' ' and '.' instead of " " and ".") #include #include int main() { std::string first; // Stores the first name std::string second; // Stores the second name std::cout << "Enter your first name: "; std::cin >> first; // Read first name std::cout << "Enter your second name: "; std::cin >> second; // Read second name std::string sentence {"Your full name is "}; // Create basic sentence sentence += first + ' ' + second + '.'; // Augment with names std::cout << sentence << std::endl; // Output the sentence std::cout << "The string contains " // Output its length << sentence.length() << " characters." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 07/Ex7_02.cpp ================================================ // Accessing characters in a string #include #include #include int main() { std::string text; // Stores the input std::cout << "Enter a line of text:\n"; std::getline(std::cin, text); // Read a line including spaces unsigned vowels {}; // Count of vowels unsigned consonants {}; // Count of consonants for (size_t i {}; i < text.length(); ++i) { if (std::isalpha(text[i])) // Check for a letter { switch (std::tolower(text[i])) // Convert to lowercase { case 'a': case 'e': case 'i': case 'o': case 'u': ++vowels; break; default: ++consonants; break; } } } std::cout << "Your input contained " << vowels << " vowels and " << consonants << " consonants." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 07/Ex7_02A.cpp ================================================ // Accessing characters in a string // (same as Ex7_02, except that this version uses the more convenient range-based for loop) #include #include #include // for std::isalpha() and tolower() int main() { std::string text; // Stores the input std::cout << "Enter a line of text:\n"; std::getline(std::cin, text); // Read a line including spaces unsigned vowels {}; // Count of vowels unsigned consonants {}; // Count of consonants for (const char ch : text) { if (std::isalpha(ch)) // Check for a letter { switch (std::tolower(ch)) // Convert to lowercase { case 'a': case 'e': case 'i': case 'o': case 'u': ++vowels; break; default: ++consonants; break; } } } std::cout << "Your input contained " << vowels << " vowels and " << consonants << " consonants." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 07/Ex7_03.cpp ================================================ // Comparing strings #include // For stream I/O #include // For string formatting #include // For the string type #include // For the vector container int main() { std::vector names; // Vector of names std::string input_name; // Stores a name for (;;) // Indefinite loop (stopped using break) { std::cout << "Enter a name followed by Enter (leave blank to stop): "; std::getline(std::cin, input_name); // Read a name and... if (input_name.empty()) break; // ...if it's not empty... names.push_back(input_name); // ...add it to the vector } // Sort the names in ascending sequence bool sorted {}; do { sorted = true; // remains true when names are sorted for (size_t i {1}; i < names.size(); ++i) { if (names[i-1] > names[i]) { // Out of order - so swap names names[i].swap(names[i-1]); sorted = false; } } } while (!sorted); // Find the length of the longest name size_t max_length{}; for (const auto& name : names) if (max_length < name.length()) max_length = name.length(); // Output the sorted names 5 to a line const size_t field_width{ max_length + 2 }; size_t count {}; std::cout << "In ascending sequence the names you entered are:\n"; for (const auto& name : names) { std::cout << std::format("{:>{}}", name, field_width); // Right-align + dynamic width if (!(++count % 5)) std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 07/Ex7_04.cpp ================================================ // Searching within strings #include #include int main() { std::string sentence {"Manners maketh man"}; std::string word {"man"}; std::cout << sentence.find(word) << std::endl; // Outputs 15 std::cout << sentence.find("Ma") << std::endl; // Outputs 0 std::cout << sentence.find('k') << std::endl; // Outputs 10 std::cout << sentence.find('x') << std::endl; // Outputs std::string::npos } ================================================ FILE: Examples/NoModules/Chapter 07/Ex7_05.cpp ================================================ // Searching within substrings #include #include int main() { std::string text; // The string to be searched std::string word; // Substring to be found std::cout << "Enter the string to be searched and press Enter:\n"; std::getline(std::cin, text); std::cout << "Enter the string to be found and press Enter:\n"; std::getline(std::cin, word); size_t count{}; // Count of substring occurrences size_t index{}; // String index while ((index = text.find(word, index)) != std::string::npos) { ++count; index += word.length(); // Advance by full word (discards overlapping occurrences) } std::cout << "Your text contained " << count << " occurrences of \"" << word << "\"." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 07/Ex7_06.cpp ================================================ #include #include #include #include int main() { std::string text; // The string to be searched std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); const std::string separators{ " ,;:.\"!?'\n" }; // Word delimiters std::vector words; // Words found size_t start { text.find_first_not_of(separators) }; // First word start index while (start != std::string::npos) // Find the words { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of word if (end == std::string::npos) // Found a separator? end = text.length(); // No, so set to end of text words.push_back(text.substr(start, end - start)); // Store the word start = text.find_first_not_of(separators, end + 1); // Find first character of next word } std::cout << "Your string contains the following " << words.size() << " words:\n"; size_t count{}; // Number output for (const auto& word : words) { std::cout << std::format("{:15}", word); if (!(++count % 5)) std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 07/Ex7_07.cpp ================================================ // Replacing words in a string #include #include int main() { std::string text; // The string to be modified std::cout << "Enter a string terminated by *:\n"; std::getline(std::cin, text, '*'); std::string word; // The word to be replaced std::cout << "Enter the word to be replaced: "; std::cin >> word; std::string replacement; // The word to be substituted std::cout << "Enter the string to be substituted for " << word << ": "; std::cin >> replacement; if (word == replacement) // Verify there's something to do { std::cout << "The word and its replacement are the same.\n" << "Operation aborted." << std::endl; return 1; } size_t start {text.find(word)}; // Index of 1st occurrence of word while (start != std::string::npos) // Find and replace all occurrences { text.replace(start, word.length(), replacement); // Replace word start = text.find(word, start + replacement.length()); } std::cout << "\nThe string you entered is now:\n" << text << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_01.cpp ================================================ // Calculating powers #include #include // Function to calculate x to the power n double power(double x, int n) { double result{ 1.0 }; if (n >= 0) { for (int i{ 1 }; i <= n; ++i) result *= x; } else // n < 0 { for (int i{ 1 }; i <= -n; ++i) result /= x; } return result; } int main() { // Calculate powers of 8 from -3 to +3 for (int i{ -3 }; i <= 3; ++i) std::cout << std::format("{:10g}", power(8.0, i)); std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_02.cpp ================================================ // Calculating powers - rearranged #include #include //double power(double x, int n); // Function prototype - uncomment for successful compilation int main() { // Calculate powers of 8 from -3 to +3 for (int i{ -3 }; i <= 3; ++i) std::cout << std::format("{:10}", power(8.0, i)); std::cout << std::endl; } // Function to calculate x to the power n double power(double x, int n) { double result{ 1.0 }; if (n >= 0) { for (int i{ 1 }; i <= n; ++i) result *= x; } else // n < 0 { for (int i{ 1 }; i <= -n; ++i) result /= x; } return result; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_03.cpp ================================================ // Failing to modify the original value of a function argument #include double changeIt(double value_to_be_changed); // Function prototype int main() { double it {5.0}; double result {changeIt(it)}; std::cout << "After function execution, it = " << it << "\nResult returned is " << result << std::endl; } // Function that attempts to modify an argument and return it double changeIt(double it) { it += 10.0; // This modifies the copy std::cout << "Within function, it = " << it << std::endl; return it; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_04.cpp ================================================ // Modifying the value of a caller variable #include double changeIt(double* pointer_to_it); // Function prototype int main() { double it {5.0}; double result {changeIt(&it)}; // Now we pass the address std::cout << "After function execution, it = " << it << "\nResult returned is " << result << std::endl; } // Function to modify an argument and return it double changeIt(double* pit) { *pit += 10.0; // This modifies the original double std::cout << "Within function, *pit = " << *pit << std::endl; return *pit; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_05.cpp ================================================ // Passing an array to a function #include #include // For std::size() double average(double array[], size_t count); // Function prototype int main() { double values[] {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}; std::cout << "Average = " << average(values, std::size(values)) << std::endl; } // Function to compute an average double average(double array[], size_t count) { double sum {}; // Accumulate total in here for (size_t i {}; i < count; ++i) sum += array[i]; // Sum array elements return sum / count; // Return average } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_05A.cpp ================================================ // Passing an array to a function - false expectations // Note: with main() as defined in this file, // this program will likely either crash or produce garbage output! #include double average10(double array[10]); // Function prototype int main() { // double values[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; double values[] { 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(values) << std::endl; } // Function to compute an average double average10(double array[10]) /* The [10] does not mean what you might expect! */ { double sum{}; // Accumulate total in here for (size_t i{} ; i < 10; ++i) sum += array[i]; // Sum array elements return sum / 10; // Return average } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_06.cpp ================================================ // Passing a two-dimensional array to a function #include #include // For std::size() double yield(const double values[][4], size_t n); int main() { double beans[3][4] { { 1.0, 2.0, 3.0, 4.0}, { 5.0, 6.0, 7.0, 8.0}, { 9.0, 10.0, 11.0, 12.0} }; std::cout << "Yield = " << yield(beans, std::size(beans)) << std::endl; } // Function to compute total yield double yield(const double array[][4], size_t size) { double sum {}; for (size_t i {}; i < size; ++i) // Loop through rows { for (size_t j {}; j < std::size(array[i]); ++j) // Loop through elements in a row { sum += array[i][j]; } } return sum; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_06A.cpp ================================================ // Passing a two-dimensional array to a function (range-based for loop) #include #include // For std::size() double yield(const double values[][4], size_t n); int main() { double beans[3][4]{ { 1.0, 2.0, 3.0, 4.0}, { 5.0, 6.0, 7.0, 8.0}, { 9.0, 10.0, 11.0, 12.0} }; std::cout << "Yield = " << yield(beans, std::size(beans)) << std::endl; } // Function to compute total yield double yield(const double array[][4], size_t size) { double sum{}; for (size_t i{}; i < size; ++i) // Loop through rows { for (double val : array[i]) // Loop through elements in a row { sum += val; } } return sum; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_07.cpp ================================================ // Modifying the value of a caller variable references vs pointers #include void change_it_by_pointer(double* reference_to_it); // Pass pointer (by value) void change_it_by_reference(double& reference_to_it); // Pass by reference int main() { double it {5.0}; change_it_by_pointer(&it); // Now we pass the address std::cout << "After first function execution, it = " << it << std::endl; change_it_by_reference(it); // Now we pass a reference, not the value! std::cout << "After second function execution, it = " << it << std::endl; } void change_it_by_pointer(double* pit) { *pit += 10.0; // This modifies the original double } void change_it_by_reference(double& pit) { pit += 10.0; // This modifies the original double as well! } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_08.cpp ================================================ // Using a reference parameter #include #include #include #include using std::string; using std::vector; void find_words(vector& words, const string& str, const string& separators); void list_words(const vector& words); int main() { std::string text; // The string to be searched std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); const std::string separators {" ,;:.\"!?'\n"}; // Word delimiters std::vector words; // Words found find_words(words, text, separators); list_words(words); } void find_words(vector& words, const string& text, const string& separators) { size_t start {text.find_first_not_of(separators)}; // First word start index while (start != string::npos) // Find the words { size_t end{ text.find_first_of(separators, start + 1); } // Find end of word if (end == string::npos) // Found a separator? end = text.length(); // No, so set to end of text words.push_back(text.substr(start, end - start)); // Store the word start = text.find_first_not_of(separators, end + 1); // Find 1st character of next word } } void list_words(const vector& words) { std::cout << "Your string contains the following " << words.size() << " words:\n"; size_t count {}; // Number of outputted words for (const auto& word : words) { std::cout << std::format("{:>15}", word); if (!(++count % 5)) std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_09A.cpp ================================================ // Passing an array to a function - pass by reference // Note: with main() as defined in this file, this program will not compile... #include double average10(const double (&)[10]); // Function prototype int main() { // Use 10 values to make example compile... // double values[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; double values[] { 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(values) << std::endl; } // Function to compute an average double average10(const double (&array)[10]) /* Only arrays of length 10 can be passed! */ { double sum {}; // Accumulate total in here for (size_t i {}; i < 10; ++i) sum += array[i]; // Sum array elements return sum / 10; // Return average } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_09B.cpp ================================================ // Passing an array to a function - pass by reference improved #include #include // for std::size() double average10(const double (&)[10]); // Function prototype int main() { double values[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; // double values[] { 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(values) << std::endl; } // Function to compute an average double average10(const double (&array)[10]) { double sum {}; // Accumulate total in here for (double val : array) sum += val; // Sum array elements return sum / std::size(array); // Return average } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_09C.cpp ================================================ // Passing an array to a function - use std::array<> #include #include double average10(const std::array& array); // Function prototype int main() { std::array values{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; // std::array values{ 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(values) << std::endl; } // Function to compute an average double average10(const std::array& array) { double sum {}; // Accumulate total in here for (double val : array) sum += val; // Sum array elements return sum / array.size(); // Return average } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_10.cpp ================================================ // Implicit conversions of reference parameters #include void double_it(double& it) { it *= 2; } void print_it(const double& it) { std::cout << it << std::endl; } int main() { double d{123}; double_it(d); print_it(d); int i{456}; // double_it(i); /* error, does not compile! */ print_it(i); } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_11.cpp ================================================ // Using multiple default parameter values #include #include #include // The function prototype including defaults for parameters void show_data(const int data[], size_t count = 1, const std::string& title = "Data Values", size_t width = 10, size_t perLine = 5); int main() { int samples[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; int dataItem {-99}; show_data(&dataItem); dataItem = 13; show_data(&dataItem, 1, "Unlucky for some!"); show_data(samples, std::size(samples)); show_data(samples, std::size(samples), "Samples"); show_data(samples, std::size(samples), "Samples", 6); show_data(samples, std::size(samples), "Samples", 8, 4); } void show_data(const int data[], size_t count, const std::string& title, size_t width, size_t perLine) { std::cout << title << std::endl; // Display the title // Output the data values for (size_t i {}; i < count; ++i) { std::cout << std::format("{:{}}", data[i], width); // Display a data item if ((i+1) % perLine == 0) // Newline after perLine values std::cout << '\n'; } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_12.cpp ================================================ // Program that lists its command line arguments #include int main(int argc, char* argv[]) { for (int i{}; i < argc; ++i) std::cout << argv[i] << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_13.cpp ================================================ // Returning a pointer #include #include #include #include // for std::size() void show_data(const double data[], size_t count = 1, const std::string& title = "Data Values", size_t width = 10, size_t perLine = 5); const double* largest(const double data[], size_t count); const double* smallest(const double data[], size_t count); double* shift_range(double data[], size_t count, double delta); double* scale_range(double data[], size_t count, double divisor); double* normalize_range(double data[], size_t count); int main() { double samples[] { 11.0, 23.0, 13.0, 4.0, 57.0, 36.0, 317.0, 88.0, 9.0, 100.0, 121.0, 12.0 }; const size_t count{std::size(samples)}; // Number of samples show_data(samples, count, "Original Values"); // Output original values normalize_range(samples, count); // Normalize the values show_data(samples, count, "Normalized Values", 12); // Output normalized values } // Outputs an array of double values void show_data(const double data[], size_t count, const std::string& title, size_t width, size_t perLine) { std::cout << title << std::endl; // Display the title // Output the data values for (size_t i {}; i < count; ++i) { // Display a data item (uses a dynamic field width: see Chapter 7) std::cout << std::format("{:{}.6g}", data[i], width); if ((i + 1) % perLine == 0) // Newline after perLine values std::cout << '\n'; } std::cout << std::endl; } const double* smallest(const double data[], size_t count) { if (!count) return nullptr; // There is no smallest in an empty array size_t index_min {}; for (size_t i {1}; i < count; ++i) if (data[index_min] > data[i]) index_min = i; return &data[index_min]; } double* shift_range(double data[], size_t count, double delta) { for (size_t i {}; i < count; ++i) data[i] += delta; return data; } const double* largest(const double data[], size_t count) { if (!count) return nullptr; // There is no largest in an empty array size_t index_max {}; for (size_t i {1}; i < count; ++i) if (data[index_max] < data[i]) index_max = i; return &data[index_max]; } double* scale_range(double data[], size_t count, double divisor) { if (!divisor) return data; // Do nothing for a zero divisor for (size_t i{}; i < count; ++i) data[i] /= divisor; return data; } double* normalize_range(double data[], size_t count) { shift_range(data, count, -(*smallest(data, count))); return scale_range(data, count, *largest(data, count)); } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_14.cpp ================================================ // Overloading a function #include #include #include // Function prototypes double largest(const double data[], size_t count); double largest(const std::vector& data); int largest(const std::vector& data); std::string largest(const std::vector& words); // int largest(const std::vector& words); /* Above function overload would not compile: overloaded functions must differ in more than just their return type! */ int main() { double array[] {1.5, 44.6, 13.7, 21.2, 6.7}; std::vector numbers {15, 44, 13, 21, 6, 8, 5, 2}; std::vector data{3.5, 5, 6, -1.2, 8.7, 6.4}; std::vector names {"Charles Dickens", "Emily Bronte", "Jane Austen", "Henry James", "Arthur Miller"}; std::cout << "The largest of array is " << largest(array, std::size(array)) << std::endl; std::cout << "The largest of numbers is " << largest(numbers) << std::endl; std::cout << "The largest of data is " << largest(data) << std::endl; std::cout << "The largest of names is " << largest(names) << std::endl; } // Finds the largest of an array of double values double largest(const double data[], size_t count) { double max{ data[0] }; for (size_t i{ 1 }; i < count; ++i) if (max < data[i]) max = data[i]; return max; } // Finds the largest of a vector of double values double largest(const std::vector& data) { double max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of int values int largest(const std::vector& data) { int max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of string objects std::string largest(const std::vector& words) { std::string max_word {words[0]}; for (const auto& word : words) if (max_word < word) max_word = word; return max_word; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_15.cpp ================================================ // Overloading a function with reference parameters #include #include double larger(double a, double b); // Non-reference parameters long& larger(long& a, long& b); // Reference parameters int main() { double a_double {1.5}, b_double {2.5}; std::cout << std::format("The larger of double values {} and {} is {}\n", a_double, b_double, larger(a_double, b_double)); int a_int {15}, b_int {25}; std::cout << std::format("The larger of int values {} and {} is {}\n", a_int, b_int, larger(static_cast(a_int), static_cast(b_int))); } // Returns the larger of two floating point values double larger(double a, double b) { std::cout << "double larger() called." << std::endl; return a > b ? a : b; } // Returns the larger of two long references long& larger(long& a, long& b) { std::cout << "long ref larger() called" << std::endl; return a > b ? a : b; } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_16.cpp ================================================ // Recursive version of function for x to the power n, n positive or negative #include #include double power(double x, int n); int main() { for (int i{ -3 }; i <= 3; ++i) // Calculate powers of 8 from -3 to +3 std::cout << std::format("{:10g}", power(8.0, i)); std::cout << std::endl; } // Recursive function to calculate x to the power n double power(double x, int n) { if (n == 0) return 1.0; else if (n > 0) return x * power(x, n - 1); else /* n < 0 */ return 1.0 / power(x, -n); } ================================================ FILE: Examples/NoModules/Chapter 08/Ex8_17.cpp ================================================ // Sorting words recursively #include #include #include #include using Words = std::vector>; void swap(Words& words, size_t first, size_t second); void sort(Words& words); void sort(Words& words, size_t start, size_t end); void extract_words(Words& words, const std::string& text, const std::string& separators); void show_words(const Words& words); size_t max_word_length(const Words& words); int main() { Words words; std::string text; // The string to be sorted const auto separators{" ,.!?\"\n"}; // Word delimiters // Read the string to be processed from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); extract_words(words, text, separators); if (words.empty()) { std::cout << "No words in text." << std::endl; return 0; } sort(words); // Sort the words show_words(words); // Output the words } void extract_words(Words& words, const std::string& text, const std::string& separators) { size_t start {text.find_first_not_of(separators)}; // Start index of first word while (start != std::string::npos) { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of a word if (end == std::string::npos) // Found a separator? end = text.length(); // Yes, so set to end of text words.push_back(std::make_shared(text.substr(start, end - start))); start = text.find_first_not_of(separators, end + 1); // Find start next word } } void swap(Words& words, size_t first, size_t second) { auto temp{words[first]}; words[first] = words[second]; words[second] = temp; } // Sort strings in ascending sequence void sort(Words& words) { if (!words.empty()) sort(words, 0, words.size() - 1); } void sort(Words& words, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle address to partition set swap(words, start, (start + end) / 2); // Swap middle address with start // Check words against chosen word size_t current {start}; for (size_t i {start + 1}; i <= end; i++) { if (*words[i] < *words[start]) // Is word less than chosen word? swap(words, ++current, i); // Yes, so swap to the left } swap(words, start, current); // Swap chosen and last swapped words if (current > start) sort(words, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(words, current + 1, end); // Sort right subset if exists } size_t max_word_length(const Words& words) { size_t max {}; for (auto& pword : words) if (max < pword->length()) max = pword->length(); return max; } void show_words(const Words& words) { const size_t field_width {max_word_length(words) + 1}; const size_t words_per_line {8}; std::cout << std::format("{:{}}", *words[0], field_width); // Output first word size_t words_in_line {}; // Number of words in current line for (size_t i {1}; i < words.size(); ++i) { // Output newline when initial letter changes or after 8 per line if ((*words[i])[0] != (*words[i - 1])[0] || ++words_in_line == words_per_line) { words_in_line = 0; std::cout << std::endl; } std::cout << std::format("{:{}}", *words[i], field_width); // Output a word } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 09/Ex9_01.cpp ================================================ // Working with std::optional<> #include // std::optional<> is defined in the module #include #include std::optional find_last( const std::string& string, char to_find, std::optional start_index = std::nullopt); // or: ... start_index = {}); int main() { const auto string{ "Growing old is mandatory; growing up is optional." }; const std::optional found_a{ find_last(string, 'a') }; if (found_a) std::cout << "Found the last a at index " << *found_a << std::endl; const auto found_b{ find_last(string, 'b') }; if (found_b.has_value()) std::cout << "Found the last b at index " << found_b.value() << std::endl; // following line gives an error (cannot convert std::optional to size_t) // const size_t found_c{ find_last(string, 'c') }; const auto found_early_i{ find_last(string, 'i', 10) }; if (found_early_i != std::nullopt) std::cout << "Found an early i at index " << *found_early_i << std::endl; } std::optional find_last(const std::string& string, char to_find, std::optional start_index) { // code below will not work for empty strings if (string.empty()) return std::nullopt; // or: 'return std::optional{};' // or: 'return {};' // determine the starting index for the loop that follows: size_t index{ start_index.value_or(string.size() - 1) }; while (true) // never use while (index >= 0) here, as size_t is always >= 0! { if (string[index] == to_find) return index; if (index == 0) return std::nullopt; --index; } } ================================================ FILE: Examples/NoModules/Chapter 09/Ex9_02.cpp ================================================ // Using std::string_view parameters #include #include #include #include #include using std::string; using std::string_view; using std::vector; void find_words(vector& words, string_view str, string_view separators); void list_words(const vector& words); int main() { std::string text; // The string to be searched std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); const std::string separators{ " ,;:.\"!?'\n" }; // Word delimiters std::vector words; // Words found find_words(words, text, separators); list_words(words); } void find_words(vector& words, string_view text, string_view separators) { size_t start{ text.find_first_not_of(separators) }; // First word start index while (start != string_view::npos) // Find the words { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of word if (end == string_view::npos) // Found a separator? end = text.length(); // No, so set to end of text words.push_back(std::string{ text.substr(start, end - start) }); // Store the word // Or: words.emplace_back(text.substr(start, end - start)); // (in-place construction) start = text.find_first_not_of(separators, end + 1); // Find 1st character of next word } } void list_words(const vector& words) { std::cout << "Your string contains the following " << words.size() << " words:\n"; size_t count{}; // Number of outputted words for (const auto& word : words) { std::cout << std::format("{:>15}", word); if (!(++count % 5)) std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 09/Ex9_03.cpp ================================================ // Using std::span<> to reduce the number of overloads of largest() // Clearly the three resulting functions are still similar. // See Chapter 10 on how you can eliminate this duplication using function templates. #include #include #include #include #include // Old function prototypes //double largest(const double data[], size_t count); //double largest(const std::vector& data); //int largest(const std::vector& data); //std::string largest(const std::vector& words); // New function prototypes // (these functions work for any sequential input, not just arrays or vectors) /* Caution: these signatures are not ideal yet: see Ex9_03A */ double largest(std::span data); int largest(std::span data); std::string largest(std::span words); int main() { double array[] {1.5, 44.6, 13.7, 21.2, 6.7}; std::vector numbers {15, 44, 13, 21, 6, 8, 5, 2}; std::vector data{3.5, 5.0, 6.0, -1.2, 8.7, 6.4}; std::array array_data{ 3.5, 5.0, 6.0, -1.2, 8.7, 6.4 }; // Throwing in an std::array for good measure std::vector names {"Charles Dickens", "Emily Bronte", "Jane Austen", "Henry James", "Arthur Miller"}; std::cout << "The largest of array is " << largest(array) << std::endl; std::cout << "The largest of numbers is " << largest(numbers) << std::endl; std::cout << "The largest of data is " << largest(data) << std::endl; std::cout << "The largest of array_data is (also) " << largest(array_data) << std::endl; std::cout << "The largest of names is " << largest(names) << std::endl; } // Finds the largest of a span of values double largest(std::span data) { double max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of int values int largest(std::span data) { int max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of string objects std::string largest(std::span words) { std::string max_word {words[0]}; for (const auto& word : words) if (max_word < word) max_word = word; return max_word; } ================================================ FILE: Examples/NoModules/Chapter 09/Ex9_03A.cpp ================================================ // Using std::span to ensure largest() works for const inputs #include #include #include #include #include // Old function prototypes //double largest(const double data[], size_t count); //double largest(const std::vector& data); //int largest(const std::vector& data); //std::string largest(const std::vector& words); // New function prototypes // (these functions work for any sequential input, not just arrays or vectors) double largest(std::span data); int largest(std::span data); std::string largest(std::span words); int main() { const double array[] {1.5, 44.6, 13.7, 21.2, 6.7}; const std::vector numbers {15, 44, 13, 21, 6, 8, 5, 2}; const std::vector data{3.5, 5.0, 6.0, -1.2, 8.7, 6.4}; const std::array array_data{ 3.5, 5.0, 6.0, -1.2, 8.7, 6.4 }; // Throwing in an std::array for good measure const std::vector names {"Charles Dickens", "Emily Bronte", "Jane Austen", "Henry James", "Arthur Miller"}; std::cout << "The largest of array is " << largest(array) << std::endl; std::cout << "The largest of numbers is " << largest(numbers) << std::endl; std::cout << "The largest of data is " << largest(data) << std::endl; std::cout << "The largest of array_data is (also) " << largest(array_data) << std::endl; std::cout << "The largest of names is " << largest(names) << std::endl; } // Finds the largest of a span of values double largest(std::span data) { double max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of int values int largest(std::span data) { int max {data[0]}; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of string objects std::string largest(std::span words) { std::string max_word {words[0]}; for (const auto& word : words) if (max_word < word) max_word = word; return max_word; } ================================================ FILE: Examples/NoModules/Chapter 10/Ex10_01.cpp ================================================ // Using a function template #include #include #include template T larger(T a, T b); // Function template prototype int main() { std::cout << "Larger of 1.5 and 2.5 is " << larger(1.5, 2.5) << std::endl; std::cout << "Larger of 3.5 and 4.5 is " << larger(3.5, 4.5) << std::endl; int big_int {17011983}, small_int {10}; std::cout << std::format("Larger of {} and {} is {}\n", big_int, small_int, larger(big_int, small_int)); std::string a_string {"A"}, z_string {"Z"}; std::cout << std::format(R"(Larger of "{}" and "{}" is "{}")", a_string, z_string, larger(a_string, z_string)) << std::endl; } // Template for functions to return the larger of two values template T larger(T a, T b) { return a > b ? a : b; } ================================================ FILE: Examples/NoModules/Chapter 10/Ex10_02.cpp ================================================ // Overloading function templates #include #include #include template T larger(T a, T b); // Function template prototype template T* larger(T*, T*); template const T* larger(const std::vector& data); int main() { int big_int {17011983}, small_int {10}; std::cout << std::format("Larger of {} and {} is {}", big_int, small_int, larger(big_int, small_int)) << std::endl; std::cout << std::format("Larger of {} and {} is {}", big_int, small_int, *larger(&big_int, &small_int)) << std::endl; std::vector data {-1.4, 7.3, -100.0, 54.1, 16.3}; std::cout << "The largest value in data is " << *larger(data) << std::endl; std::vector words {"The", "higher", "the", "fewer"}; std::cout << std::format(R"(The largest word in words is "{}")", *larger(words)) << std::endl; } // Template for functions to return the larger of two values template T larger(T a, T b) { return a > b ? a : b; } template T* larger(T* a, T* b) { return *a > * b ? a : b; } template const T* larger(const std::vector& data) { const T* result {}; // The largest of an empty vector is nullptr for (auto& value : data) if (!result || value > *result) result = &value; return result; } ================================================ FILE: Examples/NoModules/Chapter 10/Ex10_03.cpp ================================================ // Using return type deduction with templates #include #include // Template for functions to return the larger of two values // Supports implicit converion of differently-typed arguments template auto larger(const T1& a, const T2& b) { return a > b ? a : b; } int main() { int small_int {10}; std::cout << "Larger of " << small_int << " and 9.6 is " << larger(small_int, 9.6) << std::endl; // deduced return type: double std::string a_string {"A"}; std::cout << "Larger of \"" << a_string << "\" and \"Z\" is \"" << larger(a_string, "Z") << '"' << std::endl; // deduced return type: std::string } ================================================ FILE: Examples/NoModules/Chapter 10/Ex10_03A.cpp ================================================ // Using return type deduction with templates (decltype(auto) instead of auto) #include #include #include // Template for functions to return the larger of two values // Supports implicit converion of differently-typed arguments template decltype(auto) larger(const T1& a, const T2& b) { return a > b ? a : b; } int main() { const int small_int {10}; std::cout << "Larger of " << small_int << " and 9.6 is " << larger(small_int, 9.6) << std::endl; // deduced return type: double const std::string a_string {"A"}; std::cout << "Larger of \"" << a_string << "\" and \"Z\" is \"" << larger(a_string, "Z") << '"' << std::endl; // deduced return type: std::string const std::vector v1{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; const std::vector v2{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 11 }; std::cout << "The larger of our two vectors ends with " << larger(v1, v2).back(); } ================================================ FILE: Examples/NoModules/Chapter 10/Ex10_04.cpp ================================================ // Defining templates for functions that accept fixed-size arrays #include template T average(const T(&array)[N]); int main() { double doubles[2]{ 1.0, 2.0 }; std::cout << average(doubles) << std::endl; double moreDoubles[]{ 1.0, 2.0, 3.0, 4.0 }; std::cout << average(moreDoubles) << std::endl; // double* pointer = doubles; // std::cout << average(pointer) << std::endl; /* will not compile */ std::cout << average({ 1.0, 2.0, 3.0, 4.0 }) << std::endl; int ints[] = { 1, 2, 3, 4 }; std::cout << average(ints) << std::endl; } template T average(const T(&array)[N]) { T sum{}; // Accumulate total in here for (size_t i{}; i < N; ++i) sum += array[i]; // Sum array elements return sum / N; // Return average } ================================================ FILE: Examples/NoModules/Chapter 11/Ex11_06/Ex11_06.cpp ================================================ // Defining and using a namespace #include #include namespace math { const double sqrt2{ 1.414213562373095 }; // the square root of 2 auto square(const auto& x) { return x * x; } auto pow4(const auto& x) { return square(square(x)); } } int main() { std::cout << "math::sqrt2 has the value " << math::sqrt2 << std::endl; std::cout << "This should be 0: " << (math::sqrt2 - std::numbers::sqrt2) << std::endl; std::cout << "This should be 2: " << math::square(math::sqrt2) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 11/Ex11_07/Ex11_07.cpp ================================================ // Separating declarations and definitions of functions // declared in a namespace. #include #include "math.h" int main() { const double values[]{ 10, 2, 1, 8, 3, 7, 4, 5, 6, 9 }; std::cout << "Arithmetic mean: " << math::averages::arithmetic_mean(values) << std::endl; std::cout << "Geometric mean: " << math::averages::geometric_mean(values) << std::endl; std::cout << "Root mean square: " << math::averages::rms(values) << std::endl; std::cout << "Median: " << math::averages::median(values) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 11/Ex11_07/math.cpp ================================================ #include "math.h" #include // For std::pow(), std::sqrt(), ... #include // For std::numeric_limits::quiet_NaN() #include // For std::numeric_limits::quiet_NaN() namespace { void quicksort(std::vector& data); // See Chapter 8 } // Option 1: define in nested namespace block (compact syntax) namespace math::averages { double arithmetic_mean(std::span data) { // The arithmetic mean, the most commonly used average, // is defined as the sum of all elements divided by the number of elements. double sum {}; for (auto value : data) sum += value; return data.empty() ? std::numeric_limits::quiet_NaN() // Or std::nan("") : sum / data.size(); } } // Option 2: define in nested namespace blocks namespace math { namespace averages { double geometric_mean(std::span data) { // The geometric mean of n elements // is defined as the n-th root of the product of all n elements double product{ 1 }; for (auto value : data) product *= value; return data.empty() ? std::numeric_limits::quiet_NaN() : std::pow(product, 1.0 / data.size()); } } } // Option 3: define using fully qualified function name double math::averages::rms(std::span data) { // The RMS or root mean square is defined as // square root of the arithmetic mean of the squares of the elements. double sum_squares{}; for (auto value : data) sum_squares += square(value); return data.empty() ? std::numeric_limits::quiet_NaN() // Or std::nan("") : std::sqrt(sum_squares / data.size()); } // Option 4: define using qualified name in outer namespace block namespace math { double averages::median(std::span data) { // The median of an odd number of elements is the value // that appears in the middle position of these elements when they are sorted. // The median of an even number of elements is typically // defined as the mean of the two middle elements in a sorted range. // We cannot sort the data span itself, because it's elements are const. // Therefore, we first copy the const input data to be able to sort it std::vector sorted; // Use range-based for loop to copy the input data. // See Chapter 20 for built-in means of copying a data range. for (auto value : data) sorted.push_back(value); // See Chapter 20 for built-in means of (partially) sorting data quicksort(sorted); const size_t mid = data.size() / 2; return data.empty() ? std::numeric_limits::quiet_NaN() // Or std::nan : data.size() % 2 == 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; } } namespace { void quicksort(std::vector& data, size_t start, size_t end); void quicksort(std::vector& data) { if (!data.empty()) quicksort(data, 0, data.size() - 1); } void quicksort(std::vector& data, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle value to partition set, // and move it to the front of the current range std::swap(data[start], data[(start + end) / 2]); // Compare all other values against chosen value at index start size_t current{ start }; for (size_t i{ start + 1 }; i <= end; i++) { if (data[i] < data[start]) // Is the value less than chosen value? std::swap(data[++current], data[i]); // Yes, so swap to the left } std::swap(data[start], data[current]); // Swap chosen and last swapped words if (current > start) quicksort(data, start, current - 1); // Sort left subset if exists if (end > current + 1) quicksort(data, current + 1, end); // Sort right subset if exists } } ================================================ FILE: Examples/NoModules/Chapter 11/Ex11_07/math.h ================================================ #ifndef MATH_H #define MATH_H #include namespace math { auto square(const auto& x) { return x * x; }; namespace averages { double arithmetic_mean(std::span data); double geometric_mean(std::span data); double rms(std::span data); double median(std::span data); } } #endif ================================================ FILE: Examples/NoModules/Chapter 11/Ex11_08/Ex11_08.cpp ================================================ // Using using declarations and using directives #include #include "squaring.h" /* Make names from the math namespace available locally */ auto my_hypot(const auto& x, const auto& y) // Renamed to my_hypot to avoid clashes with hypot() function in { using namespace math; // Or: // using math::square; // using math::sqrt; /* Same as, of course: using std::sqrt; */ return sqrt(square(x) + square(y)); } int main() { std::cout << "math::sqrt2 has the value " << math::sqrt2 << std::endl; std::cout << "This should be 0: " << (math::sqrt2 - std::numbers::sqrt2) << std::endl; std::cout << "This should be 2: " << math::square(math::sqrt2) << std::endl; std::cout << "This should be 5: " << my_hypot(3, 4) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 11/Ex11_08/squaring.h ================================================ #include // For std::sqrt() #include // For std::numbers::sqrt2 /* Re-export two existing entities from the math namespace using using declarations */ namespace math // Exports all nested declarations at once { using std::numbers::sqrt2; using std::sqrt; // Never 'using std::sqrt();' or 'using std::sqrt(double);'! auto square(const auto& x) { return x * x; } auto pow4(const auto& x) { return square(square(x)); } } /* Export all names from a namespace from the math namespace using a using directive */ //namespace math //{ // export using namespace std::numbers; //} ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_01/Ex12_01.cpp ================================================ // Defining a class constructor #include // Class to represent a box class Box { public: // Constructor Box(double length, double width, double height) { std::cout << "Box constructor called." << std::endl; m_length = length; m_width = width; m_height = height; } // Function to calculate the volume of a box double volume() { return m_length * m_width * m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; int main() { Box firstBox {80.0, 50.0, 40.0}; // Create a box double firstBoxVolume {firstBox.volume()}; // Calculate the box volume std::cout << "Volume of Box object is " << firstBoxVolume << std::endl; // Box secondBox; // Causes a compiler error message } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_01A/Ex12_01A.cpp ================================================ // Defining a default constructor #include // Class to represent a box class Box { public: // Box() {} // Explicitly defined default constructor Box() = default; // Defaulted default constructor // Constructor Box(double length, double width, double height) { std::cout << "Box constructor called." << std::endl; m_length = length; m_width = width; m_height = height; } // Function to calculate the volume of a box double volume() { return m_length * m_width * m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; int main() { Box firstBox {80.0, 50.0, 40.0}; // Create a box double firstBoxVolume {firstBox.volume()}; // Calculate the box volume std::cout << "Volume of Box object is " << firstBoxVolume << std::endl; Box secondBox; // No longer causes a compiler error message } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_02/Ex12_02.cpp ================================================ // Defining member functions outside a class // Note that, unlike what we said in the text, // in this example we put the function definitions after the main() function. // This illustrates that to invoke a member function, // the compiler again only needs access to the signature of the member function. #include // Class to represent a box class Box { public: Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; int main() { Box firstBox {80.0, 50.0, 40.0}; // Create a box double firstBoxVolume {firstBox.volume()}; // Calculate the box volume std::cout << "Volume of Box object is " << firstBoxVolume << std::endl; Box secondBox; // No longer causes a compiler error message } // Constructor definition Box::Box(double length, double width, double height) { std::cout << "Box constructor called." << std::endl; m_length = length; m_width = width; m_height = height; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_03/Ex12_03.cpp ================================================ // Using a member initializer list #include // Class to represent a box class Box { public: Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; int main() { Box firstBox {80.0, 50.0, 40.0}; // Create a box double firstBoxVolume {firstBox.volume()}; // Calculate the box volume std::cout << "Volume of Box object is " << firstBoxVolume << std::endl; Box secondBox; // No longer causes a compiler error message } // Constructor definition Box::Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} { std::cout << "Box constructor called." << std::endl; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_04/Ex12_04.cpp ================================================ // Using the explicit keyword // Uncomment "explicit" to make the compilation of // the expression "box1.hasLargerVolumeThan(50.0)" fail #include class Cube { public: /*explicit*/ Cube(double side); // Constructor double volume(); // Calculate volume of a cube bool hasLargerVolumeThan(Cube cube); // Compare volume of a cube with another private: double m_side; }; Cube::Cube(double side) : m_side{ side } { std::cout << "Cube constructor called." << std::endl; } double Cube::volume() { return m_side * m_side * m_side; } bool Cube::hasLargerVolumeThan(Cube cube) { return volume() > cube.volume(); } int main() { Cube box1{ 7.0 }; Cube box2{ 3.0 }; if (box1.hasLargerVolumeThan(box2)) std::cout << "box1 is larger than box2." << std::endl; else std::cout << "Volume of box1 is less than or equal to that of box2." << std::endl; std::cout << "Volume of box1 is " << box1.volume() << std::endl; if (box1.hasLargerVolumeThan(50.0)) std::cout << "Volume of box1 is greater than 50" << std::endl; else std::cout << "Volume of box1 is less than or equal to 50" << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_05/Ex12_05.cpp ================================================ // Delegating constructors #include class Box { public: Box(double length, double width, double height); explicit Box(double side); // Constructor for a cube (explicit!) Box() = default; // Defaulted default constructor double volume(); // Function to calculate the volume of a box private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; // A constructor that initializes all three member variables Box::Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} { std::cout << "Box constructor 1 called." << std::endl; } // This second constructor forwards to the first one to initialize all members. // Note that you do not repeat the explicit keyword here! Box::Box(double side) : Box{ side, side, side } { std::cout << "Box constructor 2 called." << std::endl; } int main() { Box box1{ 2.0, 3.0, 4.0 }; // An arbitrary box Box box2{ 5.0 }; // A box that is a cube std::cout << "box1 volume = " << box1.volume() << std::endl; std::cout << "box2 volume = " << box2.volume() << std::endl; } double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_05A/Ex12_05A.cpp ================================================ // Implementing the copy constructor // Note: this example is explained but not named in the text #include class Box { public: Box(double length, double width, double height); explicit Box(double side); // Constructor for a cube (explicit!) Box() = default; // Defaulted default constructor Box(const Box& box); // Copy constructor double volume(); // Function to calculate the volume of a box private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; // A basic copy constructor Box::Box(const Box& box) : m_length{ box.m_length }, m_width{ box.m_width }, m_height{ box.m_height } { std::cout << "Copy constructor called." << std::endl; } // A delegating copy constructor //Box::Box(const Box& box) : Box{ box.m_length, box.m_width, box.m_height } //{ // std::cout << "Copy constructor called." << std::endl; //} int main() { Box box1{ 2.0, 3.0, 4.0 }; // An arbitrary box Box box2{ 5.0 }; // A box that is a cube std::cout << "box1 volume = " << box1.volume() << std::endl; std::cout << "box2 volume = " << box2.volume() << std::endl; Box box3{ box2 }; std::cout << "box3 volume = " << box3.volume() << std::endl; // Volume = 125 } // A constructor that initializes all three member variables Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor 1 called." << std::endl; } // This second constructor forwards to the first one to initialize all members. // Note that you do not repeat the explicit keyword here! Box::Box(double side) : Box{ side, side, side } { std::cout << "Box constructor 2 called." << std::endl; } double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_06/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // Class to represent a box class Box { public: Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; // Constructor definition // (inline because this member is defined in a header file: see online Appendix A) inline Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Member function definition // (inline because this member is defined in a header file: see online Appendix A) inline double Box::volume() { return m_length * m_width * m_height; } #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_06/Ex12_06.cpp ================================================ // Defining classes in separate files. // Without modules, this normally means the class is defined in a header file, // and the member functions in source file. To mirror the book's exercise, though, // here we define all members in the header as well (as inline functions). // See online Appendix A for details. #include // For use of std::cout, std::endl, etc. #include "Box.h" // For use of the Box class int main() { Box myBox{ 6.0, 6.0, 18.5 }; // Create a box std::cout << "Volume of the first Box object is " << myBox.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_06A/Box.cpp ================================================ #include #include "Box.h" // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_06A/Box.h ================================================ #ifndef BOX_H #define BOX_H // Class to represent a box class Box { public: Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_06A/Ex12_06A.cpp ================================================ // Defining classes in separate files. // Without modules, this normally means the class is defined in a header file, // and the member functions in source file like we did for this example. // See online Appendix A for details. #include // For use of std::cout, std::endl, etc. #include "Box.h" // For use of the Box class int main() { Box myBox{ 6.0, 6.0, 18.5 }; // Create a box std::cout << "Volume of the first Box object is " << myBox.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_06B/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // Class to represent a box class Box { public: Box() = default; Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Function to calculate the volume of a box double volume() { return m_length * m_width * m_height; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_06B/Ex12_06B.cpp ================================================ // Defining classes with in-class member definitions. #include // For use of std::cout, std::endl, etc. #include "Box.h" // For use of the Box class int main() { Box myBox{ 6.0, 6.0, 18.5 }; // Create a box std::cout << "Volume of the first Box object is " << myBox.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_07/Box.cpp ================================================ #include #include "Box.h" // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_07/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: // Constructors Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box // Functions to provide access to the values of member variables double getLength() { return m_length; } double getWidth() { return m_width; } double getHeight() { return m_height; } // Functions to set member variable values void setLength(double length) { if (length > 0) m_length = length; } void setWidth(double width) { if (width > 0) m_width = width; } void setHeight(double height) { if (height > 0) m_height = height; } private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_07/Ex12_07.cpp ================================================ // Accessing private members through getters and setters #include "Box.h" #include int main() { Box myBox {3.0, 4.0, 5.0}; std::cout << "myBox dimensions are " << myBox.getLength() << " by " << myBox.getWidth() << " by " << myBox.getHeight() << std::endl; myBox.setLength(-20.0); // ignored! myBox.setWidth(40.0); myBox.setHeight(10.0); std::cout << "myBox dimensions are now " << myBox.getLength() // 3 (unchanged) << " by " << myBox.getWidth() // by 40 << " by " << myBox.getHeight() << std::endl; // by 10 } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_08/Box.cpp ================================================ #include #include "Box.h" // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Member function definition double Box::volume() { return m_length * m_width * m_height; } // Mutator function definitions Box& Box::setLength(double length) { if (length > 0) m_length = length; return *this; } Box& Box::setWidth(double width) { if (width > 0) m_width = width; return *this; } Box& Box::setHeight(double height) { if (height > 0) m_height = height; return *this; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_08/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: // Constructors Box() = default; Box(double length, double width, double height); double volume(); // Function to calculate the volume of a box // Inspector functions double getLength() { return m_length; } double getWidth() { return m_width; } double getHeight() { return m_height; } // Mutator functions Box& setLength(double length); Box& setWidth(double width); Box& setHeight(double height); private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_08/Ex12_08.cpp ================================================ // Accessing private members through getters and setters (method chaining variant) #include "Box.h" #include int main() { Box myBox {3.0, 4.0, 5.0}; std::cout << "myBox dimensions are " << myBox.getLength() << " by " << myBox.getWidth() << " by " << myBox.getHeight() << std::endl; myBox.setLength(-20.0).setWidth(40.0).setHeight(10.0); // Set all dimensions of myBox std::cout << "myBox dimensions are now " << myBox.getLength() // 3 (unchanged) << " by " << myBox.getWidth() // by 40 << " by " << myBox.getHeight() << std::endl; // by 10 } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_09/Box.cpp ================================================ #include #include "Box.h" // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Const member function definition double Box::volume() const { return m_length * m_width * m_height; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_09/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: // Constructors Box() = default; Box(double length, double width, double height); double volume() const; // Const function to calculate the volume of a box // Functions to provide access to the values of member variables (all const!) double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions to set member variable values (not const!) void setLength(double length) { if (length > 0) m_length = length; } void setWidth(double width) { if (width > 0) m_width = width; } void setHeight(double height) { if (height > 0) m_height = height; } private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_09/Ex12_09.cpp ================================================ // Const objects and const member functions #include "Box.h" #include int main() { // v-- this const was added... const Box myBox {3.0, 4.0, 5.0}; std::cout << "myBox dimensions are " << myBox.getLength() << " by " << myBox.getWidth() << " by " << myBox.getHeight() << std::endl; // Invoking mutators / setters is not possible on a const object: //myBox.setLength(-20.0); // ignored! //myBox.setWidth(40.0); //myBox.setHeight(10.0); //std::cout << "myBox dimensions are now " << myBox.getLength() // 3 (unchanged) // << " by " << myBox.getWidth() // by 40 // << " by " << myBox.getHeight() << std::endl; // by 10 std::cout << "myBox's volume is " << myBox.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_10/Box.cpp ================================================ #include #include "Box.h" // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Const member function definition double Box::volume() const { return m_length * m_width * m_height; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_10/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: // Constructors Box() = default; Box(double length, double width, double height); double volume() const; // Const function to calculate the volume of a box // Non-const overloads (return references to dimension variable) double& length() { std::cout << "non-const overload called\n"; return m_length; }; double& width() { std::cout << "non-const overload called\n"; return m_width; }; double& height() { std::cout << "non-const overload called\n"; return m_height; }; // Const overloads (return references to const variables) const double& length() const { std::cout << "const overload called\n"; return m_length; }; const double& width() const { std::cout << "const overload called\n"; return m_width; }; const double& height() const { std::cout << "const overload called\n"; return m_height; }; // Attempt to return non-const references to member variables from const functions // double& length() const { return m_length; }; // This must not be allowed to compile! // double& width() const { return m_width; }; // double& height() const { return m_height; }; private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_10/Ex12_10.cpp ================================================ // Overloading on const #include "Box.h" #include int main() { const Box constBox{ 1, 2, 3 }; // constBox.length() = 2; // Does not compile: good! std::cout << constBox.length() << std::endl; Box nonConstBox{ 3, 2, 1 }; nonConstBox.length() *= 2; std::cout << nonConstBox.length() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_11/Box.cpp ================================================ #include #include "Box.h" // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Const member function definition double Box::volume() const { return m_length * m_width * m_height; } // Modify mutable member variable from a const member function void Box::printVolume() const { // Count how many times printVolume() is called using a mutable member in a const function std::cout << "The volume of this box is " << volume() << std::endl; std::cout << "printVolume() has been called " << ++m_count << " time(s)" << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_11/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: // Constructors Box() = default; Box(double length, double width, double height); double volume() const; // Function to calculate the volume of a box void printVolume() const; // Function to print out the volume of a box (const!) // Functions to provide access to the values of member variables (all const!) double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions to set member variable values (not const!) void setLength(double length) { if (length > 0) m_length = length; } void setWidth(double width) { if (width > 0) m_width = width; } void setHeight(double height) { if (height > 0) m_height = height; } private: double m_length{1.0}; double m_width {1.0}; double m_height{1.0}; mutable unsigned m_count{}; // Counts the amount of time printVolume() is called }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_11/Ex12_11.cpp ================================================ // Const objects and const member functions #include "Box.h" #include int main() { const Box myBox {3.0, 4.0, 5.0}; std::cout << "myBox dimensions are " << myBox.getLength() << " by " << myBox.getWidth() << " by " << myBox.getHeight() << std::endl; myBox.printVolume(); myBox.printVolume(); myBox.printVolume(); } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_12/Box.cpp ================================================ #include #include "Box.h" // Constructor definition Box::Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } { std::cout << "Box constructor called." << std::endl; } // Const member function definition double Box::volume() const { return m_length * m_width * m_height; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_12/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: Box() : Box{ 1.0, 1.0, 1.0} {} // A delegating default constructor Box(double length, double width, double height); double volume() const; // Function to calculate the volume of a box friend double surfaceArea(const Box& box); // Friend function for the surface area private: double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_12/Ex12_12.cpp ================================================ // Using a friend function of a class #include #include #include "Box.h" int main() { Box box1 {2.2, 1.1, 0.5}; // An arbitrary box Box box2; // A default box auto box3{ std::make_unique(15.0, 20.0, 8.0) }; // Dynamically allocated Box std::cout << "Volume of box1 = " << box1.volume() << std::endl; std::cout << "Surface area of box1 = " << surfaceArea(box1) << std::endl; std::cout << "Volume of box2 = "<< box2.volume() << std::endl; std::cout << "Surface area of box2 = " << surfaceArea(box2) << std::endl; std::cout << "Volume of box3 = " << box3->volume() << std::endl; std::cout << "Surface area of box3 = " << surfaceArea(*box3) << std::endl; } // friend function to calculate the surface area of a Box object double surfaceArea(const Box& box) { return 2.0 * (box.m_length * box.m_width + box.m_length * box.m_height + box.m_height * box.m_width); } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_13/Box.cpp ================================================ #include #include "Box.h" Box::Box(double length, double width, double height) // Constructor definition : m_length{length}, m_width{width}, m_height{height} { std::cout << "Box constructor 1 called." << std::endl; } Box::Box(double side) : Box{side, side, side} // Constructor for a cube { std::cout << "Box constructor 2 called." << std::endl; } Box::Box() // Default constructor { std::cout << "Default Box constructor called." << std::endl; } Box::Box(const Box& box) // Copy constructor : m_length{box.m_length}, m_width{box.m_width}, m_height{box.m_height} { std::cout << "Box copy constructor called." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_13/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: /* Constructors */ Box(double length, double width, double height); Box(double side); // Constructor for a cube Box(); // Default constructor Box(const Box& box); // Copy constructor double volume() const { return m_length * m_width * m_height; }; private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_13/Ex12_13.cpp ================================================ // Creating an array of objects #include #include "Box.h" int main() { const Box box1 {2.0, 3.0, 4.0}; // An arbitrary box Box box2 {5.0}; // A box that is a cube std::cout << "box1 volume = " << box1.volume() << std::endl; std::cout << "box2 volume = " << box2.volume() << std::endl; Box box3 {box2}; std::cout << "box3 volume = " << box3.volume() << std::endl; // Volume = 125 std::cout << std::endl; Box boxes[6] {box1, box2, box3, Box {2.0}}; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_14/Box.cpp ================================================ #include #include "Box.h" Box::Box(double length, double width, double height) // Constructor definition : m_length{length}, m_width{width}, m_height{height} { ++s_object_count; std::cout << "Box constructor 1 called." << std::endl; } Box::Box(double side) : Box{side, side, side} // Constructor for a cube { // Do not increment s_object_count in forwarding constructor: // already incremented in the constructor this constructor forwards to! std::cout << "Box constructor 2 called." << std::endl; } Box::Box() // Default constructor { ++s_object_count; std::cout << "Default Box constructor called." << std::endl; } Box::Box(const Box& box) // Copy constructor : m_length{box.m_length}, m_width{box.m_width}, m_height{box.m_height} { ++s_object_count; std::cout << "Box copy constructor called." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_14/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: Box(); // Default constructor Box(double side); // Constructor for a cube Box(const Box& box); // Copy constructor Box(double length, double width, double height); double volume() const { return m_length * m_width * m_height; } size_t getObjectCount() const { return s_object_count; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; static inline size_t s_object_count {}; // Count of objects ever created }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_14/Ex12_14.cpp ================================================ // Using a static member variable #include #include "Box.h" int main() { const Box box1 {2.0, 3.0, 4.0}; // An arbitrary box Box box2 {5.0}; // A box that is a cube std::cout << "box1 volume = " << box1.volume() << std::endl; std::cout << "box2 volume = " << box2.volume() << std::endl; Box box3 {box2}; std::cout << "box3 volume = " << box3.volume() << std::endl; // Volume = 125 std::cout << std::endl; Box boxes[6] {box1, box2, box3, Box {2.0}}; std::cout << "\nThere are now " << box1.getObjectCount() << " Box objects.\n"; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_15/CylindricalBox.cpp ================================================ #include #include "CylindricalBox.h" CylindricalBox::CylindricalBox(float radius, float height, std::string_view material) : m_radius{ radius } , m_height{ height } , m_material{ material } { std::cout << "Box constructed consisting of " << material; if (material == s_default_material) { std::cout << " (the default material!)"; } std::cout << std::endl; } float CylindricalBox::volume() const { return PI * m_radius * m_radius * m_height; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_15/CylindricalBox.h ================================================ #ifndef CYLINDRICAL_BOX_H #define CYLINDRICAL_BOX_H #include #include class CylindricalBox { public: static inline const float s_max_radius { 35.0f }; static inline const float s_max_height { 60.0f }; static inline const std::string_view s_default_material { "paperboard" }; CylindricalBox(float radius, float height, std::string_view material = s_default_material); float volume() const; private: // The value of PI used by CylindricalBox's volume() function static inline const float PI { 3.141592f }; float m_radius; float m_height; std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_15/Ex12_15.cpp ================================================ // Defining and using static constants #include #include "CylindricalBox.h" int main() { CylindricalBox bigBox{ 1.23f, CylindricalBox::s_max_height, CylindricalBox::s_default_material }; std::cout << "The volume of bigBox is " << bigBox.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_16/Box.cpp ================================================ #include #include "Box.h" Box::Box(double length, double width, double height) // Constructor definition : m_length{length}, m_width{width}, m_height{height} { ++s_object_count; std::cout << "Box constructor 1 called." << std::endl; } Box::Box(double side) : Box{side, side, side} // Constructor for a cube { // Do not increment s_object_count in forwarding constructor: // already incremented in the constructor this constructor forwards to! std::cout << "Box constructor 2 called." << std::endl; } Box::Box() // Default constructor { ++s_object_count; std::cout << "Default Box constructor called." << std::endl; } Box::Box(const Box& box) // Copy constructor : m_length{box.m_length}, m_width{box.m_width}, m_height{box.m_height} { ++s_object_count; std::cout << "Box copy constructor called." << std::endl; } Box::~Box() // Destructor { std::cout << "Box destructor called." << std::endl; --s_object_count; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_16/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: Box(); // Default constructor Box(double side); // Constructor for a cube Box(const Box& box); // Copy constructor Box(double length, double width, double height); ~Box(); // Destructor double volume() const { return m_length * m_width * m_height; } static size_t getObjectCount() { return s_object_count; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; static inline size_t s_object_count {}; // Count of objects ever created }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_16/Ex12_16.cpp ================================================ // Implementing a destructor #include #include "Box.h" int main() { std::cout << "There are now " << Box::getObjectCount() << " Box objects." << std::endl; const Box box1 {2.0, 3.0, 4.0}; // An arbitrary box Box box2 {5.0}; // A box that is a cube std::cout << "There are now " << Box::getObjectCount() << " Box objects." << std::endl; for (double d {} ; d < 3.0 ; ++d) { Box box {d, d + 1.0, d + 2.0}; std::cout << "Box volume is " << box.volume() << std::endl; } std::cout << "There are now " << Box::getObjectCount() << " Box objects." << std::endl; auto pBox{ std::make_unique(1.5, 2.5, 3.5) }; std::cout << "Box volume is " << pBox->volume() << std::endl; std::cout << "There are now " << pBox->getObjectCount() << " Box objects." << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_17/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } void listBox() const { std::cout << std::format("Box({:.1f},{:.1f},{:.1f})", m_length, m_width, m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_17/Ex12_17.cpp ================================================ // Using a linked list #include "RandomBoxes.h" #include "Truckload.h" int main() { Truckload load1; // Create an empty list // Add 12 random Box objects to the list const size_t boxCount {12}; for (size_t i {} ; i < boxCount ; ++i) load1.addBox(randomSharedBox()); std::cout << "The first list:\n"; load1.listBoxes(); // Copy the truckload Truckload copy{load1}; std::cout << "The copied truckload:\n"; copy.listBoxes(); // Find the largest Box in the list SharedBox largestBox{load1.getFirstBox()}; SharedBox nextBox{load1.getNextBox()}; while (nextBox) { if (nextBox->compare(*largestBox) > 0) largestBox = nextBox; nextBox = load1.getNextBox(); } std::cout << "\nThe largest box in the first list is "; largestBox->listBox(); std::cout << std::endl; load1.removeBox(largestBox); std::cout << "\nAfter deleting the largest box, the list contains:\n"; load1.listBoxes(); const size_t nBoxes {20}; // Number of vector elements std::vector boxes; // Array of Box objects for (size_t i {} ; i < nBoxes ; ++i) boxes.push_back(randomSharedBox()); Truckload load2{boxes}; std::cout << "\nThe second list:\n"; load2.listBoxes(); auto smallestBox{ load2.getFirstBox() }; for (auto box{ load2.getNextBox() }; box; box = load2.getNextBox()) if (box->compare(*smallestBox) < 0) smallestBox = box; std::cout << "\nThe smallest box in the second list is "; smallestBox->listBox(); std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_17/Package.h ================================================ #ifndef PACKAGE_H #define PACKAGE_H #include "SharedBox.h" class Package { public: Package(SharedBox box) : m_box{box}, m_next{nullptr} {} // Constructor ~Package() { delete m_next; } // Destructor // Retrieve the Box pointer SharedBox getBox() const { return m_box; } // Retrieve or update the pointer to the next Package Package* getNext() { return m_next; } void setNext(Package* package) { m_next = package; } private: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_17/RandomBoxes.h ================================================ #ifndef RANDOM_BOXES_H #define RANDOM_BOXES_H #include "Box.h" #include // For random number generation #include // For std::bind() #include // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max inline auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } inline Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } inline auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_17/SharedBox.h ================================================ #ifndef SHARED_BOX_H #define SHARED_BOX_H #include #include "Box.h" using SharedBox = std::shared_ptr; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_17/Truckload.cpp ================================================ #include "Truckload.h" #include "Package.h" #include // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->getNext()) { addBox(package->getBox()); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } void Truckload::listBoxes() const { const size_t boxesPerLine{ 4 }; size_t count {}; for (Package* package{m_head}; package; package = package->getNext()) { std::cout << ' '; package->getBox()->listBox(); if (! (++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } SharedBox Truckload::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->getBox() : nullptr; } SharedBox Truckload::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->getNext(); // Move to the next package return m_current? m_current->getBox() : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->setNext(package); // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->getBox() == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->setNext(current->getNext()); // Update pointers in member variables where required: if (current == m_head) m_head = current->getNext(); if (current == m_tail) m_tail = previous; if (current == m_current) m_current = current->getNext(); current->setNext(nullptr); // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->getNext(); // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_17/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "SharedBox.h" #include // Do not #include "Package.inl" here to avoid this class definition to leak into consumers of Truckload // (best we can do here to mimick internal module implementation partitions...) class Package; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload void listBoxes() const; // Output the Boxes private: Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list Package* m_current {}; // Last retrieved from the list }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_18/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } void listBox() const { std::cout << std::format("Box({:.1f},{:.1f},{:.1f})", m_length, m_width, m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_18/Ex12_18.cpp ================================================ // Using nested classes #include "RandomBoxes.h" #include "Truckload.h" int main() { Truckload load1; // Create an empty list // Add 12 random Box objects to the list const size_t boxCount {12}; for (size_t i {} ; i < boxCount ; ++i) load1.addBox(randomSharedBox()); std::cout << "The first list:\n"; load1.listBoxes(); // Copy the truckload Truckload copy{load1}; std::cout << "The copied truckload:\n"; copy.listBoxes(); // Find the largest Box in the list SharedBox largestBox{load1.getFirstBox()}; SharedBox nextBox{load1.getNextBox()}; while (nextBox) { if (nextBox->compare(*largestBox) > 0) largestBox = nextBox; nextBox = load1.getNextBox(); } std::cout << "\nThe largest box in the first list is "; largestBox->listBox(); std::cout << std::endl; load1.removeBox(largestBox); std::cout << "\nAfter deleting the largest box, the list contains:\n"; load1.listBoxes(); const size_t nBoxes {20}; // Number of vector elements std::vector boxes; // Array of Box objects for (size_t i {} ; i < nBoxes ; ++i) boxes.push_back(randomSharedBox()); Truckload load2{boxes}; std::cout << "\nThe second list:\n"; load2.listBoxes(); auto smallestBox{ load2.getFirstBox() }; for (auto box{ load2.getNextBox() }; box; box = load2.getNextBox()) if (box->compare(*smallestBox) < 0) smallestBox = box; std::cout << "\nThe smallest box in the second list is "; smallestBox->listBox(); std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_18/RandomBoxes.h ================================================ #ifndef RANDOM_BOXES_H #define RANDOM_BOXES_H #include "Box.h" #include // For random number generation #include // For std::bind() #include // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max inline auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } inline Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } inline auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } #endif ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_18/Truckload.cpp ================================================ #include "Truckload.h" #include // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } void Truckload::listBoxes() const { const size_t boxesPerLine{ 4 }; size_t count {}; for (Package* package{m_head}; package; package = package->m_next) { std::cout << ' '; package->m_box->listBox(); if (! (++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } SharedBox Truckload::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } SharedBox Truckload::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; if (current == m_current) m_current = current->m_next; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } ================================================ FILE: Examples/NoModules/Chapter 12/Ex12_18/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload void listBoxes() const; // Output the Boxes private: class Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list Package* m_current {}; // Last retrieved from the list }; #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_01/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } bool operator<(const Box& aBox) const // Less-than operator { return volume() < aBox.volume(); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_01/Ex13_01.cpp ================================================ // Implementing a less-than operator #include #include #include "Box.h" int main() { std::vector boxes {Box {2.0, 2.0, 3.0}, Box {1.0, 3.0, 2.0}, Box {1.0, 2.0, 1.0}, Box {2.0, 3.0, 3.0}}; Box smallBox {boxes[0]}; for (const auto& box : boxes) { if (box < smallBox) smallBox = box; } std::cout << "The smallest box has dimensions " << smallBox.getLength() << 'x' << smallBox.getWidth() << 'x' << smallBox.getHeight() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_02/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } bool operator<(const Box& aBox) const // Less-than operator { return volume() < aBox.volume(); } bool operator<(double value) const; // Compare Box volume < double value private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; // Compare the volume of a Box object with a constant inline bool Box::operator<(double value) const { return volume() < value; } // Function comparing a constant with volume of a Box object inline bool operator<(double value, const Box& aBox) { return value < aBox.volume(); } #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_02/Ex13_02.cpp ================================================ // Using the overloaded 'less-than' operators for Box ojects #include #include #include #include "Box.h" // Display box dimensions void show(const Box& box) { std::cout << std::format("Box {:g}x{:g}x{:g}", box.getLength(), box.getWidth(), box.getHeight()) << std::endl; } int main() { std::vector boxes {Box {2.0, 2.0, 3.0}, Box {1.0, 3.0, 2.0}, Box {1.0, 2.0, 1.0}, Box {2.0, 3.0, 3.0}}; const double minVolume{6.0}; std::cout << "Objects with volumes less than " << minVolume << " are:\n"; for (const auto& box : boxes) if (box < minVolume) show(box); std::cout << "Objects with volumes greater than " << minVolume << " are:\n"; for (const auto& box : boxes) if (minVolume < box) show(box); } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_03/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const { return m_length == otherBox.m_length && m_width == otherBox.m_width && m_height == otherBox.m_height; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_03/Ex13_03.cpp ================================================ // Overloading <=> and == to fully support all comparison operators #include #include #include #include #include "Box.h" void show(const Box& box) { std::cout << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } void show(const Box& box1, std::string_view relationship, const Box& box2) { show(box1); std::cout << relationship; show(box2); std::cout << std::endl; } int main() { const std::vector boxes {Box {2.0, 1.5, 3.0}, Box {1.0, 3.0, 5.0}, Box {1.0, 2.0, 1.0}, Box {2.0, 3.0, 2.0}}; const Box theBox {3.0, 1.0, 4.0}; for (const auto& box : boxes) if (theBox > box) show(theBox, " is greater than ", box); // > works std::cout << std::endl; for (const auto& box : boxes) if (theBox != box) show(theBox, " is not equal to ", box); // != works std::cout << std::endl; for (const auto& box : boxes) if (6.0 <= box) // Yes, even double <= Box works!! { std::cout << "6 is less than or equal to "; show(box); std::cout << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_03A/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_03A/Ex13_03A.cpp ================================================ // Defaulting the == operator #include #include #include #include #include "Box.h" void show(const Box& box) { std::cout << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } void show(const Box& box1, std::string_view relationship, const Box& box2) { show(box1); std::cout << relationship; show(box2); std::cout << std::endl; } int main() { const std::vector boxes {Box {2.0, 1.5, 3.0}, Box {1.0, 3.0, 5.0}, Box {1.0, 2.0, 1.0}, Box {2.0, 3.0, 2.0}}; const Box theBox {3.0, 1.0, 4.0}; for (const auto& box : boxes) if (theBox > box) show(theBox, " is greater than ", box); // > works std::cout << std::endl; for (const auto& box : boxes) if (theBox != box) show(theBox, " is not equal to ", box); // != works std::cout << std::endl; for (const auto& box : boxes) if (6.0 <= box) // Yes, even double <= Box works!! { std::cout << "6 is less than or equal to "; show(box); std::cout << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_04/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream #include class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; inline std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_04/Ex13_04.cpp ================================================ // Overloading the << operator #include #include #include #include #include "Box.h" int main() { const std::vector boxes {Box {2.0, 1.5, 3.0}, Box {1.0, 3.0, 5.0}, Box {1.0, 2.0, 1.0}, Box {2.0, 3.0, 2.0}}; const Box theBox {3.0, 1.0, 4.0}; for (const auto& box : boxes) if (theBox > box) std::cout << theBox << " is greater than " << box << std::endl; // > works std::cout << std::endl; for (const auto& box : boxes) if (theBox != box) std::cout << theBox << " is not equal to " << box << std::endl; // != works std::cout << std::endl; for (const auto& box : boxes) if (6.0 <= box) // Yes, even double <= Box works!! std::cout << "6 is less than or equal to " << box << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_05/Box.cpp ================================================ #include "Box.h" #include #include // For the min() and max() function templates double Box::volume() const { return m_length * m_width * m_height; } Box Box::operator+(const Box& aBox) const { // New object has larger length and width, and sum of heights return Box{ std::max(m_length, aBox.m_length), std::max(m_width, aBox.m_width), m_height + aBox.m_height }; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_05/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box operator+(const Box& aBox) const; // Function to add two Box objects private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; std::ostream& operator<<(std::ostream& stream, const Box& box); #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_05/Ex13_05.cpp ================================================ // Using the addition operator for Box objects #include #include #include #include // For random number generation #include // For std::bind() #include "Box.h" // See Chapter 12 for an explanation of this function auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 1.0, max }; // Generate in [1, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const double limit {99}; // Upper limit on Box dimensions auto random { createUniformPseudoRandomNumberGenerator(limit) }; const size_t boxCount {20}; // Number of Box object to be created std::vector boxes; // Vector of Box objects // Create 20 Box objects for (size_t i {}; i < boxCount; ++i) boxes.push_back(Box{ random(), random(), random() }); size_t first {}; // Index of first Box object of pair size_t second {1}; // Index of second Box object of pair double minVolume {(boxes[first] + boxes[second]).volume()}; for (size_t i {}; i < boxCount - 1; ++i) { for (size_t j {i + 1}; j < boxCount; j++) { if (boxes[i] + boxes[j] < minVolume) { first = i; second = j; minVolume = (boxes[i] + boxes[j]).volume(); } } } std::cout << "The two boxes that sum to the smallest volume are " << boxes[first] << " and " << boxes[second] << '\n'; std::cout << std::format("The volume of the first box is {:.1f}\n", boxes[first].volume()); std::cout << std::format("The volume of the second box is {:.1f}\n", boxes[second].volume()); std::cout << "The sum of these boxes is " << (boxes[first] + boxes[second]) << '\n'; std::cout << std::format("The volume of the sum is {:.1f}", minVolume) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_06/Box.cpp ================================================ #include "Box.h" #include #include // For the min() and max() function templates double Box::volume() const { return m_length * m_width * m_height; } // Overloaded += operator Box& Box::operator+=(const Box& aBox) { // New object has larger length and width, and sum of heights m_length = std::max(m_length, aBox.m_length); m_width = std::max(m_width, aBox.m_width); m_height += aBox.m_height; return *this; } // Function to add two Box objects Box Box::operator+(const Box& aBox) const { Box copy{ *this }; copy += aBox; return copy; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_06/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box& operator+=(const Box& aBox); // Function to add a Box objects Box operator+(const Box& aBox) const; // Function to add two Box objects private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; std::ostream& operator<<(std::ostream& stream, const Box& box); #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_06/Ex13_06.cpp ================================================ // Using the addition operator for Box objects #include #include #include #include // For random number generation #include // For std::bind() #include "Box.h" // See Chapter 12 for an explanation of this function auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 1.0, max }; // Generate in [1, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const double limit {99}; // Upper limit on Box dimensions auto random { createUniformPseudoRandomNumberGenerator(limit) }; const size_t boxCount {20}; // Number of Box object to be created std::vector boxes; // Vector of Box objects // Create 20 Box objects for (size_t i {}; i < boxCount; ++i) boxes.push_back(Box{ random(), random(), random() }); size_t first {}; // Index of first Box object of pair size_t second {1}; // Index of second Box object of pair double minVolume {(boxes[first] + boxes[second]).volume()}; for (size_t i {}; i < boxCount - 1; ++i) { for (size_t j {i + 1}; j < boxCount; j++) { if (boxes[i] + boxes[j] < minVolume) { first = i; second = j; minVolume = (boxes[i] + boxes[j]).volume(); } } } std::cout << "The two boxes that sum to the smallest volume are " << boxes[first] << " and " << boxes[second] << '\n'; std::cout << std::format("The volume of the first box is {:.1f}\n", boxes[first].volume()); std::cout << std::format("The volume of the second box is {:.1f}\n", boxes[second].volume()); std::cout << "The sum of these boxes is " << (boxes[first] + boxes[second]) << '\n'; std::cout << std::format("The volume of the sum is {:.1f}", minVolume) << std::endl; Box sum{ 0, 0, 0 }; // Start from an empty Box for (const auto& box : boxes) // And then add all randomly generated Box objects sum += box; std::cout << "The sum of " << boxCount << " random boxes is " << sum << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_07/Ex13_07.cpp ================================================ // Implicit conversions reduce the number of operator functions #include #include "Integer.h" int main() { const Integer i{1}; const Integer j{2}; const auto result = (i * 2 + 4 / j - 1) % j; std::cout << result.getValue() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_07/Integer.h ================================================ #ifndef INTEGER_H #define INTEGER_H class Integer { public: Integer(int value = 0) : m_value{value} {} int getValue() const { return m_value; } void setValue(int value) { m_value = value; } private: int m_value; }; inline Integer operator+(const Integer& one, const Integer& other) { return one.getValue() + other.getValue(); } inline Integer operator-(const Integer& one, const Integer& other) { return one.getValue() - other.getValue(); } inline Integer operator*(const Integer& one, const Integer& other) { return one.getValue() * other.getValue(); } inline Integer operator/(const Integer& one, const Integer& other) { return one.getValue() / other.getValue(); } inline Integer operator%(const Integer& one, const Integer& other) { return one.getValue() % other.getValue(); } #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_08/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream #include class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} Box operator~() const { return Box{m_width, m_length, m_height}; // Width and length are swapped } double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; inline std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_08/Ex13_08.cpp ================================================ // Overloading a unary "rotate" operator #include #include "Box.h" int main() { Box someBox{ 1, 2, 3 }; std::cout << ~someBox << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_09/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream #include class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} Box& operator++(); // Prefix ++operator const Box operator++(int); // Postfix operator++ Box& operator--(); // Prefix --operator const Box operator--(int); // Postfix operator-- double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; inline Box& Box::operator++() // Prefix ++operator { ++m_length; ++m_width; ++m_height; return *this; } inline const Box Box::operator++(int) // Postfix operator++ { auto copy{ *this }; // Create a copy of the current object ++(*this); // Increment the current object using the prefix operator... return copy; // Return the unincremented copy } inline Box& Box::operator--() // Prefix --operator { --m_length; --m_width; --m_height; return *this; } inline const Box Box::operator--(int) // Postfix operator-- { auto copy{ *this }; // Create a copy of the current object --(*this); // Decrement the current object using the prefix operator... return copy; // Return copy of the original value } inline std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_09/Ex13_09.cpp ================================================ // Overloading pre- and postfix inrement and decrement operators #include #include "Box.h" int main() { Box theBox{ 3.0, 1.0, 3.0 }; std::cout << "Our test Box is " << theBox << std::endl; std::cout << "Postfix increment evaluates to the original object: " << theBox++ << std::endl; std::cout << "After postfix increment: " << theBox << std::endl; std::cout << "Prefix decrement evaluates to the decremented object: " << --theBox << std::endl; std::cout << "After prefix decrement: " << theBox << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_10/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f},{:.1f},{:.1f})", box.m_length, box.m_width, box.m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_10/Ex13_10.cpp ================================================ // Using the subscript operator #include #include #include // For random number generation #include // For std::bind() #include "Truckload.h" // See Chapter 12 for an explanation of this function auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 1.0, max }; // Generate in [1, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const double limit {99.0}; // Upper limit on Box dimensions auto random = createUniformPseudoRandomNumberGenerator(limit); Truckload load; const size_t boxCount {16}; // Number of Box object to be created // Create boxCount Box objects for (size_t i {}; i < boxCount; ++i) load.addBox(std::make_shared(random(), random(), random())); std::cout << "The boxes in the Truckload are:\n"; std::cout << load; // Find the largest Box in the Truckload double maxVolume {}; size_t maxIndex {}; size_t i {}; while (load[i]) { if (load[i]->volume() > maxVolume) { maxIndex = i; maxVolume = load[i]->volume(); } ++i; } std::cout << "\nThe largest box is: "; std::cout << *load[maxIndex] << std::endl; load.removeBox(load[maxIndex]); std::cout << "\nAfter deleting the largest box, the Truckload contains:\n"; std::cout << load; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_10/Truckload.cpp ================================================ #include "Truckload.h" #include // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } return nullptr; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_10/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; std::ostream& operator<<(std::ostream& stream, const Truckload& load); #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_11/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include #include // For the std::min()/max() function templates class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f},{:.1f},{:.1f})", box.m_length, box.m_width, box.m_height); } Box operator+(const Box& aBox) const // Function to add two Box objects { return Box{ std::max(m_length, aBox.m_length), std::max(m_width, aBox.m_width), m_height + aBox.m_height }; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_11/Ex13_11.cpp ================================================ // Modifying the result of an overloaded subscript operator #include #include #include // For random number generation #include // For std::bind() #include "Truckload.h" /* Caution: in the text, we suggest to add static SharedBox nullBox{}; to the Truckload class definition. This will not compile. In-class definitions of non-const static members are only allowed if you add the inline keyword, as we did in this solution. See Chapter 12 for more explanation, and for the alternative of defining the member out-of-class. */ // See Chapter 12 for an explanation of this function auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 1.0, max }; // Generate in [1, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const double limit {99.0}; // Upper limit on Box dimensions auto random = createUniformPseudoRandomNumberGenerator(limit); Truckload load; const size_t boxCount {16}; // Number of Box object to be created // Create boxCount Box objects for (size_t i {}; i < boxCount; ++i) load.addBox(std::make_shared(random(), random(), random())); std::cout << "The boxes in the Truckload are:\n"; std::cout << load; // Find the largest Box in the Truckload double maxVolume {}; size_t maxIndex {}; size_t i {}; while (load[i]) { if (load[i]->volume() > maxVolume) { maxIndex = i; maxVolume = load[i]->volume(); } ++i; } std::cout << "\nThe largest box is: "; std::cout << *load[maxIndex] << std::endl; load.removeBox(load[maxIndex]); std::cout << "\nAfter deleting the largest box, the Truckload contains:\n"; std::cout << load; load[0] = load[1]; // Copy 2nd element to the 1st std::cout << "\nAfter copying the 2nd element to the 1st, the list contains:\n"; std::cout << load; load[1] = std::make_shared(*load[2] + *load[3]); std::cout << "\nAfter making the 2nd element a pointer to the 3rd plus 4th," " the list contains:\n"; std::cout << load; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_11/Truckload.cpp ================================================ #include "Truckload.h" #include // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox& Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } return nullBox; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_11/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list // Caution: inline is required to allow for this definition to appear in-class. // In the text we forgot to add this. See Chapter 12 for more explanation, // and for the alternative of defining the member out-of-class. static inline SharedBox nullBox{}; // Pointer to nullptr }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; std::ostream& operator<<(std::ostream& stream, const Truckload& load); #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_12/Ex13_12.cpp ================================================ // Defining a copy assignment operator #include "Message.h" #include int main() { Message beware{ "Careful" }; Message warning; warning = beware; // Call assignment operator std::cout << "After assignment beware is: " << beware.getText() << std::endl; std::cout << "After assignment warning is: " << warning.getText() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_12/Message.cpp ================================================ #include "Message.h" Message& Message::operator=(const Message& message) { if (&message != this) { delete[] m_text; // Delete the previous char array m_text = new char[std::strlen(message.m_text) + 1]; // Replace it with a new array std::strcpy(m_text, message.m_text); // Copy the text (mind the order!) } return *this; // Return the left operand } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_12/Message.h ================================================ #ifndef MESSAGE_H #define MESSAGE_H #include // For std::strlen() and std::strcpy() class Message { public: explicit Message(const char* text = "") : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! { std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! } ~Message() { delete[] m_text; } Message& operator=(const Message& message); // Assignment operator const char* getText() const { return m_text; } private: char* m_text; }; #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_12A/Ex13_12A.cpp ================================================ // Always define both copy members together // (see also 'Rule of Five' in Chapter 18!) #include "Message.h" #include int main() { Message beware{ "Careful" }; Message warning; warning = beware; // Call assignment operator Message caution{ warning }; std::cout << "After assignment beware is: " << beware.getText() << std::endl; std::cout << "After assignment warning is: " << warning.getText() << std::endl; std::cout << "As a copy of warning, caution is: " << caution.getText() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_12A/Message.cpp ================================================ #include "Message.h" Message::Message(const Message& message) : Message{ message.m_text } // By far easiest and preferred option: forward to existing constructor! { } Message& Message::operator=(const Message& message) { if (&message != this) { delete[] m_text; // Delete the previous char array m_text = new char[std::strlen(message.m_text) + 1]; // Replace it with a new array std::strcpy(m_text, message.m_text); // Copy the text (mind the order!) } return *this; // Return the left operand } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_12A/Message.h ================================================ #ifndef MESSAGE_H #define MESSAGE_H #include // For std::strlen() and std::strcpy() class Message { public: explicit Message(const char* text = "") : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! { std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! } ~Message() { delete[] m_text; } Message(const Message& message); // Copy constructor Message& operator=(const Message& message); // Copy assignment operator const char* getText() const { return m_text; } private: char* m_text; }; #endif ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_12B/Ex13_12B.cpp ================================================ // Sneak preview: use copy-and-swap for copy assignment operator // (see Chapter 17 for details, including the noexcept keyword...) // In short, you eliminate duplicating the logic of copying an object // by expressing the copy assignment operator in terms of the copy constructor. // Less duplication means less room for error, less maintenance overhead, etc. // Note: this solution is hinted at in a Note in the text, but not explicitly named. #include "Message.h" #include int main() { Message beware{ "Careful" }; Message warning; warning = beware; // Call assignment operator Message caution{ warning }; std::cout << "After assignment beware is: " << beware.getText() << std::endl; std::cout << "After assignment warning is: " << warning.getText() << std::endl; std::cout << "As a copy of warning, caution is: " << caution.getText() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_12B/Message.cpp ================================================ #include "Message.h" #include // For std::swap() Message::Message(const Message& message) : Message{ message.m_text } // By far easiest and preferred option: forward to existing constructor! { } Message& Message::operator=(const Message& message) { // Note: self-assignment test no longer required (or even recommended) auto copy{ message }; // Copy-... swap(copy); // ...and-swap! return *this; // Always return reference to left operand } void Message::swap(Message& other) noexcept { std::swap(m_text, other.m_text); } ================================================ FILE: Examples/NoModules/Chapter 13/Ex13_12B/Message.h ================================================ #ifndef MESSAGE_H #define MESSAGE_H #include // For std::strlen() and std::strcpy() class Message { public: explicit Message(const char* text = "") : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! { std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! } ~Message() { delete[] m_text; } Message(const Message& message); // Copy constructor Message& operator=(const Message& message); // Copy assignment operator void swap(Message& other) noexcept; const char* getText() const { return m_text; } private: char* m_text; }; inline void swap(Message& one, Message& other) noexcept { return one.swap(other); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_01/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H class Box { public: Box() = default; Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_01/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: explicit Carton(std::string_view material = "Cardboard") // Constructor : m_material{material} {} private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_01/Ex14_01.cpp ================================================ // Defining and using a derived class #include #include "Box.h" // For the Box class #include "Carton.h" // For the Carton class int main() { // Create a Box object and two Carton objects Box box {40.0, 30.0, 20.0}; Carton carton; Carton chocolateCarton {"Solid bleached board"}; // Good old SBB // Check them out - sizes first of all std::cout << "box occupies " << sizeof box << " bytes" << std::endl; std::cout << "carton occupies " << sizeof carton << " bytes" << std::endl; std::cout << "candyCarton occupies " << sizeof chocolateCarton << " bytes" << std::endl; // Now volumes... std::cout << "box volume is " << box.volume() << std::endl; std::cout << "carton volume is " << carton.volume() << std::endl; std::cout << "chocolateCarton volume is " << chocolateCarton.volume() << std::endl; std::cout << "chocolateCarton length is " << chocolateCarton.getLength() << std::endl; // Uncomment any of the following for an error... // box.m_length = 10.0; // chocolateCarton.m_length = 10.0; } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_01A/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H class Box { public: Box() = default; Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_01A/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as private base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : private Box { public: explicit Carton(std::string_view mat = "Cardboard") : m_material {mat} {} using Box::volume; // Inherit as public private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_01A/Ex14_01A.cpp ================================================ // Changing the access specification of inherited members using using #include #include "Box.h" // For the Box class #include "Carton.h" // For the Carton class int main() { // Create a Box object and two Carton objects Box box {40.0, 30.0, 20.0}; Carton carton; Carton chocolateCarton {"Solid bleached board"}; // Good old SBB // Check them out - sizes first of all std::cout << "box occupies " << sizeof box << " bytes" << std::endl; std::cout << "carton occupies " << sizeof carton << " bytes" << std::endl; std::cout << "candyCarton occupies " << sizeof chocolateCarton << " bytes" << std::endl; // Now volumes... std::cout << "box volume is " << box.volume() << std::endl; std::cout << "carton volume is " << carton.volume() << std::endl; std::cout << "chocolateCarton volume is " << chocolateCarton.volume() << std::endl; // Uncomment any of the following for an error... // std::cout << "chocolateCarton length is " << chocolateCarton.getLength() << std::endl; // // box.m_length = 10.0; // chocolateCarton.m_length = 10.0; } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_02/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_02/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } /* // This constructor won't compile! Carton::Carton(double l, double w, double h, std::string_view material) : m_length{ l }, m_width{ w }, m_height{ h }, m_material{ material } { std::cout << "Carton(double,double,double,string_view) called.\n"; } */ /* // Constructor that will compile! Carton::Carton(double l, double w, double h, std::string_view material) : m_material{material} { m_length = l; // These should normally be initialized in a base class constructor... m_width = w; m_height = h; std::cout << "Carton(double,double,double,string_view) called.\n"; } */ private: std::string m_material {"Cardboard"}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_02/Ex14_02.cpp ================================================ // Calling base class constructors in a derived class constructor #include #include "Carton.h" // For the Carton class int main() { // Create four Carton objects Carton carton1; std::cout << std::endl; Carton carton2 {"White-lined chipboard"}; std::cout << std::endl; Carton carton3 {4.0, 5.0, 6.0, "PET"}; std::cout << std::endl; Carton carton4 {2.0, "Folding boxboard"}; std::cout << std::endl; std::cout << "carton1 volume is " << carton1.volume() << std::endl; std::cout << "carton2 volume is " << carton2.volume() << std::endl; std::cout << "carton3 volume is " << carton3.volume() << std::endl; std::cout << "carton4 volume is " << carton4.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_03/Box.h ================================================ // Box.h - defines Box class (with additional copy constuctor) #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor // Copy constructor Box(const Box& b) : m_length{ b.m_length }, m_width{ b.m_width }, m_height{ b.m_height } { std::cout << "Box copy constructor" << std::endl; } double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_03/Carton.h ================================================ // Carton.h - defines the Carton class (with additional copy constuctor) #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } // Copy constructor (wrong) Carton(const Carton& carton) : m_material {carton.m_material} { std::cout << "Carton copy constructor" << std::endl; } /* // Copy constructor (correct) Carton(const Carton& carton) : Box{ carton }, m_material{ carton.m_material } { std::cout << "Carton copy constructor" << std::endl; } */ private: std::string m_material {"Cardboard"}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_03/Ex14_03.cpp ================================================ // Using a derived class copy constructor #include #include "Carton.h" int main() { // Declare and initialize a Carton object Carton carton{ 20.0, 30.0, 40.0, "Expanded polystyrene" }; std::cout << std::endl; Carton cartonCopy{ carton }; // Use copy constructor std::cout << std::endl; std::cout << "Volume of carton is " << carton.volume() << std::endl << "Volume of cartonCopy is " << cartonCopy.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: Carton() = default; Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } private: std::string m_material {"Cardboard"}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04/Ex14_04.cpp ================================================ // Defaulting the default constructor in a derived class (working version) #include #include "Box.h" // For the Box class #include "Carton.h" // For the Carton class int main() { // Create three Carton objects Carton carton1; std::cout << std::endl; Carton carton2 {4.0, 5.0, 6.0, "PET"}; std::cout << std::endl; Carton carton3 {2.0, "Folding boxboard"}; std::cout << std::endl; std::cout << "carton1 volume is " << carton1.volume() << std::endl; std::cout << "carton2 volume is " << carton2.volume() << std::endl; std::cout << "carton3 volume is " << carton3.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04A/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } // Box() { std::cout << "Box() called.\n"; } // Default constructor removed! double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04A/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: Carton() = default; Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } private: std::string m_material {"Cardboard"}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04A/Ex14_04A.cpp ================================================ // Defaulting the default constructor in a derived class // without a default constructor in the base class // is equivalent to deleting the default constructor. #include #include "Box.h" // For the Box class #include "Carton.h" // For the Carton class /* Note: this example will fail to compile! */ int main() { // Create three Carton objects Carton carton1; std::cout << std::endl; Carton carton2 {4.0, 5.0, 6.0, "PET"}; std::cout << std::endl; Carton carton3 {2.0, "Folding boxboard"}; std::cout << std::endl; std::cout << "carton1 volume is " << carton1.volume() << std::endl; std::cout << "carton2 volume is " << carton2.volume() << std::endl; std::cout << "carton3 volume is " << carton3.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04B/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() = delete; // Default constructor explicitly deleted double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04B/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: Carton() = default; Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } private: std::string m_material {"Cardboard"}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04B/Ex14_04B.cpp ================================================ // Defaulting the default constructor in a derived class // with a deleted default constructor in the base class // is equivalent to deleting the default constructor. #include #include "Box.h" // For the Box class #include "Carton.h" // For the Carton class /* Note: this example will fail to compile! */ int main() { // Create three Carton objects Carton carton1; std::cout << std::endl; Carton carton2 {4.0, 5.0, 6.0, "PET"}; std::cout << std::endl; Carton carton3 {2.0, "Folding boxboard"}; std::cout << std::endl; std::cout << "carton1 volume is " << carton1.volume() << std::endl; std::cout << "carton2 volume is " << carton2.volume() << std::endl; std::cout << "carton3 volume is " << carton3.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04C/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } private: Box() { std::cout << "Box() called.\n"; } // Default constructor (made private) double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04C/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: Carton() = default; Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } private: std::string m_material {"Cardboard"}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_04C/Ex14_04C.cpp ================================================ // Defaulting the default constructor in a derived class // with a private constructor in the base class // is equivalent to deleting the default constructor. #include #include "Box.h" // For the Box class #include "Carton.h" // For the Carton class /* Note: this example will fail to compile! */ int main() { // Create three Carton objects Carton carton1; std::cout << std::endl; Carton carton2 {4.0, 5.0, 6.0, "PET"}; std::cout << std::endl; Carton carton3 {2.0, "Folding boxboard"}; std::cout << std::endl; std::cout << "carton1 volume is " << carton1.volume() << std::endl; std::cout << "carton2 volume is " << carton2.volume() << std::endl; std::cout << "carton3 volume is " << carton3.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_05/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_05/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { using Box::Box; // Inherit Box class constructors public: Carton(double length, double width, double height, std::string_view mat) : Box{length, width, height}, m_material{mat} { std::cout << "Carton(double,double,double,string_view) called.\n"; } private: std::string m_material {"Cardboard"}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_05/Ex14_05.cpp ================================================ // Inheriting constructors #include #include "Carton.h" // For the Carton class int main() { Carton cart; // Calls inherited default constructor Carton cube { 4.0 }; // Calls inherited constructor Carton copy { cube }; // Calls default copy constructor Carton carton {1.0, 2.0, 3.0}; // Calls inherited constructor Carton cerealCarton (50.0, 30.0, 20.0, "Chipboard"); // Calls Carton class constructor } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_06/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor // Copy constructor Box(const Box& b) : m_length{ b.m_length }, m_width{ b.m_width }, m_height{ b.m_height } { std::cout << "Box copy constructor" << std::endl; } // Destructor ~Box() { std::cout << "Box destructor" << std::endl; } double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_06/Carton.h ================================================ // Carton.h - defines the Carton class #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } // Copy constructor (correct) Carton(const Carton& carton) : Box{ carton }, m_material{ carton.m_material } { std::cout << "Carton copy constructor" << std::endl; } // Destructor ~Carton() { std::cout << "Carton destructor. Material = " << m_material << std::endl; } private: std::string m_material {"Cardboard"}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_06/Ex14_06.cpp ================================================ // Destructors in a class hierarchy #include #include "Carton.h" // For the Carton class int main() { Carton carton; Carton candyCarton{50.0, 30.0, 20.0, "SBB"}; // Solid bleached board std::cout << "carton volume is " << carton.volume() << std::endl; std::cout << "candyCarton volume is " << candyCarton.volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } // One new constructor Carton(double l, double w, double h, std::string_view m, double density, double thickness) : Carton{l, w, h, m} { m_thickness = thickness; m_density = density; std::cout << "Carton(double,double,double,string_view,double,double) called.\n"; } // Copy constructor Carton(const Carton& carton) : Box{carton}, m_material{carton.m_material}, m_thickness{carton.m_thickness}, m_density{carton.m_density} { std::cout << "Carton copy constructor" << std::endl; } // Destructor ~Carton() { std::cout << "Carton destructor. Material = " << m_material << std::endl; } double getWeight() const { return 2.0 * (m_length * m_width + m_width * m_height + m_height * m_length) * m_thickness * m_density; } private: std::string m_material {"Cardboard"}; double m_thickness {0.125}; // Material thickness in inch double m_density {0.2}; // Material density in pounds/cubic inch }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07/CerealPack.h ================================================ // Cerealpack.h - Class defining a carton of cereal #ifndef CEREALPACK_H #define CEREALPACK_H #include #include "Carton.h" #include "FoodContainer.h" class CerealPack : public Carton, public FoodContainer { public: CerealPack(double length, double width, double height, std::string_view cerealType) : Carton {length, width, height, "Chipboard"}, FoodContainer {cerealType} { std::cout << "CerealPack constructor" << std::endl; FoodContainer::volume = 0.9 * Carton::volume(); // Set food container's volume } ~CerealPack() { std::cout << "CerealPack destructor" << std::endl; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07/Ex14_07.cpp ================================================ // Ex14_07 - doesn't compile! // Using multiple inheritance can lead to ambiguity if members with // the same name are inherited from different base classes. #include #include "CerealPack.h" // For the CerealPack class int main() { CerealPack cornflakes{ 8.0, 3.0, 10.0, "Cornflakes" }; std::cout << "cornflakes volume is " << cornflakes.volume() << std::endl << "cornflakes weight is " << cornflakes.getWeight() << std::endl; /* // Here is one way to make this work (see Ex14_07A and B for alternatives) std::cout << "cornflakes volume is " << cornflakes.Carton::volume() << std::endl << "cornflakes weight is " << cornflakes.FoodContainer::getWeight() << std::endl; */ } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07/FoodContainer.h ================================================ #ifndef FOOD_CONTAINER_H #define FOOD_CONTAINER_H #include #include #include class FoodContainer { public: FoodContainer() { std::cout << "FoodContainer() called.\n"; } FoodContainer(std::string_view name) : name {name} { std::cout << "FoodContainer(string_view) called.\n"; } FoodContainer(std::string_view name, double density, double volume) : name {name}, density {density}, volume {volume} { std::cout << "FoodContainer(string_view,double,double) called.\n"; } ~FoodContainer() { std::cout << "FoodContainer destructor" << std::endl; } double getWeight() const { return volume * density; } protected: // Protected to make initialization in CerealPack constructor work (book says private) std::string name {"cereal"}; // Food type double volume {}; // Cubic inches double density {0.03}; // Pounds per cubic inch }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07A/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07A/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } // One new constructor Carton(double l, double w, double h, std::string_view m, double density, double thickness) : Carton{l, w, h, m} { m_thickness = thickness; m_density = density; std::cout << "Carton(double,double,double,string_view,double,double) called.\n"; } // Copy constructor Carton(const Carton& carton) : Box{carton}, m_material{carton.m_material}, m_thickness{carton.m_thickness}, m_density{carton.m_density} { std::cout << "Carton copy constructor" << std::endl; } // Destructor ~Carton() { std::cout << "Carton destructor. Material = " << m_material << std::endl; } double getWeight() const { return 2.0 * (m_length * m_width + m_width * m_height + m_height * m_length) * m_thickness * m_density; } private: std::string m_material {"Cardboard"}; double m_thickness {0.125}; // Material thickness in inch double m_density {0.2}; // Material density in pounds/cubic inch }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07A/CerealPack.h ================================================ // Cerealpack.h - Class defining a carton of cereal #ifndef CEREALPACK_H #define CEREALPACK_H #include #include "Carton.h" #include "FoodContainer.h" class CerealPack : public Carton, public FoodContainer { public: CerealPack(double length, double width, double height, std::string_view cerealType) : Carton {length, width, height, "Chipboard"}, FoodContainer {cerealType} { std::cout << "CerealPack constructor" << std::endl; FoodContainer::volume = 0.9 * Carton::volume(); // Set food container's volume } ~CerealPack() { std::cout << "CerealPack destructor" << std::endl; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07A/Ex14_07A.cpp ================================================ // Ex14_07A - Disambiguating ambiguous member inheritance through casting #include #include "CerealPack.h" // For the CerealPack class int main() { CerealPack cornflakes{ 8.0, 3.0, 10.0, "Cornflakes" }; std::cout << "cornflakes volume is " << static_cast(cornflakes).volume() << std::endl << "cornflakes weight is " << static_cast(cornflakes).getWeight() << std::endl; /* // Alternate solution by explicitly qualifying the base class name (see also Ex14_07) std::cout << "cornflakes volume is " << cornflakes.Carton::volume() << std::endl << "cornflakes weight is " << cornflakes.FoodContainer::getWeight() << std::endl; */ } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07A/FoodContainer.h ================================================ #ifndef FOOD_CONTAINER_H #define FOOD_CONTAINER_H #include #include #include class FoodContainer { public: FoodContainer() { std::cout << "FoodContainer() called.\n"; } FoodContainer(std::string_view name) : name {name} { std::cout << "FoodContainer(string_view) called.\n"; } FoodContainer(std::string_view name, double density, double volume) : name {name}, density {density}, volume {volume} { std::cout << "FoodContainer(string_view,double,double) called.\n"; } ~FoodContainer() { std::cout << "FoodContainer destructor" << std::endl; } double getWeight() const { return volume * density; } protected: // Protected to make initialization in CerealPack constructor work (book says private) std::string name {"cereal"}; // Food type double volume {}; // Cubic inches double density {0.03}; // Pounds per cubic inch }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07B/Box.h ================================================ // Box.h - defines Box class #ifndef BOX_H #define BOX_H #include // For standard streams #include // For string formatting class Box { public: // Constructors Box(double l, double w, double h) : m_length{l}, m_width{w}, m_height{h} { std::cout << "Box(double, double, double) called.\n"; } explicit Box(double side) : Box{side, side, side} { std::cout << "Box(double) called.\n"; } Box() { std::cout << "Box() called.\n"; } // Default constructor double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } protected: // Protected to facilitate further examples double m_length {1.0}; // later this chapter (should normally be private) double m_width {1.0}; double m_height {1.0}; }; // Stream output for Box objects inline std::ostream& operator<<(std::ostream& stream, const Box& box) { return stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); } #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07B/Carton.h ================================================ // Carton.h - defines the Carton class with the Box class as base #ifndef CARTON_H #define CARTON_H #include #include #include "Box.h" class Carton : public Box { public: Carton() { std::cout << "Carton() called.\n"; } explicit Carton(std::string_view material) : m_material{material} { std::cout << "Carton(string_view) called.\n"; } Carton(double side, std::string_view material) : Box{side}, m_material{material} { std::cout << "Carton(double,string_view) called.\n"; } Carton(double l, double w, double h, std::string_view material) : Box{l, w, h}, m_material{material} { std::cout << "Carton(double,double,double,string_view) called.\n"; } // One new constructor Carton(double l, double w, double h, std::string_view m, double density, double thickness) : Carton{l, w, h, m} { m_thickness = thickness; m_density = density; std::cout << "Carton(double,double,double,string_view,double,double) called.\n"; } // Copy constructor Carton(const Carton& carton) : Box{carton}, m_material{carton.m_material}, m_thickness{carton.m_thickness}, m_density{carton.m_density} { std::cout << "Carton copy constructor" << std::endl; } // Destructor ~Carton() { std::cout << "Carton destructor. Material = " << m_material << std::endl; } double getWeight() const { return 2.0 * (m_length * m_width + m_width * m_height + m_height * m_length) * m_thickness * m_density; } private: std::string m_material {"Cardboard"}; double m_thickness {0.125}; // Material thickness in inch double m_density {0.2}; // Material density in pounds/cubic inch }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07B/CerealPack.h ================================================ // Cerealpack.h - Class defining a carton of cereal #ifndef CEREALPACK_H #define CEREALPACK_H #include #include "Carton.h" #include "FoodContainer.h" class CerealPack : public Carton, public FoodContainer { public: CerealPack(double length, double width, double height, std::string_view cerealType) : Carton {length, width, height, "Chipboard"}, FoodContainer {cerealType} { std::cout << "CerealPack constructor" << std::endl; FoodContainer::volume = 0.9 * Carton::volume(); // Set food container's volume } ~CerealPack() { std::cout << "CerealPack destructor" << std::endl; } using Carton::volume; using FoodContainer::getWeight; }; #endif ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07B/Ex14_07B.cpp ================================================ // Ex14_07B - Disambiguating ambiguous member through // using declarations in the derived class (CerealPack) #include #include "CerealPack.h" // For the CerealPack class int main() { CerealPack cornflakes{ 8.0, 3.0, 10.0, "Cornflakes" }; std::cout << "cornflakes volume is " << cornflakes.volume() << std::endl << "cornflakes weight is " << cornflakes.getWeight() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 14/Ex14_07B/FoodContainer.h ================================================ #ifndef FOOD_CONTAINER_H #define FOOD_CONTAINER_H #include #include #include class FoodContainer { public: FoodContainer() { std::cout << "FoodContainer() called.\n"; } FoodContainer(std::string_view name) : name {name} { std::cout << "FoodContainer(string_view) called.\n"; } FoodContainer(std::string_view name, double density, double volume) : name {name}, density {density}, volume {volume} { std::cout << "FoodContainer(string_view,double,double) called.\n"; } ~FoodContainer() { std::cout << "FoodContainer destructor" << std::endl; } double getWeight() const { return volume * density; } protected: // Protected to make initialization in CerealPack constructor work (book says private) std::string name {"cereal"}; // Food type double volume {}; // Cubic inches double density {0.03}; // Pounds per cubic inch }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_01/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_01/Ex15_01.cpp ================================================ // Behavior of inherited functions in a derived class #include "Box.h" // For the Box class #include "ToughPack.h" // For the ToughPack class int main() { Box box {20.0, 30.0, 40.0}; // Create a box ToughPack hardcase {20.0, 30.0, 40.0}; // Create a tough pack - same size box.showVolume(); // Display volume of base box (calls volume() for box) hardcase.showVolume(); // Display volume of derived box (call volume() for hardcase) //std::cout << "hardcase volume is " << hardcase.volume() << std::endl; //Box* box_pointer{ &hardcase }; //std::cout << "hardcase volume through a Box* pointer is " // << box_pointer->volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_01/ToughPack.h ================================================ #ifndef TOUGHPACK_H #define TOUGHPACK_H #include "Box.h" class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const { return 0.85 * m_length * m_width * m_height; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_02/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_02/Carton.h ================================================ #ifndef CARTON_H #define CARTON_H #include // For std::max() #include #include #include "Box.h" class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_02/Ex15_02.cpp ================================================ // Using virtual functions #include #include "Box.h" // For the Box class #include "ToughPack.h" // For the ToughPack class #include "Carton.h" // For the Carton class int main() { Box box {20.0, 30.0, 40.0}; ToughPack hardcase {20.0, 30.0, 40.0}; // A derived box - same size Carton carton {20.0, 30.0, 40.0, "Plastic"}; // A different derived box box.showVolume(); // Volume of Box hardcase.showVolume(); // Volume of ToughPack carton.showVolume(); // Volume of Carton // Now using a base pointer... Box* base {&box}; // Points to type Box std::cout << "\nbox volume through base pointer is " << base->volume() << std::endl; base ->showVolume(); base = &hardcase; // Points to type ToughPack std::cout << "hardcase volume through base pointer is " << base->volume() << std::endl; base->showVolume(); base = &carton; // Points to type Carton std::cout << "carton volume through base pointer is " << base->volume() << std::endl; base->showVolume(); } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_02/ToughPack.h ================================================ #ifndef TOUGHPACK_H #define TOUGHPACK_H #include "Box.h" class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const { return 0.85 * m_length * m_width * m_height; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_03/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_03/Carton.h ================================================ #ifndef CARTON_H #define CARTON_H #include // For std::max() #include #include #include "Box.h" class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_03/Ex15_03.cpp ================================================ // Access specifiers and virtual functions #include #include "Box.h" // For the Box class #include "ToughPack.h" // For the ToughPack class #include "Carton.h" // For the Carton class int main() { Box box {20.0, 30.0, 40.0}; ToughPack hardcase {20.0, 30.0, 40.0}; // A derived box - same size Carton carton {20.0, 30.0, 40.0, "Plastic"}; // A different derived box box.showVolume(); // Volume of Box hardcase.showVolume(); // Volume of ToughPack carton.showVolume(); // Volume of Carton // Uncomment the following statement for an error // std::cout << "\nhardcase volume is " << hardcase.volume() << std::endl; // Now using a base pointer... Box* base {&box}; // Points to type Box std::cout << "\nbox volume through base pointer is " << base->volume() << std::endl; base->showVolume(); base = &hardcase; // Points to type ToughPack std::cout << "hardcase volume through base pointer is " << base->volume() << std::endl; base->showVolume(); base = &carton; // Points to type Carton std::cout << "carton volume through base pointer is " << base->volume() << std::endl; base->showVolume(); } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_03/ToughPack.h ================================================ #ifndef TOUGHPACK_H #define TOUGHPACK_H #include "Box.h" class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_04/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume(int i = 5) const { std::cout << "(Box argument = " << i << ") "; return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_04/Carton.h ================================================ #ifndef CARTON_H #define CARTON_H #include // For std::max() #include #include #include "Box.h" class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume(int i = 50) const override { std::cout << "(Carton argument = " << i << ") "; return std::max((m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5), 0.0); } private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_04/Ex15_04.cpp ================================================ // Default parameter values in virtual functions #include #include "Box.h" // For the Box class #include "ToughPack.h" // For the ToughPack class #include "Carton.h" // For the Carton class int main() { Box box{ 20.0, 30.0, 40.0 }; ToughPack hardcase{ 20.0, 30.0, 40.0 }; // A derived box - same size Carton carton{ 20.0, 30.0, 40.0, "Plastic" }; // A different derived box box.showVolume(); // Volume of Box hardcase.showVolume(); // Volume of ToughPack carton.showVolume(); // Volume of Carton std::cout << "\nhardcase volume is " << hardcase.volume() << std::endl; // Now using a base pointer... Box* base{ &box }; // Points to type Box std::cout << "\nbox volume through base pointer is " << base->volume() << std::endl; base->showVolume(); base = &hardcase; // Points to type ToughPack std::cout << "hardcase volume through base pointer is " << base->volume() << std::endl; base->showVolume(); base = &carton; // Points to type Carton std::cout << "carton volume through base pointer is " << base->volume() << std::endl; base->showVolume(); } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_04/ToughPack.h ================================================ #ifndef TOUGHPACK_H #define TOUGHPACK_H #include "Box.h" class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; // Function to calculate volume of a ToughPack allowing 15% for packing double volume(int i = 500) const override { std::cout << "(ToughPack argument = " << i << ") "; return 0.85 * m_length * m_width * m_height; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_05/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_05/Carton.h ================================================ #ifndef CARTON_H #define CARTON_H #include // For std::max() #include #include #include "Box.h" class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_05/Ex15_05.cpp ================================================ // Using a reference parameter to call virtual function #include #include "Box.h" // For the Box class #include "ToughPack.h" // For the ToughPack class #include "Carton.h" // For the Carton class // Global function to display the volume of a box void showVolume(const Box& box) { std::cout << "Box usable volume is " << box.volume() << std::endl; } int main() { Box box {20.0, 30.0, 40.0}; // A base box ToughPack hardcase {20.0, 30.0, 40.0}; // A derived box - same size Carton carton {20.0, 30.0, 40.0, "Plastic"}; // A different derived box showVolume(box); // Display volume of base box showVolume(hardcase); // Display volume of derived box showVolume(carton); // Display volume of derived box } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_05/ToughPack.h ================================================ #ifndef TOUGHPACK_H #define TOUGHPACK_H #include "Box.h" class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_06/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_06/Carton.h ================================================ #ifndef CARTON_H #define CARTON_H #include // For std::max() #include #include #include "Box.h" class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_06/Ex15_06.cpp ================================================ // Polymorphic vectors of smart pointers #include #include // For smart pointers #include // For vector #include "Box.h" // For the Box class #include "ToughPack.h" // For the ToughPack class #include "Carton.h" // For the Carton class int main() { // Careful: this first attempt at a mixed collection is a bad idea (object slicing!) std::vector boxes; boxes.push_back(Box{20.0, 30.0, 40.0}); boxes.push_back(ToughPack{20.0, 30.0, 40.0}); boxes.push_back(Carton{20.0, 30.0, 40.0, "plastic"}); for (const auto& box : boxes) box.showVolume(); std::cout << std::endl; // Next, we create a proper polymorphic vector<>: std::vector> polymorphicBoxes; polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0, "plastic")); for (const auto& box : polymorphicBoxes) box->showVolume(); } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_06/ToughPack.h ================================================ #ifndef TOUGHPACK_H #define TOUGHPACK_H #include "Box.h" class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_07/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} // Uncomment virtual to ensure destructors of derived classes are called correctly /*virtual*/ ~Box() { std::cout << "Box destructor called" << std::endl; } // More typical declaration of the destructor of a base class // virtual ~Box() = default; // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_07/Carton.h ================================================ #ifndef CARTON_H #define CARTON_H #include // For std::max() #include #include #include "Box.h" class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} ~Carton() { std::cout << "Carton destructor called" << std::endl; } // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_07/Ex15_07.cpp ================================================ // Polymorphic vectors of smart pointers #include #include // For smart pointers #include // For vector #include "Box.h" // For the Box class #include "ToughPack.h" // For the ToughPack class #include "Carton.h" // For the Carton class int main() { // Careful: this first attempt at a mixed collection is a bad idea (object slicing!) std::vector boxes; boxes.push_back(Box{20.0, 30.0, 40.0}); boxes.push_back(ToughPack{20.0, 30.0, 40.0}); boxes.push_back(Carton{20.0, 30.0, 40.0, "plastic"}); for (const auto& p : boxes) p.showVolume(); std::cout << std::endl; // Next, we create a proper polymorphic vector<>: std::vector> polymorphicBoxes; polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0, "plastic")); for (const auto& p : polymorphicBoxes) p->showVolume(); } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_07/ToughPack.h ================================================ #ifndef TOUGHPACK_H #define TOUGHPACK_H #include "Box.h" class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; ~ToughPack() { std::cout << "ToughPack destructor called" << std::endl; } protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_07A/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} virtual ~Box() = default; // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_07A/Carton.h ================================================ #ifndef CARTON_H #define CARTON_H #include // For std::max() #include #include #include "Box.h" class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} ~Carton() override = default; // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_07A/Ex15_07A.cpp ================================================ // Calling the base class version of a virtual function (see ToughPack::volume()) #include #include // For smart pointers #include // For vector #include "Box.h" // For the Box class #include "ToughPack.h" // For the ToughPack class #include "Carton.h" // For the Carton class int main() { // Careful: this first attempt at a mixed collection is a bad idea (object slicing!) std::vector boxes; boxes.push_back(Box{20.0, 30.0, 40.0}); boxes.push_back(ToughPack{20.0, 30.0, 40.0}); boxes.push_back(Carton{20.0, 30.0, 40.0, "plastic"}); for (const auto& p : boxes) p.showVolume(); std::cout << std::endl; // Next, we create a proper polymorphic vector<>: std::vector> polymorphicBoxes; polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0)); polymorphicBoxes.push_back(std::make_unique(20.0, 30.0, 40.0, "plastic")); for (const auto& p : polymorphicBoxes) p->showVolume(); } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_07A/ToughPack.h ================================================ #ifndef TOUGHPACK_H #define TOUGHPACK_H #include "Box.h" class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * Box::volume(); } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_08/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box(double length, double width, double height) : m_length {length}, m_width {width}, m_height {height} { std::cout << "Box constructor called for a Box of volume " << volume() << std::endl; } virtual ~Box() { std::cout << "Box destructor called for a Box of volume " << volume() << std::endl; } // Function to calculate volume of a Box virtual double volume() const { return m_length * m_width * m_height; } void showVolume() const { std::cout << "The volume from inside Box::showVolume() is " << volume() << std::endl; } private: double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_08/Ex14_08.cpp ================================================ // Calling virtual functions from constructors and destructors #include "Box.h" #include "ToughPack.h" int main() { ToughPack toughPack{ 1.0, 2.0, 3.0 }; toughPack.showVolume(); // Should show a volume equal to 85% of 1x2x3, or 5.1 } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_08/ToughPack.h ================================================ #ifndef TOUGH_PACK_H #define TOUGH_PACK_H #include "Box.h" class ToughPack : public Box { public: ToughPack(double length, double width, double height) : Box{length, width, height} { std::cout << "ToughPack constructor called for a Box of volume " << volume() << std::endl; } virtual ~ToughPack() { std::cout << "ToughPack destructor called for a Box of volume " << volume() << std::endl; } // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * Box::volume(); } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_09/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {} Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} virtual ~Box() = default; // Function to show the volume of an object void showVolume() const { std::cout << "Box usable volume is " << volume() << std::endl; } // Function to calculate the volume of a Box object virtual double volume() const { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_09/Carton.h ================================================ #ifndef CARTON_H #define CARTON_H #include // For std::max() #include #include #include "Box.h" class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_09/Ex15_09.cpp ================================================ // Using the typeid() operator #include #include // For the std::type_info class #include "Box.h" #include "Carton.h" // Define trivial non-polymorphic base and derived classes: class NonPolyBase {}; class NonPolyDerived : public NonPolyBase {}; Box& getSomeBox(); // Function returning a reference to a polymorphic type NonPolyBase& getSomeNonPoly(); // Function returning a reference to a non-polymorphic type int main() { // Part 1: typeid() on types and == operator std::cout << "Type double has name " << typeid(double).name() << std::endl; std::cout << "1 is " << (typeid(1) == typeid(int)? "an int" : "no int") << std::endl; // Part 2: typeid() on polymorphic references Carton carton{ 1, 2, 3, "paperboard" }; Box& boxReference{ carton }; std::cout << "Type of carton is " << typeid(carton).name() << std::endl; std::cout << "Type of boxReference is " << typeid(boxReference).name() << std::endl; std::cout << "These are " << (typeid(carton) == typeid(boxReference)? "" : "not ") << "equal" << std::endl; // Part 3: typeid() on polymorphic pointers Box* boxPointer{ &carton }; std::cout << "Type of &carton is " << typeid(&carton).name() << std::endl; std::cout << "Type of boxPointer is " << typeid(boxPointer).name() << std::endl; std::cout << "Type of *boxPointer is " << typeid(*boxPointer).name() << std::endl; // Part 4: typeid() with non-polymorphic classes NonPolyDerived derived; NonPolyBase& baseRef{ derived }; std::cout << "Type of baseRef is " << typeid(baseRef).name() << std::endl; // Part 5: typeid() on expressions const auto& type_info1{ typeid(getSomeBox()) }; // function call evaluated const auto& type_info2{ typeid(getSomeNonPoly()) }; // function call not evaluated std::cout << "Type of getSomeBox() is " << type_info1.name() << std::endl; std::cout << "Type of getSomeNonPoly() is " << type_info2.name() << std::endl; } Box& getSomeBox() { std::cout << "getSomeBox() called..." << std::endl; static Carton carton{ 2, 3, 5, "duplex" }; return carton; } NonPolyBase& getSomeNonPoly() { std::cout << "getSomeNonPoly() called..." << std::endl; static NonPolyDerived derived; return derived; } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_10/Box.h ================================================ #ifndef BOX_H #define BOX_H #include class Box { public: Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} virtual ~Box() = default; // Virtual destructor virtual double volume() const = 0; // Function to calculate the volume protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_10/Carton.h ================================================ #ifndef CARTON_H #define CARTON_H #include // For std::max() #include #include #include "Box.h" class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_10/Ex15_10.cpp ================================================ // Using an abstract class #include #include "Box.h" // For the Box class #include "ToughPack.h" // For the ToughPack class #include "Carton.h" // For the Carton class int main() { // Box box{20.0, 30.0, 40.0}; // Uncomment for compiler error ToughPack hardcase {20.0, 30.0, 40.0}; // A derived box - same size Carton carton {20.0, 30.0, 40.0, "plastic"}; // A different derived box Box*pBox {&hardcase}; // Base pointer - derived address std::cout << "hardcase volume is " << pBox->volume() << std::endl; pBox = &carton; // New derived address std::cout << "carton volume is " << pBox->volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_10/ToughPack.h ================================================ #ifndef TOUGHPACK_H #define TOUGHPACK_H #include "Box.h" class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_11/Box.h ================================================ #ifndef BOX_H #define BOX_H #include "Vessel.h" class Box : public Vessel { public: Box(double l, double w, double h) : m_length {l}, m_width {w}, m_height {h} {} double volume() const override { return m_length * m_width * m_height; } protected: // Should be private in production-quality code (add getters to access) double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_11/Can.h ================================================ // Can.h Class defining a cylindrical can of a given height and diameter #ifndef CAN_H #define CAN_H #include "Vessel.h" #include class Can : public Vessel { public: Can(double diameter, double height) : m_diameter {diameter}, m_height {height} {} double volume() const override { return std::numbers::pi * m_diameter * m_diameter * m_height / 4; } private: double m_diameter, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_11/Carton.h ================================================ #ifndef CARTON_H #define CARTON_H #include // For std::max() #include #include #include "Box.h" class Carton : public Box { public: // Constructor explicitly calling the base constructor Carton(double l, double w, double h, std::string_view mat = "cardboard") : Box{l, w, h}, m_material{mat} {} // Function to calculate the volume of a Carton object double volume() const override { const double volume {(m_length - 0.5) * (m_width - 0.5) * (m_height - 0.5)}; return std::max(volume, 0.0); // Or: return volume > 0.0 ? volume : 0.0; } private: std::string m_material; }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_11/Ex15_11.cpp ================================================ // Using an interface class and indirect base classes #include #include // For the vector container #include "Box.h" // For the Box class #include "ToughPack.h" // For the ToughPack class #include "Carton.h" // For the Carton class #include "Can.h" // for the Can class int main() { Box box {40, 30, 20}; Can can {10, 3}; Carton carton {40, 30, 20, "Plastic"}; ToughPack hardcase {40, 30, 20}; std::vector vessels {&box, &can, &carton, &hardcase}; for (const auto* vessel : vessels) std::cout << "Volume is " << vessel->volume() << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_11/ToughPack.h ================================================ #ifndef TOUGHPACK_H #define TOUGHPACK_H #include "Box.h" class ToughPack : public Box { public: // Inherit the Box(length, width, height) constructor using Box::Box; protected: // Function to calculate volume of a ToughPack allowing 15% for packing double volume() const override { return 0.85 * m_length * m_width * m_height; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 15/Ex15_11/Vessel.h ================================================ // Vessel.h Abstract class defining a vessel #ifndef VESSEL_H #define VESSEL_H class Vessel { public: virtual ~Vessel() = default; // As always: a virtual destructor! virtual double volume() const = 0; }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_01/Ex16_01.cpp ================================================ // Throwing and catching exceptions #include int main() { for (size_t i {}; i < 5; ++i) { try { if (i < 2) throw i; std::cout << "i not thrown - value is " << i << std::endl; if (i > 3) throw "Here is another!"; std::cout << "End of the try block." << std::endl; } catch (size_t i) // Catch exceptions of type size_t { std::cout << "i caught - value is " << i << std::endl; } catch (const char* message) // Catch exceptions of type char* { std::cout << "message caught - value is \"" << message << '"' << std::endl; } std::cout << "End of the for loop body (after the catch blocks)" << " - i is " << i << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_02/Ex16_02.cpp ================================================ // Throw an exception object #include #include "Troubles.h" void trySomething(int i); int main() { for (int i {}; i < 2; ++i) { try { trySomething(i); } catch (const Trouble& t) { // What seems to be the trouble? std::cout << "Exception: " << t.what() << std::endl; } } } void trySomething(int i) { // There's always trouble when trying something... if (i == 0) throw Trouble {}; else throw Trouble {"Nobody knows the trouble I've seen..."}; } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_02/Troubles.h ================================================ // Exception class definition #ifndef MYTROUBLES_H #define MYTROUBLES_H #include #include class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} std::string_view what() const { return m_message; } private: std::string m_message; }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_03/Ex16_03.cpp ================================================ // Throwing and catching objects in a hierarchy #include #include "Troubles.h" int main() { for (int i {}; i < 7; ++i) { try { if (i == 3) throw Trouble{}; else if (i == 5) throw MoreTrouble{}; else if (i == 6) throw BigTrouble{}; } catch (const BigTrouble& t) { std::cout << "BigTrouble object caught: " << t.what() << std::endl; } catch (const MoreTrouble& t) { std::cout << "MoreTrouble object caught: " << t.what() << std::endl; } catch (const Trouble& t) { std::cout << "Trouble object caught: " << t.what() << std::endl; } std::cout << "End of the for loop (after the catch blocks) - i is " << i << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_03/Troubles.h ================================================ // MyTroubles.h Exception classes #ifndef MYTROUBLES_H #define MYTROUBLES_H #include #include class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_04/Ex16_04.cpp ================================================ // Catching exceptions with a base class handler #include #include // for the type_info type returned by the typeid operator #include "Troubles.h" int main() { for (int i {}; i < 7; ++i) { try { if (i == 3) throw Trouble{}; else if (i == 5) throw MoreTrouble{}; else if (i == 6) throw BigTrouble{}; } catch (const Trouble& t) { //std::cout << "Trouble object caught: " << t.what() << std::endl; std::cout << typeid(t).name() << " object caught: " << t.what() << std::endl; } std::cout << "End of the for loop (after the catch blocks) - i is " << i << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_04/Troubles.h ================================================ // MyTroubles.h Exception classes #ifndef MYTROUBLES_H #define MYTROUBLES_H #include #include class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_05/Ex16_05.cpp ================================================ // Rethrowing exceptions #include #include #include "Troubles.h" int main() { for (int i {}; i < 7; ++i) { try { try { if (i == 3) throw Trouble{}; else if (i == 5) throw MoreTrouble{}; else if (i == 6) throw BigTrouble{}; } catch (const Trouble& t) { if (typeid(t) == typeid(Trouble)) std::cout << "Trouble object caught in inner block: " << t.what() << std::endl; else throw; // Rethrow current exception } } catch (const Trouble& t) { std::cout << typeid(t).name() << " object caught in outer block: " << t.what() << std::endl; } std::cout << "End of the for loop (after the catch blocks) - i is " << i << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_05/Troubles.h ================================================ // MyTroubles.h Exception classes #ifndef MYTROUBLES_H #define MYTROUBLES_H #include #include class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_06/Ex16_06.cpp ================================================ // Catching any exception #include #include // For use of typeid() #include "Troubles.h" int main() { for (int i {}; i < 7; ++i) { try { try { if (i == 3) throw Trouble{}; else if (i == 5) throw MoreTrouble{}; else if (i == 6) throw BigTrouble{}; } catch (const BigTrouble& bt) { std::cout << "Oh dear, big trouble. Let's handle it here and now." << std::endl; // Do not rethrow... } catch (...) // Catch any other exception { std::cout << "We caught something else! Let's rethrow it. " << std::endl; throw; // Rethrow current exception } } catch (const Trouble& t) { std::cout << typeid(t).name() << " object caught in outer block: " << t.what() << std::endl; } std::cout << "End of the for loop (after the catch blocks) - i is " << i << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_06/Troubles.h ================================================ // MyTroubles.h Exception classes #ifndef MYTROUBLES_H #define MYTROUBLES_H #include #include class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07/Ex16_07.cpp ================================================ // Exceptions may result in resource leaks! #include #include // For std::sqrt() #include "Troubles.h" double computeValue(size_t x); // A function to compute a single value double* computeValues(size_t howMany); // A function to compute an array of values int main() { try { double* values{ computeValues(10'000) }; // Unfortunately we won't be making it this far... delete[] values; } catch (const Trouble&) { std::cout << "No worries: I've caught it!" << std::endl; } } double* computeValues(size_t howMany) { double* values{ new double[howMany] }; for (size_t i{}; i < howMany; ++i) values[i] = computeValue(i); return values; } double computeValue(size_t x) { if (x < 100) return std::sqrt(x); // Return the square root of the input argument else throw Trouble{ "The trouble with trouble is, it starts out as fun!" }; } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07/Troubles.h ================================================ // MyTroubles.h Exception classes #ifndef MYTROUBLES_H #define MYTROUBLES_H #include #include class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07A/Ex16_07A.cpp ================================================ // Use of a try-catch block to fix the memory leak! #include #include // For std::sqrt() #include "Troubles.h" double computeValue(size_t x); // A function to compute a single value double* computeValues(size_t howMany); // A function to compute an array of values int main() { try { double* values{ computeValues(10'000) }; // Unfortunately we won't be making it this far... delete[] values; } catch (const Trouble&) { std::cout << "No worries: I've caught it!" << std::endl; } } double* computeValues(size_t howMany) { double* values{ new double[howMany] }; try { for (size_t i {}; i < howMany; ++i) values[i] = computeValue(i); return values; } catch (const Trouble&) { std::cout << "I sense trouble... Freeing memory..." << std::endl; delete[] values; throw; } } double computeValue(size_t x) { if (x < 100) return std::sqrt(x); // Return the square root of the input argument else throw Trouble{ "The trouble with trouble is, it starts out as fun!" }; } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07A/Troubles.h ================================================ // MyTroubles.h Exception classes #ifndef MYTROUBLES_H #define MYTROUBLES_H #include #include class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07B/DoubleArrayRAII.h ================================================ // A custom RAII class to manage a dynamic double[] array resource #ifndef DOUBLE_ARRAY_RAII_H #define DOUBLE_ARRAY_RAII_H #include class DoubleArrayRAII final { public: explicit DoubleArrayRAII(size_t size) : m_resource{ new double[size] } {} ~DoubleArrayRAII() { std::cout << "Freeing memory..." << std::endl; delete[] m_resource; } // Delete copy constructor and assignment operator DoubleArrayRAII(const DoubleArrayRAII&) = delete; DoubleArrayRAII& operator=(const DoubleArrayRAII&) = delete; // Array subscript operator double& operator[](size_t index) noexcept { return m_resource[index]; } const double& operator[](size_t index) const noexcept { return m_resource[index]; } // Function to access the encapsulated resource double* get() const noexcept { return m_resource; } // Function to instruct the RAII object to hand over the resource. // Once called, the RAII object shall no longer release the resource // upon destruction anymore. Returns the resource in the process. double* release() noexcept { double* result = m_resource; m_resource = nullptr; return result; } private: double* m_resource; }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07B/Ex16_07B.cpp ================================================ // Exceptions may result in resource leaks! #include #include // For std::sqrt() #include "Troubles.h" #include "DoubleArrayRAII.h" double computeValue(size_t x); // A function to compute a single value double* computeValues(size_t howMany); // A function to compute an array of values int main() { try { double* values{ computeValues(10'000) }; // Unfortunately we won't be making it this far... delete[] values; } catch (const Trouble&) { std::cout << "No worries: I've caught it!" << std::endl; } } double* computeValues(size_t howMany) { DoubleArrayRAII values{ howMany }; for (size_t i{}; i < howMany; ++i) values[i] = computeValue(i); return values.release(); } double computeValue(size_t x) { if (x < 100) return std::sqrt(x); // Return the square root of the input argument else throw Trouble{ "The trouble with trouble is, it starts out as fun!" }; } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07B/Troubles.h ================================================ // MyTroubles.h Exception classes #ifndef MYTROUBLES_H #define MYTROUBLES_H #include #include class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07C/Ex16_07C.cpp ================================================ // Avoid resource leaks due to exceptions using std::unique_ptr<> // Note: this example is given but not named in the text. // Instead of a custom RAII class DoubleArrayRAII, it uses std::unique_ptr<>. // Unlike the former, the latter can be returned from computeValues() as well. #include #include #include // For std::sqrt() #include "Troubles.h" double computeValue(size_t x); // A function to compute a single value std::unique_ptr computeValues(size_t howMany); // A function to compute an array of values int main() { try { auto values{ computeValues(10'000) }; // Cannot leak either: resource is managed by RAII object! } catch (const Trouble&) { std::cout << "No worries: I've caught it!" << std::endl; } } std::unique_ptr computeValues(size_t howMany) { auto values{ std::make_unique(howMany) }; for (size_t i{}; i < howMany; ++i) values[i] = computeValue(i); return values; } double computeValue(size_t x) { if (x < 100) return std::sqrt(x); // Return the square root of the input argument else throw Trouble{ "The trouble with trouble is, it starts out as fun!" }; } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07C/Troubles.h ================================================ // MyTroubles.h Exception classes #ifndef MYTROUBLES_H #define MYTROUBLES_H #include #include class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07D/Ex16_07D.cpp ================================================ // Avoid resource leaks due to exceptions using std::unique_ptr<> // Note: this example is given but not named in the text. // Instead of a custom RAII class DoubleArrayRAII, it uses std::vector<>. // Unlike the former, the latter can of course be returned from computeValues() as well. #include #include #include // For std::sqrt() #include "Troubles.h" double computeValue(size_t x); // A function to compute a single value std::vector computeValues(size_t howMany); // A function to compute an array of values int main() { try { auto values{ computeValues(10'000) }; // Cannot leak either: resource is managed by RAII object! } catch (const Trouble&) { std::cout << "No worries: I've caught it!" << std::endl; } } std::vector computeValues(size_t howMany) { std::vector values; for (size_t i{}; i < howMany; ++i) values.push_back(computeValue(i)); return values; } double computeValue(size_t x) { if (x < 100) return std::sqrt(x); // Return the square root of the input argument else throw Trouble{ "The trouble with trouble is, it starts out as fun!" }; } ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_07D/Troubles.h ================================================ // MyTroubles.h Exception classes #ifndef MYTROUBLES_H #define MYTROUBLES_H #include #include class Trouble { public: explicit Trouble(std::string_view message = "There's a problem") : m_message {message} {} virtual ~Trouble() = default; // Base classes must have a virtual destructor! virtual std::string_view what() const { return m_message; } private: std::string m_message; }; // Derived exception class class MoreTrouble : public Trouble { public: explicit MoreTrouble(std::string_view str = "There's more trouble...") : Trouble {str} {} }; // Derived exception class class BigTrouble : public MoreTrouble { public: explicit BigTrouble(std::string_view str = "Really big trouble...") : MoreTrouble {str} {} }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_09/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::min() function template #include "DimensionError.h" class Box { public: Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } { if (l <= 0.0 || w <= 0.0 || h <= 0.0) throw DimensionError{ std::min({l, w, h}) }; } double volume() const { return m_length * m_width * m_height; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_09/DimensionError.h ================================================ #ifndef DIMENSION_ERROR_H #define DIMENSION_ERROR_H #include // For derived exception classes such as std::out_of_range #include // For std::to_string() and the std::string type class DimensionError : public std::out_of_range { public: explicit DimensionError(double value) : std::out_of_range{ "Zero or negative dimension: " + std::to_string(value) } , m_value{ value } {} // Function to obtain the invalid dimension value double getValue() const noexcept { return m_value; } private: double m_value; }; #endif ================================================ FILE: Examples/NoModules/Chapter 16/Ex16_09/Ex16_09.cpp ================================================ // Using an exception class #include #include "Box.h" // For the Box class #include "DimensionError.h" // For the dimension_error class int main() { try { Box box1 {1.0, 2.0, 3.0}; std::cout << "box1 volume is " << box1.volume() << std::endl; Box box2 {1.0, -2.0, 3.0}; std::cout << "box2 volume is " << box2.volume() << std::endl; } catch (const std::exception& ex) { std::cout << "Exception caught in main(): " << ex.what() << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_01/Array.h ================================================ // This solution uses the const-and-back-again idiom to avoid code duplication // between the non-const and const overloads of the array subscript operators. // It does not yet use the copy-and-swap idiom for the copy assignment operator // template, though: see Ex17_01A. #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For std::to_string() #include // For std::as_const() template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Copy assignment operator T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Copy assignment operator template (not exception safe) template Array& Array::operator=(const Array& rhs) { if (&rhs != this) // If lhs != rhs... { // ...do the assignment... delete[] m_elements; // Release any free store memory m_size = rhs.m_size; // Copy the members of rhs into lhs m_elements = new T[m_size]; for (size_t i {}; i < m_size; ++i) m_elements[i] = rhs.m_elements[i]; } return *this; // ... return lhs } #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_01/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {}; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } private: double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_01/Ex17_01.cpp ================================================ // Using a class template #include "Box.h" #include "Array.h" #include #include int main() { try { const size_t numValues {20}; Array values {numValues}; for (unsigned i {}; i < numValues; ++i) values[i] = i + 1; std::cout << "Sums of pairs of elements:"; size_t lines {}; for (size_t i {numValues - 1}; i >= 0; --i) { std::cout << (lines++ % 5 == 0 ? "\n" : "") << std::format("{:5g}", values[i] + values[i-1]); } } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } try { const size_t numBoxes {5}; Array boxes {numBoxes}; for (size_t i {} ; i <= numBoxes ; ++i) std::cout << "Box volume is " << boxes[i].volume() << std::endl; } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_01A/Array.h ================================================ // This solution uses // 1) the const-and-back-again idiom to avoid code duplication // between the non-const and const overloads of the array subscript operators. // 2) the copy-and-swap idiom for a thread-safe copy assignment operator #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For std::to_string() #include // For std::as_const() template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Copy assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_01A/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {}; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } private: double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_01A/Ex17_01A.cpp ================================================ // Using a class template #include "Box.h" #include "Array.h" #include #include int main() { try { const size_t numValues {20}; Array values {numValues}; for (unsigned i {}; i < numValues; ++i) values[i] = i + 1; std::cout << "Sums of pairs of elements:"; size_t lines {}; for (size_t i {numValues - 1}; i >= 0; --i) { std::cout << (lines++ % 5 == 0 ? "\n" : "") << std::format("{:5g}", values[i] + values[i-1]); } } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } try { const size_t numBoxes {5}; Array boxes {numBoxes}; for (size_t i {} ; i <= numBoxes ; ++i) std::cout << "Box volume is " << boxes[i].volume() << std::endl; } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_02/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For to_string() #include // For std::as_const() template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Assignment operator void swap(Array& other) noexcept; // noexcept swap() function T& operator[](int index); // Subscript operator const T& operator[](int index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements{ new T[size] {} }, m_size{ size } {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](int index) const { if (index < startIndex) throw std::out_of_range{ "Index too small: " + std::to_string(index) }; if (index > startIndex + static_cast(m_size) - 1) throw std::out_of_range{ "Index too large: " + std::to_string(index) }; return m_elements[index - startIndex]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](int index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (can only swap arrays with identical startIndex) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_02/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {}; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } private: double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_02/Ex17_02.cpp ================================================ // Using a class template with a non-type parameter #include "Box.h" #include "Array.h" #include #include #include // For use of typeid() int main() { try { try { const size_t size {21}; // Number of array elements const int start {-10}; // Index for first element const int end {start + static_cast(size) - 1}; // Index for last element Array values {size}; // Define array of double values for (int i {start}; i <= end; ++i) // Initialize the elements values[i] = i - start + 1; std::cout << "Sums of pairs of elements: "; size_t lines {}; for (int i {end}; i >= start; --i) { std::cout << (lines++ % 5 == 0 ? "\n" : "") << std::format("{:5g}", values[i] + values[i-1]); } } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } // Create array of Box objects const int numBoxes {9}; Array boxes { static_cast(numBoxes) }; for (int i { -numBoxes / 2 }; i <= numBoxes/2 + numBoxes%2; ++i) std::cout << std::format("Volume of Box[{}] is {}\n", i, boxes[i].volume()); } catch (const std::exception& ex) { std::cerr << typeid(ex).name() << " exception caught in main()! " << ex.what() << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_02A/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For to_string() #include // For std::as_const() template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Assignment operator void swap(Array& other) noexcept; // noexcept swap() function T& operator[](int index); // Subscript operator const T& operator[](int index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements{ new T[size] {} }, m_size{ size } {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template (improved readability!) template const T& Array::operator[](int index) const { // Subtract startIndex to obtain the actual index into the m_elements array. // If startIndex is 0, conventional 0-based array indexing is used. const int actualIndex{ index - startIndex }; if (actualIndex < 0) throw std::out_of_range {"Index too small: " + std::to_string(index)}; if (actualIndex >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[actualIndex]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](int index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (can only swap arrays with identical startIndex) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_02A/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {}; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } private: double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_02A/Ex17_02A.cpp ================================================ // Using a class template with a non-type parameter // In this variant the code readability of the array subscript operator template // was improved (see Array<> source code). #include "Box.h" #include "Array.h" #include #include #include // For use of typeid() int main() { try { try { const size_t size {21}; // Number of array elements const int start {-10}; // Index for first element const int end {start + static_cast(size) - 1}; // Index for last element Array values {size}; // Define array of double values for (int i {start}; i <= end; ++i) // Initialize the elements values[i] = i - start + 1; std::cout << "Sums of pairs of elements: "; size_t lines {}; for (int i {end}; i >= start; --i) { std::cout << (lines++ % 5 == 0 ? "\n" : "") << std::format("{:5g}", values[i] + values[i-1]); } } catch (const std::out_of_range& ex) { std::cerr << "\nout_of_range exception object caught! " << ex.what() << std::endl; } // Create array of Box objects const int numBoxes {9}; Array boxes { static_cast(numBoxes) }; for (int i { -numBoxes / 2 }; i <= numBoxes/2 + numBoxes%2; ++i) std::cout << std::format("Volume of Box[{}] is {}\n", i, boxes[i].volume()); } catch (const std::exception& ex) { std::cerr << typeid(ex).name() << " exception caught in main()! " << ex.what() << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_03/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include // For the std::initializer_list<> template template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(std::initializer_list elements); // Initializer list constructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Copy assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Initializer list constructor template template Array::Array(std::initializer_list elements) : m_elements{ new T[elements.size()] }, m_size{ elements.size() } { // std::initializer_list<> has no operator[], but can be used in range-based for loop. // The possibility to add variable initializations such as "size_t i {};" // to a range-based for loop is new in C++20. for (size_t i{}; const T & element : elements) m_elements[i++] = element; } // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_03/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: Box() : Box{ 1.0, 1.0, 1.0 } {}; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } private: double m_length, m_width, m_height; }; #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_03/Ex17_03.cpp ================================================ // Illustrating Class Template Argument Deduction (CTAD) // by adding an initializer list constructor to our Array<> template. #include "Box.h" #include "Array.h" #include int main() { // Class Template Argument Deduction (CTAD) in action: Array integers{ 1, 2, 3, 4, 5 }; // Deduced type: Array Array doubles{ 1.0, 2.0, 3.0, 4.0, 5.0 }; // Deduced type: Array values{ numValues }; // Now uses the initializer list constructor! std::cout << "Wrong constructor used, so " << values.getSize() << " != " << numValues << std::endl; std::cout << "Single value contained in Array<> is " << values[0] << std::endl; } // Workaround: do not use uniform initialization (or "near uniform", as is thus more appropriate...) { const size_t numValues{ 50 }; Array values(numValues); // Uses Array(size_t) constructor as before std::cout << "Intended constructor used, so " << values.getSize() << " == " << numValues << std::endl; std::cout << "All values are equal to " << values[numValues / 2] << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_04/Ex17_04.cpp ================================================ // Using a stack defined by nested class templates #include "Stack.h" #include #include int main() { std::string words[]{ "The", "quick", "brown", "fox", "jumps" }; Stack wordStack; // A stack of strings for (const auto& word : words) wordStack.push(word); Stack newStack{ wordStack }; // Create a copy of the stack // Display the words in reverse order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; // Reverse wordStack onto newStack while (!wordStack.isEmpty()) newStack.push(wordStack.pop()); // Display the words in original order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; std::cout << std::endl << "Enter a line of text:" << std::endl; std::string text; std::getline(std::cin, text); // Read a line into the string object Stack characters; // A stack for characters for (size_t i{}; i < text.length(); ++i) characters.push(text[i]); // Push the string characters onto the stack std::cout << std::endl; while (!characters.isEmpty()) std::cout << characters.pop(); // Pop the characters off the stack std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_04/Stack.h ================================================ // Stack.h Templates to define stacks #ifndef STACK_H #define STACK_H #include template class Stack { public: Stack() = default; // Default constructor ~Stack(); // Destructor Stack(const Stack & stack); // Copy constructor Stack& operator=(const Stack & rhs); // Copy assignment operator void swap(Stack & other) noexcept; // noexcept swap() function void push(const T & item); // Push an object onto the stack T pop(); // Pop an object off the stack bool isEmpty() const; // Empty test private: // Nested class class Node { public: Node(const T& item) : m_item{ item } {} // Create a node from an object T m_item; // The object stored in this node Node* m_next{}; // Pointer to next node }; Node* m_head{}; // Points to the top of the stack }; // Copy constructor template Stack::Stack(const Stack& stack) { if (stack.m_head) { m_head = new Node {*stack.m_head}; // Copy the top node of the original Node* oldNode {stack.m_head}; // Points to the top node of the original Node* newNode {m_head}; // Points to the node in the new stack while (oldNode = oldNode->m_next) // If m_next was nullptr, the last node was copied { newNode->m_next = new Node{*oldNode}; // Duplicate it newNode = newNode->m_next; // Move to the node just created } } } // Destructor template Stack::~Stack() { while (m_head) { // While current pointer is not null auto* next{ m_head->m_next }; // Get the pointer to the next node delete m_head; // Delete the current head m_head = next; // Make m_head point to the next node } } // Copy assignment operator template Stack& Stack::operator=(const Stack& rhs) { auto copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Push an object onto the stack template void Stack::push(const T& item) { Node* node{ new Node{item} }; // Create the new node node->m_next = m_head; // Point to the old top node m_head = node; // Make the new node the top } // Pop an object off the stack template T Stack::pop() { if (isEmpty()) // If it's empty pop() is not valid so throw exception throw std::logic_error {"Stack empty"}; auto* next {m_head->m_next}; // Save pointer to the next node T item {m_head->m_item}; // Save the T value to return later delete m_head; // Delete the current head m_head = next; // Make head point to the next node return item; // Return the top object } template bool Stack::isEmpty() const { return m_head == nullptr; } // noexcept swap member function template void Stack::swap(Stack& other) noexcept { std::swap(m_head, other.m_head); } // Conventional noexcept swap non-member function template void swap(Stack& one, Stack& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_04A/Ex17_04A.cpp ================================================ // Using a stack defined by nested class templates // (with improvement suggested in the "A Better Stack" section: see Stack<> source) #include "Stack.h" #include #include int main() { std::string words[]{ "The", "quick", "brown", "fox", "jumps" }; Stack wordStack; // A stack of strings for (const auto& word : words) wordStack.push(word); Stack newStack{ wordStack }; // Create a copy of the stack // Display the words in reverse order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; // Reverse wordStack onto newStack while (!wordStack.isEmpty()) newStack.push(wordStack.pop()); // Display the words in original order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; std::cout << std::endl << "Enter a line of text:" << std::endl; std::string text; std::getline(std::cin, text); // Read a line into the string object Stack characters; // A stack for characters for (size_t i{}; i < text.length(); ++i) characters.push(text[i]); // Push the string characters onto the stack std::cout << std::endl; while (!characters.isEmpty()) std::cout << characters.pop(); // Pop the characters off the stack std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_04A/Stack.h ================================================ // Stack.h Templates to define stacks // (with improvement suggested in the "A Better Stack" section) #ifndef STACK_H #define STACK_H #include template class Stack { public: Stack() = default; // Default constructor ~Stack(); // Destructor Stack(const Stack & stack); // Copy constructor Stack& operator=(const Stack & rhs); // Copy assignment operator void swap(Stack & other) noexcept; // noexcept swap() function void push(const T & item); // Push an object onto the stack T pop(); // Pop an object off the stack bool isEmpty() const; // Empty test private: // Nested class class Node { public: Node(const T& item) : m_item{ item } {} // Create a node from an object T m_item; // The object stored in this node Node* m_next{}; // Pointer to next node }; Node* m_head{}; // Points to the top of the stack }; // Copy constructor template Stack::Stack(const Stack& stack) { if (stack.m_head) { m_head = new Node {*stack.m_head}; // Copy the top node of the original Node* oldNode {stack.m_head}; // Points to the top node of the original Node* newNode {m_head}; // Points to the node in the new stack while (oldNode = oldNode->m_next) // If m_next was nullptr, the last node was copied { newNode->m_next = new Node{*oldNode}; // Duplicate it newNode = newNode->m_next; // Move to the node just created } } } // Destructor template Stack::~Stack() { while (!isEmpty()) pop(); } // Copy assignment operator template Stack& Stack::operator=(const Stack& rhs) { auto copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Push an object onto the stack template void Stack::push(const T& item) { Node* node{ new Node{item} }; // Create the new node node->m_next = m_head; // Point to the old top node m_head = node; // Make the new node the top } // Pop an object off the stack template T Stack::pop() { if (isEmpty()) // If it's empty pop() is not valid so throw exception throw std::logic_error {"Stack empty"}; auto* next {m_head->m_next}; // Save pointer to the next node T item {m_head->m_item}; // Save the T value to return later delete m_head; // Delete the current head m_head = next; // Make head point to the next node return item; // Return the top object } template bool Stack::isEmpty() const { return m_head == nullptr; } // noexcept swap member function template void Stack::swap(Stack& other) noexcept { std::swap(m_head, other.m_head); } // Conventional noexcept swap non-member function template void swap(Stack& one, Stack& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_04B/Ex17_04B.cpp ================================================ // Using a stack defined by nested class templates // (using std::unique_ptr<>: see Stack<> source) // Note: this is a bonus example that is only hinted at in the text (and not explicitly named). // It requires the use of std::move(), seen only in Chapter 18. #include "Stack.h" #include #include int main() { std::string words[]{ "The", "quick", "brown", "fox", "jumps" }; Stack wordStack; // A stack of strings for (const auto& word : words) wordStack.push(word); Stack newStack{ wordStack }; // Create a copy of the stack // Display the words in reverse order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; // Reverse wordStack onto newStack while (!wordStack.isEmpty()) newStack.push(wordStack.pop()); // Display the words in original order while (!newStack.isEmpty()) std::cout << newStack.pop() << ' '; std::cout << std::endl; std::cout << std::endl << "Enter a line of text:" << std::endl; std::string text; std::getline(std::cin, text); // Read a line into the string object Stack characters; // A stack for characters for (size_t i{}; i < text.length(); ++i) characters.push(text[i]); // Push the string characters onto the stack std::cout << std::endl; while (!characters.isEmpty()) std::cout << characters.pop(); // Pop the characters off the stack std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_04B/Stack.h ================================================ // Stack.h Templates to define stacks // (using std::unique_ptr<> instead of raw pointer) #ifndef STACK_H #define STACK_H #include #include // For std::unique_ptr<> /* The required changes are minimal, but they do require std::move() which we only cover in Chapter 18. Other changes include: - no need for the destructor anymore (all memory is managed by smart pointers) - Nodes can no longer be copied, so you need to construct all Nodes with the Node(const T&) constructor */ template class Stack { public: Stack() = default; // Default constructor Stack(const Stack & stack); // Copy constructor Stack& operator=(const Stack & rhs); // Copy assignment operator void swap(Stack & other) noexcept; // noexcept swap() function void push(const T & item); // Push an object onto the stack T pop(); // Pop an object off the stack bool isEmpty() const; // Empty test private: // Nested class class Node { public: Node(const T& item) : m_item{ item } {} // Create a node from an object T m_item; // The object stored in this node std::unique_ptr m_next{}; // Pointer to next node }; std::unique_ptr m_head; // Points to the top of the stack }; // Copy constructor template Stack::Stack(const Stack& stack) { if (stack.m_head) { m_head = std::make_unique(stack.m_head->m_item); // Copy the top node of the original Node* oldNode {stack.m_head.get()}; // Points to the top node of the original Node* newNode {m_head.get()}; // Points to the node in the new stack while (oldNode = oldNode->m_next.get()) // If m_next was nullptr, the last node was copied { newNode->m_next = std::make_unique(oldNode->m_item); // Duplicate it newNode = newNode->m_next.get(); // Move to the node just created } } } // Copy assignment operator template Stack& Stack::operator=(const Stack& rhs) { auto copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Push an object onto the stack template void Stack::push(const T& item) { // See Chapter 18 for std::move() auto node{ std::make_unique(item) }; // Create the new node node->m_next = std::move(m_head); // Point to the old top node m_head = std::move(node); // Make the new node the top } // Pop an object off the stack template T Stack::pop() { if (isEmpty()) // If it's empty pop() is not valid so throw exception throw std::logic_error {"Stack empty"}; // See Chapter 18 for std::move() auto next {std::move(m_head->m_next)}; // Save pointer to the next node T item {m_head->m_item}; // Save the T value to return later m_head.reset(); // Delete the current head m_head = std::move(next); // Make head point to the next node return item; // Return the top object } template bool Stack::isEmpty() const { return m_head == nullptr; } // noexcept swap member function template void Stack::swap(Stack& other) noexcept { std::swap(m_head, other.m_head); } // Conventional noexcept swap non-member function template void swap(Stack& one, Stack& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_05/Ex17_05A.cpp ================================================ // Disambiguating dependant names: this code will not compile. template class Outer { public: class Nested { /* ... */ }; // Or a type alias of form 'using Nested = ...;' // ... }; // Uncomment the typename keyword to turn Outer::Nested into // a dependent type name and fix the compilation template void someFunction() { /*typename*/ Outer::Nested* nested; /* ... */ } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_05/Ex17_05B.cpp ================================================ // Disambiguating dependant names // Note: not all compilers may implement the C++20 rules already, // any may still require additional typename keywords in front of T::Derived template class Outer { public: class Nested { /* ... */ }; // Or a type alias of form 'using Nested = ...;' // ... }; template // T assumed to define nested Base and Derived types / aliases void someOtherFunction() { typename T::Base* b{ new T::Derived{} }; // Or: auto* b{ ... } const typename T::Derived& d{ static_cast(*b) }; // Or: const auto& d{ ... } /* ... */ } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_05/Ex17_05C.cpp ================================================ // Disambiguating dependant names // Note: not all compilers may implement the C++20 rules already, // any may still require additional typename keywords in front of Outer::Nested template class Outer { public: class Nested { /* ... */ }; // Or a type alias of form 'using Nested = ...;' // ... }; template class MyClass { public: Outer::Nested memberFunction(const Outer::Nested& nested); private: T::Nested m_member_variable; }; template T::Nested nonMemberFunction(const typename T::Nested* nested); template Outer::Nested MyClass::memberFunction(const typename Outer::Nested& nested) { return nested; } ================================================ FILE: Examples/NoModules/Chapter 17/Ex17_06/Ex17_06.cpp ================================================ // Disambiguating dependant base class names // (this code does not compile without changes) #include template class Base { public: void baseFun() { /* ... */ } protected: int m_base_var {}; }; template class Derived : public Base { public: void derivedFun(); // Option 3: using declarations (remove to see error messages) // using Base::baseFun; // using Base::m_base_var; }; template void Derived::derivedFun() { // These two lines should give compiler errors. // Uncomment the using declarations in the Derived<> template // to make them work. Alternative solutions are illustrated below. baseFun(); std::cout << m_base_var << std::endl; // Option 1: add this-> this->baseFun(); std::cout << this->m_base_var << std::endl; // Option 2: add Base:: Base::baseFun(); std::cout << Base::m_base_var << std::endl; } int main() { } ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_01/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Copy assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_01/Ex18_01.cpp ================================================ // Copying objects into a vector #include "Array.h" #include #include // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { const size_t numArrays{ 10 }; // Fill 10 Arrays with 1,000 strings each const size_t numStringsPerArray{ 1000 }; std::vector> vectorOfArrays; vectorOfArrays.reserve(numArrays); // Inform the vector<> how many Arrays we'll be adding for (size_t i {}; i < numArrays; ++i) { vectorOfArrays.push_back(buildStringArray(numStringsPerArray)); } } ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_02/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_02/Ex18_02.cpp ================================================ // Moving objects into a vector #include "Array.h" #include #include // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { const size_t numArrays{ 10 }; // Fill 10 Arrays with 1,000 strings each const size_t numStringsPerArray{ 1000 }; std::vector> vectorOfArrays; vectorOfArrays.reserve(numArrays); // Inform the vector<> how many Arrays we'll be adding for (size_t i {}; i < numArrays; ++i) { vectorOfArrays.push_back(buildStringArray(numStringsPerArray)); } } ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_03/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs); // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_03/Ex18_03.cpp ================================================ // Defining and using a move assignment operator #include "Array.h" #include // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array strings { 123 }; strings = buildStringArray(1'000); // Assign an rvalue to strings Array more_strings{ 2'000 }; strings = more_strings; // Assign an lvalue to strings } ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_04/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include template class Array { public: explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs); // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_04/Ex18_04.cpp ================================================ // Using std::move() to force the move assignment of a named variable #include "Array.h" #include // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array strings { 123 }; strings = buildStringArray(1'000); // Assign an rvalue to strings Array more_strings{ 2'000 }; strings = std::move(more_strings); // Move more_strings into strings /* Caution: once moved, an object should not be used anymore! */ // std::cout << more_strings[101] << std::endl; // ??? } ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_05A/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H // Compared to Ex18_04, this variant adds two overloads of push_back(). // The Array<>() default constructor is new as well. #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include template class Array { public: Array(); // Default constructor explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs); // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size void push_back(const T& element); // Add copy of given element to the back of the array void push_back(T&& element); // Move element to the back of the array private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template void Array::push_back(const T& element) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i{}; i < m_size; ++i) // Move all existing elements... newArray[i] = std::move(m_elements[i]); newArray[m_size] = element; // Copy the new one... swap(newArray); // ... and swap! } // push_back() overload for rvalue references template void Array::push_back(T&& element) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i{}; i < m_size; ++i) // Move all existing elements... newArray[i] = std::move(m_elements[i]); newArray[m_size] = std::move(element); // Move the new one... swap(newArray); // ... and swap! } #endif ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_05A/Ex18_05A.cpp ================================================ // Exercising two overloads of push_back(): one for lvalue arguments, and one for rvalue arguments #include "Array.h" #include // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array> array_of_arrays; Array array{ buildStringArray(1'000) }; array_of_arrays.push_back(array); // Push an lvalue array.push_back("One more for good measure"); std::cout << std::endl; array_of_arrays.push_back(std::move(array)); // Push an rvalue } ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_05B/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H // This variant merges the two overloads of push_back() of Ex18_05 into one single member. // The push_back() function accepts a new element by value. #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include template class Array { public: Array(); // Default constructor explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs); // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size void push_back(T element); // Copy or move element to the back of the array private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template void Array::push_back(T element) // Pass by value (copy of lvalue, or moved rvalue!) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i{}; i < m_size; ++i) // Move all existing elements... newArray[i] = std::move(m_elements[i]); newArray[m_size] = std::move(element); // Move the new one (could itself be a copy already)... swap(newArray); // ... and swap! } #endif ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_05B/Ex18_05B.cpp ================================================ // Exercising a single overload of push_back() // that can be used to either to add a copy of, or move, a new value into an Array<>. // The caller decides whether the element is copied or moved. #include "Array.h" #include // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array> array_of_arrays; Array array{ buildStringArray(1'000) }; array_of_arrays.push_back(array); // Push an lvalue array.push_back("One more for good measure"); std::cout << std::endl; array_of_arrays.push_back(std::move(array)); // Push an rvalue } ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_06/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H // Compared to Ex18_05B, this variant adds noexcept specifiers to all move members // and implements a strongly exception safe push_back(). // It uses some mild template meta programming in move_assign_if_noexcept() to accomplish the latter. #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include #include template std::conditional_t, T&&, const T&> move_assign_if_noexcept(T& x) noexcept { return std::move(x); } template class Array { public: Array(); // Default constructor explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array) noexcept; // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs) noexcept; // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size void push_back(T element); // Copy or move element to the back of the array private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) noexcept : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) noexcept { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template void Array::push_back(T element) // Pass by value (copy of lvalue, or moved rvalue!) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i {}; i < m_size; ++i) // Move existing elements (copy if not noexcept)... newArray[i] = move_assign_if_noexcept(m_elements[i]); newArray[m_size] = move_assign_if_noexcept(element); // Move (or copy) the new one... swap(newArray); // ... and swap! } #endif ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_06/Ex18_06.cpp ================================================ // Exercising a single, exception safe overload of push_back() // Remove the noexcept specifiers from Array<> to observe // that copying is then used instead of moving // (moving would be unsafe if an exception occurs). #include "Array.h" #include // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array> array_of_arrays; Array array{ buildStringArray(1'000) }; array_of_arrays.push_back(array); // Push an lvalue array.push_back("One more for good measure"); std::cout << std::endl; array_of_arrays.push_back(std::move(array)); // Push an rvalue } ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_07/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H // Exact same as template as Ex18_05B (that is: without noexcept specifiers!) #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include template class Array { public: Array(); // Default constructor explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array) /*noexcept*/; // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs) /*noexcept*/; // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size void push_back(T element); // Copy or move element to the back of the array private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) /*noexcept*/ : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) /*noexcept*/ { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template void Array::push_back(T element) // Pass by value (copy of lvalue, or moved rvalue!) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i{}; i < m_size; ++i) // Move all existing elements... newArray[i] = std::move(m_elements[i]); newArray[m_size] = std::move(element); // Move the new one (could itself be a copy already)... swap(newArray); // ... and swap! } #endif ================================================ FILE: Examples/NoModules/Chapter 18/Ex18_07/Ex18_07.cpp ================================================ // The effect of not adding noexcept to move members // Uncomment the noexcept specifiers in the Array<> template source // to avoid copying when a std::vector<> grows its dynamic array. #include "Array.h" #include #include // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { std::vector> v; v.push_back(buildStringArray(1'000)); std::cout << std::endl; v.push_back(buildStringArray(2'000)); } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_01/Ex19_01.cpp ================================================ // Exercising pointers to functions #include long sum(long a, long b); // Function prototype long product(long a, long b); // Function prototype int main() { long(*fun_ptr)(long, long) {}; // Pointer to function fun_ptr = product; std::cout << "3 * 5 = " << fun_ptr(3, 5) << std::endl; // Call product() thru fun_ptr fun_ptr = sum; // Reassign pointer to sum() std::cout << "3 * (4+5) + 6 = " // Call sum() thru fun_ptr twice << fun_ptr(product(3, fun_ptr(4, 5)), 6) << std::endl; } // Function to multiply two values long product(long a, long b) { return a * b; } // Function to add two values long sum(long a, long b) { return a + b; } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_02/Ex19_02.cpp ================================================ // Exercising the use of function pointers as callback functions #include #include #include "Optimum.h" // Comparison function prototypes: bool less(const int&, const int&); template bool greater(const T&, const T&); bool longer(const std::string&, const std::string&); int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *findOptimum(numbers, less) << std::endl; std::cout << "Maximum element: " << *findOptimum(numbers, greater) << std::endl; std::vector names{ "Moe", "Larry", "Shemp", "Curly", "Joe", "Curly Joe" }; std::cout << "Alphabetically last name: " << *findOptimum(names, greater) << std::endl; std::cout << "Longest name: " << *findOptimum(names, longer) << std::endl; } bool less(const int& one, const int& other) { return one < other; } template bool greater(const T& one, const T& other) { return one > other; } bool longer(const std::string& one, const std::string& other) { return one.length() > other.length(); } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_02/Optimum.h ================================================ // Optimum.h - a function template to determine the optimum element in a given vector #ifndef OPTIMUM_H #define OPTIMUM_H #include // For std::size_t (required here by some compilers) #include template const T* findOptimum(const std::vector& values, bool (*compare)(const T&, const T&)) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_03/Ex19_03.cpp ================================================ // Exercising the use of a functor as callback functions #include #include "Optimum.h" #include "Less.h" template bool greater(const T& one, const T& other) { return one > other; } int main() { Less less; // Create a 'less than' functor std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *findOptimum(numbers, less) << std::endl; std::cout << "Maximum element: " << *findOptimum(numbers, greater) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_03/Less.h ================================================ // Less.h - A basic class of functor objects #ifndef LESS_H #define LESS_H class Less { public: bool operator()(int a, int b) const { return a < b; } }; #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_03/Optimum.h ================================================ // Optimum.h - a function template to determine the optimum element in a given vector #ifndef OPTIMUM_H #define OPTIMUM_H #include // For std::size_t (required here by some compilers) #include template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_03A/Ex19_03A.cpp ================================================ // Exercising the use of standard functors #include #include #include "Optimum.h" int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *findOptimum(numbers, std::less<>{}) << std::endl; std::cout << "Maximum element: " << *findOptimum(numbers, std::greater<>{}) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_03A/Optimum.h ================================================ // Optimum.h - a function template to determine the optimum element in a given vector #ifndef OPTIMUM_H #define OPTIMUM_H #include // For std::size_t (required here by some compilers) #include template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_04/Ex19_04.cpp ================================================ // Exercising a function object with a member variable #include #include #include "Optimum.h" #include "Nearer.h" int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; int number_to_search_for {}; std::cout << "Please enter a number: "; std::cin >> number_to_search_for; std::cout << std::format("The number nearest to {} is {}", number_to_search_for, *findOptimum(numbers, Nearer{ number_to_search_for })) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_04/Nearer.h ================================================ // A class of function objects that compare two values based on how close they are // to some third value that was provided to the functor at construction time. #ifndef NEARER_H #define NEARER_H #include // For std::abs() class Nearer { public: explicit Nearer(int value) : m_value{ value } {} bool operator()(int x, int y) const { return std::abs(x - m_value) < std::abs(y - m_value); } private: int m_value; }; #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_04/Optimum.h ================================================ // Optimum.h - a function template to determine the optimum element in a given vector #ifndef OPTIMUM_H #define OPTIMUM_H #include // For std::size_t (required here by some compilers) #include template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_05/Ex19_05.cpp ================================================ // Exercising the use of stateless lambda expressions as callback functions #include #include #include #include "Optimum.h" int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *findOptimum(numbers, [](int x, int y) { return x < y; }) << std::endl; std::cout << "Maximum element: " << *findOptimum(numbers, [](int x, int y) { return x > y; }) << std::endl; // Define two anonymous comparison functions for strings: auto alpha{ [](std::string_view x, std::string_view y) { return x > y; } }; auto longer{ [](std::string_view x, std::string_view y) { return x.length() > y.length(); } }; std::vector names{ "Moe", "Larry", "Shemp", "Curly", "Joe", "Curly Joe" }; std::cout << "Alphabetically last name: " << *findOptimum(names, alpha) << std::endl; std::cout << "Longest name: " << *findOptimum(names, longer) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_05/Optimum.h ================================================ // Optimum.h - a function template to determine the optimum element in a given vector #ifndef OPTIMUM_H #define OPTIMUM_H #include // For std::size_t (required here by some compilers) #include template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_05A/Ex19_05A.cpp ================================================ // If possible, using the standard functors instead of lambda expressions // often leads to more compact and elegant code. #include #include #include #include #include "Optimum.h" int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *findOptimum(numbers, std::less<>{}) << std::endl; std::cout << "Maximum element: " << *findOptimum(numbers, std::greater<>{}) << std::endl; // Define two anonymous comparison functions for strings: auto alpha{ std::greater<>{} }; auto longer{ [](std::string_view x, std::string_view y) { return x.length() > y.length(); } }; std::vector names{ "Moe", "Larry", "Shemp", "Curly", "Joe", "Curly Joe" }; std::cout << "Alphabetically last name: " << *findOptimum(names, alpha) << std::endl; std::cout << "Longest name: " << *findOptimum(names, longer) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_05A/Optimum.h ================================================ // Optimum.h - a function template to determine the optimum element in a given vector #ifndef OPTIMUM_H #define OPTIMUM_H #include // For std::size_t (required here by some compilers) #include template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_06/Ex19_06.cpp ================================================ // Using a default capture-by-value clause to access a local variable // from within the body of a lambda expression. #include #include "Optimum.h" int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; int number_to_search_for {}; std::cout << "Please enter a number: "; std::cin >> number_to_search_for; auto nearer { [=](int x, int y) { return std::abs(x - number_to_search_for) < std::abs(y - number_to_search_for); }}; std::cout << "The number nearest to " << number_to_search_for << " is " << *findOptimum(numbers, nearer) << std::endl; //unsigned count{}; //auto counter{ [&](int x, int y) { ++count; return x < y; } }; //findOptimum(numbers, counter); //std::cout << "Number of comparisons: " << count; } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_06/Optimum.h ================================================ // Optimum.h - a function template to determine the optimum element in a given vector #ifndef OPTIMUM_H #define OPTIMUM_H #include // For std::size_t (required here by some compilers) #include template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_07/Ex19_07.cpp ================================================ // Using a lambda expression from inside a member function (see Finder.cpp) #include #include "Optimum.h" #include "Finder.h" int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; int number_to_search_for {}; std::cout << "Please enter a number: "; std::cin >> number_to_search_for; Finder finder; finder.setNumberToSearchFor(number_to_search_for); std::cout << "The number nearest to " << finder.getNumberToSearchFor() << " is " << *finder.findNearest(numbers) << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_07/Finder.cpp ================================================ // Exercising capturing the this pointer #include "Finder.h" #include "Optimum.h" #include // For std::abs() std::optional Finder::findNearest(const std::vector& values) const { if (values.empty()) return std::nullopt; else return *findOptimum(values, [this](double x, double y) { return std::abs(x - m_number_to_search_for) < std::abs(y - m_number_to_search_for); }); } double Finder::getNumberToSearchFor() const { return m_number_to_search_for; } void Finder::setNumberToSearchFor(double value) { m_number_to_search_for = value; } ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_07/Finder.h ================================================ // Finder.h - A small class to illustrate the use of lambda expression in member functions #ifndef FINDER_H #define FINDER_H #include #include class Finder { public: double getNumberToSearchFor() const; void setNumberToSearchFor(double n); std::optional findNearest(const std::vector& values) const; private: double m_number_to_search_for {}; }; #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_07/Optimum.h ================================================ // Optimum.h - a function template to determine the optimum element in a given vector #ifndef OPTIMUM_H #define OPTIMUM_H #include // For std::size_t (required here by some compilers) #include template const T* findOptimum(const std::vector& values, Comparison compare) { if (values.empty()) return nullptr; const T* optimum{ &values[0] }; for (size_t i {1}; i < values.size(); ++i) { if (compare(values[i], *optimum)) optimum = &values[i]; } return optimum; } #endif ================================================ FILE: Examples/NoModules/Chapter 19/Ex19_08/Ex19_08.cpp ================================================ // Using the std::function<> template #include #include #include // for std::abs() // A global less() function bool less(int x, int y) { return x < y; } int main() { int a{ 18 }, b{ 8 }; std::cout << std::boolalpha; // Print true/false rather than 1/0 std::function compare; compare = less; // Store a function pointer into compare std::cout << a << " < " << b << ": " << compare(a, b) << std::endl; compare = std::greater<>{}; // Store a function object into compare std::cout << a << " > " << b << ": " << compare(a, b) << std::endl; int n{ 10 }; // Store a lambda closure into compare compare = [n](int x, int y) { return std::abs(x - n) < std::abs(y - n); }; std::cout << a << " nearer to " << n << " than " << b << ": " << compare(a, b); // Check whether a function<> object is tied to an actual function std::function empty; if (empty) // Or, equivalently: 'if (empty != nullptr)' { std::cout << "Calling a default-constructed std::function<>?" << std::endl; empty(a); } } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_01/Ex20_01.cpp ================================================ // Working with std::deque<> #include #include int main() { std::deque my_deque; // A deque<> allows efficient insertions my_deque.push_back(2); // to both ends of the sequence my_deque.push_back(4); my_deque.push_front(1); my_deque[2] = 3; // A deque<> is a random-access sequence container std::cout << "There are " << my_deque.size() << " elements in my_deque: "; for (int element : my_deque) // A deque<>, like all containers, is a range std::cout << element << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_02/Ex20_02.cpp ================================================ // Working with stacks and queues #include #include #include int main() { std::stack stack; for (int i {}; i < 10; ++i) stack.push(i); std::cout << "The elements coming off the top of the stack: "; while (!stack.empty()) { std::cout << stack.top() << ' '; stack.pop(); // pop() is a void function! } std::cout << std::endl; std::queue queue; for (int i {}; i < 10; ++i) queue.push(i); std::cout << "The elements coming from the front of the queue: "; while (!queue.empty()) { std::cout << queue.front() << ' '; queue.pop(); // pop() is a void function! } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_03/Ex20_03.cpp ================================================ // Working with sets #include #include // For the std::set<> container template void printSet(const std::set& my_set); // Print the contents of a set to std::cout int main() { std::set my_set; // Insert elements 1 through 4 in arbitrary order: my_set.insert(1); my_set.insert(4); my_set.insert(3); my_set.insert(3); // Elements 3 and 1 are added twice my_set.insert(1); my_set.insert(2); printSet(my_set); std::cout << "The element 1 occurs " << my_set.count(1) << " time(s)" << std::endl; my_set.erase(1); // Remove the element 1 once printSet(my_set); my_set.clear(); // Remove all elements printSet(my_set); } void printSet(const std::set& my_set) { std::cout << "There are " << my_set.size() << " elements in my_set: "; for (int element : my_set) // A set, like all containers, is a range std::cout << element << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_04/Ex20_04.cpp ================================================ // Basic use of std::map<> #include #include #include int main() { std::map phone_book; phone_book["Donald"] = 202'456'1111; phone_book["Melania"] = 202'456'1111; phone_book["Francis"] = 39'06'6982; phone_book["Elizabeth"] = 44'020'7930'4832; std::cout << "The pope's number is " << phone_book["Francis"] << std::endl; for (const auto& [name, number] : phone_book) std::cout << name << " can be reached at " << number << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_05/Ex20_05.cpp ================================================ // Working with maps #include #include #include #include #include #include // Type aliases using Words = std::vector; using WordCounts = std::map; // Function prototypes Words extractWords(std::string_view text, std::string_view separators = " ,.!?\"\n"); WordCounts countWords(const Words& words); void showWordCounts(const WordCounts& wordCounts); size_t maxWordLength(const WordCounts& wordCounts); int main() { std::string text; // The string to count words in // Read a string from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); const Words words{ extractWords(text) }; if (words.empty()) { std::cout << "No words in text." << std::endl; return 0; } WordCounts wordCounts = countWords(words); showWordCounts(wordCounts); } Words extractWords(std::string_view text, std::string_view separators) { Words words; size_t start{ text.find_first_not_of(separators) }; // Start 1st word size_t end{}; // Index for the end of a word while (start != std::string_view::npos) { end = text.find_first_of(separators, start + 1); // Find end separator if (end == std::string_view::npos) // End of text? end = text.length(); // Yes, so set to last+1 words.push_back(text.substr(start, end - start)); start = text.find_first_not_of(separators, end + 1); // Find next word } return words; } WordCounts countWords(const Words& words) { WordCounts result; for (auto& word : words) ++result[word]; return result; } size_t maxWordLength(const WordCounts& wordCounts) { size_t max{}; for (const auto& [word, count] : wordCounts) if (count >= 2 && max < word.length()) max = word.length(); return max; } void showWordCounts(const WordCounts& wordCounts) { const size_t field_width{maxWordLength(wordCounts) + 1}; const size_t words_per_line{5}; size_t words_in_line{}; // Number of words in the current line char previous_initial{}; for (const auto& [word, count] : wordCounts) { if (count < 2) continue; // Skip words that appear only once // Output newline when initial letter changes or after 5 per line if ( (previous_initial && word[0] != previous_initial) || words_in_line++ == words_per_line) { words_in_line = 0; std::cout << std::endl; } // Output "word (count)", where word has a dynamic field width std::cout << std::format("{:>{}} ({:2})", word, field_width, count); previous_initial = word[0]; } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_06/Ex20_06.cpp ================================================ // Creating and working with Standard iterators #include #include int main() { std::vector letters{ 'a', 'b', 'c', 'd', 'e' }; auto my_iter{ letters.begin() }; std::cout << *my_iter << std::endl; // a *my_iter = 'x'; std::cout << letters[0] << std::endl; // x ++my_iter; // Move my_iter to the next element std::cout << *my_iter << std::endl; // b my_iter += 2; // Move my_iter two elements further std::cout << *my_iter-- << std::endl; // d std::cout << *my_iter << std::endl; // c (iterator altered using the post-decrement // operator in the previous statement) auto copy{ my_iter }; // Create a copy of my_iter (pointing at c) my_iter += 2; // Move my_iter two elements further std::cout << *copy << std::endl; // c (copy not affected by moving my_iter) std::cout << *my_iter << std::endl; // e std::cout << my_iter - copy << std::endl; // 2 } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_07/Ex20_07.cpp ================================================ // Iterating over the elements of a list<> #include #include int main() { std::cout << "Enter a sequence of positive numbers, terminated by -1: "; std::list numbers; while (true) { signed number{ -1 }; std::cin >> number; if (number == -1) break; numbers.push_back(static_cast(number)); } std::cout << "You entered the following numbers: "; for (auto iter{ numbers.begin() }; iter != numbers.end(); ++iter) { std::cout << *iter << ' '; } std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_08/Box.h ================================================ #ifndef BOX_H #define BOX_H // Class to represent a box class Box { public: Box() = default; Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } {} double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } void setLength(double length) { if (length > 0) m_length = length; } void setWidth(double width) { if (width > 0) m_width = width; } void setHeight(double height) { if (height > 0) m_height = height; } // Function to calculate the volume of a box double volume() const { return m_length * m_width * m_height; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; #endif ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_08/Ex20_08.cpp ================================================ // Altering elements through a mutable iterator #include #include #include "Box.h" // From Ex11_04 int main() { std::vector boxes{ Box{ 1.0, 2.0, 3.0 } }; // A vector containing 1 Box auto iter{ boxes.begin() }; std::cout << iter->volume() << std::endl; // 6 == 1.0 * 2.0 * 3.0 *iter = Box{ 2.0, 3.0, 4.0 }; std::cout << iter->volume() << std::endl; // 24 == 2.0 * 3.0 * 4.0 iter->setHeight(7.0); std::cout << iter->volume() << std::endl; // 42 == 2.0 * 3.0 * 7.0 } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_09/Ex20_09.cpp ================================================ // Inserting in and erasing from sequence containers #include #include void printVector(const std::vector& v); int main() { std::vector numbers{ 2, 4, 5 }; // Deduced type: std::vector numbers.insert(numbers.begin(), 1); // Add single element to the beginning of the sequence printVector(numbers); // 1 2 4 5 numbers.insert(numbers.begin() + numbers.size() / 2, 3); // Add in the middle printVector(numbers); // 1 2 3 4 5 std::vector more_numbers{ 6, 7, 8 }; numbers.insert(numbers.end(), more_numbers.begin(), more_numbers.end()); printVector(numbers); // 1 2 3 4 5 6 7 8 numbers.erase(numbers.end() - 3, numbers.end()); // Erase last 3 elements numbers.erase(numbers.begin() + numbers.size() / 2); // Erase the middle element numbers.erase(numbers.begin()); // Erase the first element printVector(numbers); // 2 4 5 } void printVector(const std::vector& v) { for (auto i : v) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_10/Ex20_10.cpp ================================================ // Removing all elements that satisfy a certain condition // while iterating over a container #include #include #include std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); void removeEvenNumbers(auto& numbers) /* Correct! */ { for (auto iter{ numbers.begin() }; iter != numbers.end(); ) { if (*iter % 2 == 0) iter = numbers.erase(iter); else ++iter; } } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers; for (int i{ 1 }; i <= N; ++i) numbers.push_back(i); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_11/Ex20_11.cpp ================================================ // Your first algorithms: std::min_element() and max_element() #include #include #include #include // For std::abs() int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *std::min_element(begin(numbers), end(numbers)) << std::endl; std::cout << "Maximum element: " << *std::max_element(begin(numbers), end(numbers)) << std::endl; int number_to_search_for {}; std::cout << "Please enter a number: "; std::cin >> number_to_search_for; auto nearer { [=](int x, int y) { return std::abs(x - number_to_search_for) < std::abs(y - number_to_search_for); }}; std::cout << "The number nearest to " << number_to_search_for << " is " << *std::min_element(begin(numbers), end(numbers), nearer) << std::endl; std::cout << "The number furthest from " << number_to_search_for << " is " << *std::max_element(begin(numbers), end(numbers), nearer) << std::endl; /* const auto [nearest, furthest] { std::minmax_element(begin(numbers), end(numbers), nearer) }; std::cout << "The number nearest to " << number_to_search_for << " is " << *nearest << std::endl; std::cout << "The number furthest from " << number_to_search_for << " is " << *furthest << std::endl; */ } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_11A/Ex20_11A.cpp ================================================ // Your first algorithms: std::min_element() and max_element(), // this time using the range-based versions. #include #include #include #include // For std::abs() int main() { std::vector numbers{ 91, 18, 92, 22, 13, 43 }; std::cout << "Minimum element: " << *std::ranges::min_element(numbers) << std::endl; std::cout << "Maximum element: " << *std::ranges::max_element(numbers) << std::endl; int number_to_search_for {}; std::cout << "Please enter a number: "; std::cin >> number_to_search_for; auto nearer { [=](int x, int y) { return std::abs(x - number_to_search_for) < std::abs(y - number_to_search_for); }}; std::cout << "The number nearest to " << number_to_search_for << " is " << *std::ranges::min_element(numbers, nearer) << std::endl; std::cout << "The number furthest from " << number_to_search_for << " is " << *std::ranges::max_element(numbers, nearer) << std::endl; /* const auto [nearest, furthest] { std::ranges::minmax_element(numbers, nearer) }; std::cout << "The number nearest to " << number_to_search_for << " is " << *nearest << std::endl; std::cout << "The number furthest from " << number_to_search_for << " is " << *furthest << std::endl; */ } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_12/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; #endif ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_12/Ex20_12.cpp ================================================ // Finding boxes. #include #include #include #include "Box.h" // From Ex13_03A int main() { std::vector boxes{ Box{1,2,3}, Box{5,2,3}, Box{9,2,1}, Box{3,2,1} }; // Define a lambda functor to print the result of find() or find_if(): auto print_result{ [&boxes] (auto result) { if (result == end(boxes)) std::cout << "No box found." << std::endl; else std::cout << "Found matching box at position " << (result - begin(boxes)) << std::endl; }}; // Find an exact box Box box_to_find{ 3,2,1 }; auto result{ std::find(begin(boxes), end(boxes), box_to_find) }; print_result(result); // Find a box with a volume larger than that of box_to_find const auto required_volume{ box_to_find.volume() }; result = std::find_if(begin(boxes), end(boxes), [required_volume](const Box& box) { return box > required_volume; }); print_result(result); } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_12A/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; #endif ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_12A/Ex20_12A.cpp ================================================ // Finding boxes, this time using range-based algorithms. #include #include #include #include "Box.h" // From Ex13_03A int main() { std::vector boxes{ Box{1,2,3}, Box{5,2,3}, Box{9,2,1}, Box{3,2,1} }; // Define a lambda functor to print the result of find() or find_if(): auto print_result{ [&boxes](auto result) { if (result == end(boxes)) std::cout << "No box found." << std::endl; else std::cout << "Found matching box at position " << (result - begin(boxes)) << std::endl; }}; // Find an exact box Box box_to_find{ 3,2,1 }; auto result{ std::ranges::find(boxes, box_to_find) }; print_result(result); // Find a box with a volume larger than that of box_to_find const auto required_volume{ box_to_find.volume() }; result = std::ranges::find_if(boxes, [required_volume](const Box& box) { return box > required_volume; }); print_result(result); } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_13/Ex20_13.cpp ================================================ // Extracting all odd numbers. #include #include #include #include std::set fillSet_1toN(size_t N); // Fill a set with 1, 2, ..., N void printVector(const std::vector& v); // Print the contents of a vector to std::cout int main() { const size_t num_numbers{20}; const auto numbers{ fillSet_1toN(num_numbers) }; std::vector odd_numbers( numbers.size() ); // Caution: not { numbers.size() } here! auto end_odd_numbers{ std::copy_if(begin(numbers), end(numbers), begin(odd_numbers), [](int n) { return n % 2 == 1; }) }; odd_numbers.erase(end_odd_numbers, end(odd_numbers)); printVector(odd_numbers); } std::set fillSet_1toN(size_t N) // Fill a set with 1, 2, ..., N { std::set numbers; for (int i{ 1 }; i <= N; ++i) numbers.insert(i); return numbers; } void printVector(const std::vector& v) { for (auto i : v) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_13A/Ex20_13A.cpp ================================================ // Extracting all odd numbers using std::back_inserter(). #include #include #include #include #include // For std::back_inserter() std::set fillSet_1toN(size_t N); // Fill a set with 1, 2, ..., N void printVector(const std::vector& v); // Print the contents of a vector to std::cout int main() { const size_t num_numbers{20}; const auto numbers{ fillSet_1toN(num_numbers) }; std::vector odd_numbers; std::copy_if(begin(numbers), end(numbers), back_inserter(odd_numbers), [](int n) { return n % 2 == 1; }); printVector(odd_numbers); } std::set fillSet_1toN(size_t N) // Fill a set with 1, 2, ..., N { std::set numbers; for (int i{ 1 }; i <= N; ++i) numbers.insert(i); return numbers; } void printVector(const std::vector& v) { for (auto i : v) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_13B/Ex20_13B.cpp ================================================ // Extracting all odd numbers using std::back_inserter(). // This time using the range-based version of std::copy_if(). #include #include #include #include #include // For std::back_inserter() std::set fillSet_1toN(size_t N); // Fill a set with 1, 2, ..., N void printVector(const std::vector& v); // Print the contents of a vector to std::cout int main() { const size_t num_numbers{ 20 }; const auto numbers{ fillSet_1toN(num_numbers) }; std::vector odd_numbers; std::ranges::copy_if(numbers, back_inserter(odd_numbers), [](int n) { return n % 2 == 1; }); printVector(odd_numbers); } std::set fillSet_1toN(size_t N) // Fill a set with 1, 2, ..., N { std::set numbers; for (int i{ 1 }; i <= N; ++i) numbers.insert(i); return numbers; } void printVector(const std::vector& v) { for (auto i : v) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_14/Ex20_14.cpp ================================================ // Removing all elements that satisfy a certain condition // usign the remove-erase idiom #include #include #include #include std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); void removeEvenNumbers(std::vector& numbers) { // Use the remove_if() algorithm to remove all even numbers auto first_to_erase{ std::remove_if(begin(numbers), end(numbers), [](int number) { return number % 2 == 0; }) }; // Erase all elements including and beyond first_to_erase numbers.erase(first_to_erase, end(numbers)); } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers; for (int i{ 1 }; i <= N; ++i) numbers.push_back(i); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_14A/Ex20_14A.cpp ================================================ // Removing all elements that satisfy a certain condition usign std::erase_if() #include #include #include std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); void removeEvenNumbers(std::vector& numbers) { std::erase_if(numbers, [](int number) { return number % 2 == 0; }); } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers; for (int i{ 1 }; i <= N; ++i) numbers.push_back(i); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_14B/Ex20_14B.cpp ================================================ // Removing all elements that satisfy a certain condition // usign the remove-erase idiom and the range-based version of std::remove_if. // Unlike the iterator-based version, std::ranges::erase_if() returns a subrange, // and not an iterator. // Note also that in this case std::erase_if() is even more compact (see Ex20_14A). #include #include #include #include std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); void removeEvenNumbers(std::vector& numbers) { // Use the remove_if() algorithm to remove all even numbers auto [first_to_erase, last_to_erase] { std::ranges::remove_if(numbers, [](int number) { return number % 2 == 0; }) }; // Erase all elements including and beyond first_to_erase numbers.erase(first_to_erase, last_to_erase); } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers; for (int i{ 1 }; i <= N; ++i) numbers.push_back(i); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_15/Ex20_15.cpp ================================================ // Sorting strings #include #include #include #include int main() { std::vector names{"Frodo Baggins", "Gandalf the Gray", "Aragon", "Samwise Gamgee", "Peregrin Took", "Meriadoc Brandybuck", "Gimli", "Legolas Greenleaf", "Boromir"}; // Sort the names lexicographically std::sort(begin(names), end(names)); std::cout << "Names sorted lexicographically:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl << std::endl; // Sort the names by length std::sort(begin(names), end(names), [](const auto& left, const auto& right) {return left.length() < right.length(); }); std::cout << "Names sorted by length:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_15A/Ex20_15A.cpp ================================================ // Sorting strings using range-based sort() #include #include #include #include int main() { std::vector names{"Frodo Baggins", "Gandalf the Gray", "Aragon", "Samwise Gamgee", "Peregrin Took", "Meriadoc Brandybuck", "Gimli", "Legolas Greenleaf", "Boromir"}; // Sort the names lexicographically std::ranges::sort(names); std::cout << "Names sorted lexicographically:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl << std::endl; // Sort the names by length std::ranges::sort(names, [](const auto& left, const auto& right) {return left.length() < right.length(); }); std::cout << "Names sorted by length:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_16/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; #endif ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_16/Ex20_16.cpp ================================================ // Using range adaptors #include #include #include #include // For std::back_inserter() #include #include "Box.h" using namespace std::ranges::views; int main() { // Our challenge: given a sequence of boxes, // gather pointers to all boxes larger than required_volume std::vector boxes{ { 1, 2, 3 }, { 4, 5, 6}, { 7, 8, 9 }, { 10, 11, 12 } }; const double required_volume{ 200 }; // Without range-based algorithms and range adaptors (in two steps): std::vector box_pointers; std::transform(begin(boxes), end(boxes), back_inserter(box_pointers), [](Box& box) { return &box; }); std::vector large_boxes; std::copy_if(begin(box_pointers), end(box_pointers), back_inserter(large_boxes), [=](const Box* box) { return *box >= required_volume; }); std::cout << "There are " << large_boxes.size() << " large boxes." << std::endl; // With range-based algorithm copy() and a filter-transform pipeline std::vector large_boxes_ranges_copy; std::ranges::copy( boxes | filter([=](const Box& box) { return box >= required_volume; }) | transform([](Box& box) { return &box; }), back_inserter(large_boxes_ranges_copy) ); // With range-based algorithm copy_if() and transform adaptor std::vector large_boxes_ranges_copy_if; std::ranges::copy_if( /* Transform using adaptor before filtering in copy_if() */ boxes | transform([](Box& box) { return &box; }), // Input view of boxes back_inserter(large_boxes_ranges_copy_if), // Output iterator [=](const Box* box) { return *box >= required_volume; } // Condition for copy_if() ); // With range-based algorithm transform() and a filter adaptor std::vector large_boxes_ranges_transform; std::ranges::transform( /* Filter using adaptor before transforming using algorithm */ boxes | filter([=](const Box& box) { return box >= required_volume; }), back_inserter(large_boxes_ranges_transform), // Output iterator [](Box& box) { return &box; } // Transform functor of transform() ); if (large_boxes_ranges_copy != large_boxes || large_boxes_ranges_copy_if != large_boxes || large_boxes_ranges_transform != large_boxes) { std::cerr << "Oh dear... One of the range-based algorithms gave a different result!" << std::endl; } } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_17/Ex20_17.cpp ================================================ // Range factories and range adaptors #include #include // For views, range factories, and range adaptors using namespace std::ranges::views; bool isEven(int i) { return i % 2 == 0; } int squared(int i) { return i * i; } int main() { for (int i : iota(1, 10)) // Lazily generate range [1,10) std::cout << i << ' '; std::cout << std::endl; for (int i : iota(1, 1000) | filter(isEven) | transform(squared) | drop(2) | take(5) | reverse) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 20/Ex20_18/Ex20_18.cpp ================================================ // Writing through a view #include #include #include // For views, range factories, and range adaptors bool isEven(int i) { return i % 2 == 0; } int main() { std::vector numbers{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; for (int& i : numbers | std::ranges::views::filter(isEven)) i *= i; for (int i : numbers) std::cout << i << ' '; std::cout << std::endl; } ================================================ FILE: Examples/NoModules/Chapter 21/Ex21_01/Ex21_01.cpp ================================================ // Class template instantiation errors #include #include #include #include class MyClass { /* just a dummy class */ }; int main() { std::vector v; MyClass one, other; auto biggest{ std::max(one, other) }; std::set objects; objects.insert(MyClass{}); std::list numbers{ 4, 1, 3, 2 }; std::sort(begin(numbers), end(numbers)); } ================================================ FILE: Examples/NoModules/Chapter 21/Ex21_02/Ex21_02.cpp ================================================ // Asserting that a type models a concept #include // For the std::same_as<> and std::convertible_to<> concepts #include // For std::ranges::range<> concept #include // For the std::remove_cv<> type trait #include #include #include template concept BidirectionalIterator = true; // Feel free to further work out all requirements... template concept RandomAccessIterator = BidirectionalIterator && requires(const Iter i, const Iter j, Iter k, const int n) { { i - n } -> std::same_as; { i + n } -> std::same_as; { n + i } -> std::same_as; { k += n }-> std::same_as; { k -= n }-> std::same_as; { i[n] } -> std::same_as; { i < j } -> std::convertible_to; { i > j } -> std::convertible_to; { i <= j } -> std::convertible_to; { i >= j } -> std::convertible_to; }; template concept NoExceptDestructible = requires (T & value) { { value.~T() } noexcept; }; template concept Character = std::same_as, char> || std::same_as, char8_t> || std::same_as, char16_t> || std::same_as, char32_t> || std::same_as, wchar_t>; template concept String = std::ranges::range && requires(S & s, const S & cs) { typename S::value_type; requires Character; { cs.length() } -> std::integral; { s[0] } -> std::same_as; { cs[0] } -> std::convertible_to; { s.data() } -> std::same_as; { cs.data() } -> std::same_as; // ... }; static_assert(NoExceptDestructible); static_assert(NoExceptDestructible); static_assert(String); static_assert(!String>); static_assert(Character); static_assert(Character); static_assert(RandomAccessIterator::iterator>); static_assert(!RandomAccessIterator::iterator>); static_assert(RandomAccessIterator); int main() { } ================================================ FILE: Examples/NoModules/Chapter 21/Ex21_03/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include template requires std::default_initializable && std::destructible class Array { public: Array(); explicit Array(size_t size); ~Array(); Array(const Array& array) requires std::copyable; Array& operator=(const Array& rhs) requires std::copyable; Array(Array&& array) noexcept requires std::movable; Array& operator=(Array&& rhs) noexcept requires std::movable; void swap(Array& other) noexcept; T& operator[](size_t index); const T& operator[](size_t index) const; size_t getSize() const { return m_size; } void push_back(T element) requires std::movable; private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template requires std::default_initializable && std::destructible Array::Array() : Array{0} {} // Constructor template template requires std::default_initializable && std::destructible Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template requires std::default_initializable && std::destructible Array::Array(const Array& array) requires std::copyable : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template requires std::default_initializable && std::destructible Array::Array(Array&& moved) noexcept requires std::movable : m_size{moved.m_size}, m_elements{moved.m_elements} { moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template requires std::default_initializable && std::destructible Array::~Array() { delete[] m_elements; } // const subscript operator template template requires std::default_initializable && std::destructible const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template requires std::default_initializable && std::destructible T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template requires std::default_initializable && std::destructible Array& Array::operator=(const Array& rhs) requires std::copyable { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template requires std::default_initializable && std::destructible Array& Array::operator=(Array&& rhs) noexcept requires std::movable { if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template requires std::default_initializable && std::destructible void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template requires std::default_initializable && std::destructible void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template requires std::default_initializable && std::destructible void Array::push_back(T element) requires std::movable { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i{}; i < m_size; ++i) // Move all existing elements... newArray[i] = std::move(m_elements[i]); newArray[m_size] = std::move(element); // Move the new one (could itself be a copy already)... swap(newArray); // ... and swap! } #endif ================================================ FILE: Examples/NoModules/Chapter 21/Ex21_03/Ex21_03.cpp ================================================ // Violating constraints of uninstantiated class members #include "Array.h" #include // For std::unique_ptr<> // Assert that Array> is a valid type static_assert(requires { typename Array>; }); int main() { Array> tenSmartPointers(10); Array> target; // target = tenSmartPointers; /* Constraint not satisfied: copyable */ target = std::move(tenSmartPointers); target.push_back(std::make_unique(123)); } ================================================ FILE: Examples/NoModules/Chapter 21/Ex21_04/Ex21_04.cpp ================================================ // Constraint based specialization #include // For the std::equality_comparable<> concept #include // The iterator concepts and the iter_difference_t<>() trait #include #include #include // Precondition: incrementing first eventually leads to last template requires std::equality_comparable auto distanceBetween(Iter first, Iter last) { std::cout << "Distance determined by linear traversal: "; std::iter_difference_t result{}; while (first != last) { ++first; ++result; } return result; } template auto distanceBetween(Iter first, Iter last) { std::cout << "Distance determined in constant time: "; return last - first; } int main() { std::list l{ 'a', 'b', 'c' }; std::vector v{ 1, 2, 3, 4, 5 }; float a[] { 1.2f, 3.4f, 4.5f }; std::cout << distanceBetween(cbegin(l), cend(l)) << std::endl; std::cout << distanceBetween(begin(v), end(v)) << std::endl; std::cout << distanceBetween(a, a + std::size(a)) << std::endl; } ================================================ FILE: Examples/README.md ================================================ # Examples This directory contains all source code for the examples given in [*Beginning C++20*](https://www.apress.com/9781484258835) by Ivor Horton and Peter Van Weert (Apress, 2020). The [Modules](Modules) directory contains all source code as discussed in the book. If your compiler does not support C++20 modules yet (which at the time of writing is likely), you may be better off using the code in [NoModules](NoModules) instead, though. This directory contains equivalents of all examples without the use of modules. Consult the [Workarounds](../Workarounds) directory on how to work around compilation issues with other C++20 language and library features that your compiler may not (fully) [support](https://en.cppreference.com/w/cpp/compiler_support) yet. ================================================ FILE: Exercises/Modules/Chapter 01/Exer1_03.cpp ================================================ // Exercise 1-3. Spot the errors. // The correct version should closely resemble the answer to Exercise 1.1. include // # character missing before include Int main() // Should be int, not Int { std:cout << "Hola Mundo!" << std:endl // A semicolon is missing from the end of this line // cout and endl must be prefixed with std::, not std: ) ================================================ FILE: Exercises/Modules/Chapter 01/Soln1_01.cpp ================================================ // Exercise 1-1 Writing the line "Hello World" to the screen. import ; int main() { std::cout << "Hello World" << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 01/Soln1_02.cpp ================================================ // Exercise 1-2. Write your name and age on successive lines. // There are several possibilities. You can do it in one statement for example. // You could also output '\n' to go to a new line. import ; int main() { std::cout << "Phil McCavity" << std::endl; // Name std::cout << "Age: 88" << std::endl; // and age } ================================================ FILE: Exercises/Modules/Chapter 02/Soln2_01A.cpp ================================================ // Exercise 2-1. Convert inches to feet and inches // The number of inches per foot is constant, // and should not be changed within the program, // so we recognize this by declaring it as a const. // // This solution uses only std::cout. // See alternate solution for a version that uses std::format(). // // Note: we always output "feet" and "inches", // even if it concerns only 1 foot or 1 inch. // In a later chapter you will learn about the conditional // statements and expressions that allow you to refine this. import ; int main() { const int inches_per_foot{ 12 }; // Initialize constant variable std::cout << "This program will convert inches to feet and inches." << std::endl; int inches{}; std::cout << "Please enter a number of inches: "; std::cin >> inches; const int feet{ inches / inches_per_foot }; const int remaining_inches{ inches % inches_per_foot }; std::cout << inches << " inches equals " << feet << " feet and " << remaining_inches << " inches." << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 02/Soln2_01B.cpp ================================================ // Exercise 2-1. Convert inches to feet and inches // The number of inches per foot is constant, // and should not be changed within the program, // so we recognize this by declaring it as a const. // // This solution uses std::format(). // // Note: we always output "feet" and "inches", // even if it concerns only 1 foot or 1 inch. // In a later chapter you will learn about the conditional // statements and expressions that allow you to refine this. import ; import ; int main() { const int inches_per_foot { 12 }; // Initialize constant variable std::cout << "This program will convert inches to feet and inches." << std::endl; int inches {}; std::cout << "Please enter a number of inches: "; std::cin >> inches; const int feet = inches / inches_per_foot; const int remaining_inches = inches % inches_per_foot; std::cout << std::format("{} inches equals {} feet and {} inches.", inches, feet, remaining_inches) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 02/Soln2_02A.cpp ================================================ // Exercise 2-2 Compute the area of a circle // This solution uses only std::cout. // See alternate solution for a version that uses std::format(). import ; import ; int main() { std::cout << "This program will compute the area of a circle." << std::endl; double radius {}; std::cout << "Please enter the radius of the circle: "; std::cin >> radius; const auto area{ std::numbers::pi * radius * radius }; std::cout << "The area of the circle is " << area << " square units." << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 02/Soln2_02B.cpp ================================================ // Exercise 2-2 Compute the area of a circle // This solution uses std::format() // to control the precision used when outputting the result // (we opted for 2 decimals after the decimal points). import ; import ; import ; int main() { std::cout << "This program will compute the area of a circle." << std::endl; double radius {}; std::cout << "Please enter the radius of the circle: "; std::cin >> radius; const auto area{ std::numbers::pi * radius * radius }; std::cout << std::format("The area of the circle is {:.2f} square units.", area) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 02/Soln2_03.cpp ================================================ // Exercise 2-3. Calculating the height of a tree import ; import ; #include int main() { const double inches_per_foot {12.0}; const double pi_degrees {180.0}; double feet {}; double inches {}; std::cout << "Enter the distance from the tree in feet and inches: "; std::cin >> feet >> inches; const double distance {feet + inches / inches_per_foot}; double angle {}; std::cout << "Enter the angle of the top of the tree in degrees: "; std::cin >> angle; // First convert angle to radians // (the trigoniometric functions of operate with radians) angle *= std::numbers::pi / pi_degrees; double eye_height {}; std::cout << "Enter your eye height from the ground in inches: "; std::cin >> eye_height; eye_height /= inches_per_foot; // Convert to feet const double height {eye_height + distance * std::tan(angle)}; // Tree height in feet const unsigned height_feet {static_cast(height)}; const unsigned height_inches {static_cast(std::round(inches_per_foot * (height - height_feet)))}; std::cout << "\nThe height of the tree is " << height_feet << " feet " << height_inches << " inches.\n" << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 02/Soln2_04.cpp ================================================ // Exercise 2-4. Calculating the Body Mass Index. // In the expression for bmi, both h_feet and h_inches will be implicitly converted to double // because the other operand of the sub-expression is double. import ; int main() { const double lbs_per_kg {2.2}; const double inches_per_foot {12.0}; const double meters_per_foot {0.3048}; double w_lbs {}; unsigned int h_feet {}; unsigned int h_inches {}; std::cout << "Enter your weight in pounds: "; std::cin >> w_lbs; std::cout << "Enter you height in feet and inches: "; std::cin >> h_feet >> h_inches; const double w_kg {w_lbs / lbs_per_kg}; const double h_meters { meters_per_foot * (h_feet + h_inches / inches_per_foot) }; const double bmi {w_kg / (h_meters * h_meters)}; std::cout << "Your BMI is " << bmi << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 02/Soln2_05.cpp ================================================ // Exercise 2-5. Output your BMI with one decimal after the decimal point. import ; import ; int main() { const double lbs_per_kg{ 2.2 }; const double inches_per_foot{ 12.0 }; const double meters_per_foot{ 0.3048 }; double w_lbs{}; unsigned int h_feet{}; unsigned int h_inches{}; std::cout << "Enter your weight in pounds: "; std::cin >> w_lbs; std::cout << "Enter you height in feet and inches: "; std::cin >> h_feet >> h_inches; const double w_kg{ w_lbs / lbs_per_kg }; const double h_meters{ meters_per_foot * h_feet + h_inches / inches_per_foot }; const double bmi{ w_kg / (h_meters * h_meters) }; std::cout << std::format("Your BMI is {:.1f}\n", bmi); } ================================================ FILE: Exercises/Modules/Chapter 02/Soln2_06.cpp ================================================ // Exercise 2-6. Format a table. import ; import ; import ; int main() { // Define the format strings for the various rows of the table first const auto format_header { "{:20} {:35} {}\n" }; const auto format_precision5 { "{:20} {:35} {:.5f}...\n" }; const auto format_precision3 { "{:20} {:35} {:.3f}...\n" }; std::cout << std::format(format_header, "Constant", "Description", "Approximation"); std::cout << std::format(format_precision5, "std::numbers::e", "The base of the natural logarithm", std::numbers::e); std::cout << std::format(format_precision5, "std::numbers::pi", "pi", std::numbers::pi); std::cout << std::format(format_precision5, "std::numbers::sqrt2", "Square root of 2", std::numbers::sqrt2); std::cout << std::format(format_precision3, "std::numbers::phi", "The golden ration constant", std::numbers::phi); } ================================================ FILE: Exercises/Modules/Chapter 02/Soln2_07.cpp ================================================ // Exercise 2-7. Fun with formatting. // You could always experiment further with various formatting options here! import ; import ; import ; #include int main() { // Define the format strings for the various rows of the table first const auto format_header { "{:20} {:35} {}\n" }; const auto format_precision5{ "{:20} {:35} {:.5f}...\n" }; const auto format_precision3{ "{:20} {:35} {:.3f}...\n" }; const auto format_extra_row { "{:20} {:35} {:.5E}...\n" }; std::cout << std::format(format_header, "Constant", "Description", "Approximation"); std::cout << std::format(format_precision5, "std::numbers::e", "The base of the natural logarithm", std::numbers::e); std::cout << std::format(format_precision5, "std::numbers::pi", "pi", std::numbers::pi); std::cout << std::format(format_precision5, "std::numbers::sqrt2", "Square root of 2", std::numbers::sqrt2); std::cout << std::format(format_precision3, "std::numbers::phi", "The golden ration constant", std::numbers::phi); std::cout << std::format(format_extra_row, "sin(pi/4)", "... in exponent notation", std::sin(std::numbers::pi / 4)); } ================================================ FILE: Exercises/Modules/Chapter 02/Soln2_08.cpp ================================================ // Exercise 2-8. Finding the largest of two integers without comparing them. import ; int main() { unsigned a {}; unsigned b {}; std::cout << "Enter a positive integer: "; std::cin >> a; std::cout << "Enter another positive integer: "; std::cin >> b; // The trick is that, for integer values x and y, // x / y equals zero if x < y. // So unless a and b are equal, either a/b or b/a is zero, // meaning in (x * (a/b) + y * (b/a)) one of both operands of + equals 0. // Once you have that, it's just a matter of working out the details. const unsigned larger {(a * (a / b) + b * (b / a)) / (a / b + b / a)}; const unsigned smaller {(b * (a / b) + a * (b / a)) / (a / b + b / a)}; std::cout << "The larger integer is " << larger << ".\n" << "The smaller integer is " << smaller << '.' << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 03/Soln3_01.cpp ================================================ // Exercise 3-1. Output an integer and its complements in binary and decimal. // This tests how well you remember string formatting using std::format() // (see Chapter 2 if you forgot some of the formatting options), // as well as two's complement binary encoding and bitwise ~. import ; import ; // See Appendix A (available online) for static_assert(). // You should adjust the field widths of the table if ints have a different size. static_assert(sizeof(int) == 4, "This program assumes 32 bit ints"); int main() { int value {}; std::cout << "Enter any integer: "; std::cin >> value; const auto inverted{ static_cast(~value) }; // Output column headings (0b and 32 bits make for a width of 34) std::cout << std::format(" {:^34} {:^34} {:^34}\n", "value", "~value", "~value + 1"); // Output binary values (cast to unsigned integers first for more realistic output) std::cout << std::format(" {:#034b} {:#034b} {:#034b}\n", value, inverted, inverted + 1); // Output decimal values std::cout << std::format(" {:^34} {:^34} {:^34}\n", value, ~value, ~value + 1); } ================================================ FILE: Exercises/Modules/Chapter 03/Soln3_02.cpp ================================================ // Exercise 3-2. Calculating the number of boxes that can be stored on a shelf, without overhang. // We have to calculate how many boxes we can get into a row, // and how many rows we can have, and then multiply these numbers together. // The 'no overhang' problem is easily handled: casting from double to long // (using static_cast<>()) ensures that the fractional part of the double value // is omitted, and only whole boxes are counted. // By including static_cast<>() in the code, we are effectively telling the // compiler that we know what information will be lost in the cast. import ; int main() { const int inches_per_foot {12}; double shelf_length {}; double shelf_depth {}; int box_size {}; // Prompt the user for both the shelf and box dimensions std::cout << "Enter shelf length (feet): "; std::cin >> shelf_length; std::cout << "Enter shelf depth (feet): "; std::cin >> shelf_depth; std::cout << "Enter length ofthe side of a box (inches): "; std::cin >> box_size; // Calculating the number of whole boxes needed to fill the shelf. long boxes {static_cast((shelf_length * inches_per_foot) / box_size) * static_cast((shelf_depth * inches_per_foot) / box_size)}; // Displaying the number of boxes std::cout << "The number of boxes that can be contained in a single layer is " << boxes << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 03/Soln3_03.cpp ================================================ /********************************************************************************* Exercise 3-3. The output from the code is 2. Here's the important statement again: auto j {(k >> 4) & ~(~0u << 3)}; This question is an exercise in bit manipulation on k. First, (k >> 4) shifts the bits in k 4 places to the right; the bitwise representation of 430 is 110101110, so a 4-bit shift leaves 11010. Next, ~0 is composed of all 1s; shifting that three places to the left and complementing the result will leave 111. Finally, doing a bitwise AND on 11010 and 111 leaves 10 (in binary) or 2 (in decimal) as the result. *********************************************************************************/ import ; int main() { /* Note: try to figure out the solution to this exercise without actually running it */ auto k{ 430u }; auto j{ (k >> 4) & ~(~0u << 3) }; std::cout << j << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 03/Soln3_04.cpp ================================================ // Exercise 3-4. Packing and unpacking characters. // Strictly speaking, whether or not this program works is compiler-dependent. // Although it's extremely unlikely that you'll notice a problem. // Concretely: it'll only work if a single byte is 8 bit (virtually always the case), // and the fundamental type int counts at least 4 bytes (true for most modern systems). import ; import ; int main() { unsigned int packed {}; unsigned char ch {}; std::cout << std::format("{:26}", "Enter a character: "); std::cin >> ch; packed |= ch; std::cout << std::format("{:26}", "Enter a second character: "); std::cin >> ch; packed <<= 8; // Shift left 1 byte packed |= ch; std::cout << std::format("{:26}", "Enter a third character: "); std::cin >> ch; packed <<= 8; // Shift left 1 byte packed |= ch; std::cout << std::format("{:26}", "Enter a fourth character: "); std::cin >> ch; packed <<= 8; // Shift left 1 byte packed |= ch; std::cout << std::format("The word containing 4 packed characters is {:#0x}", packed) << std::endl; std::cout << "Unpacking the characters again gives (low-order byte first): "; // Unpacking packed... // (without the static_cast() conversions the bytes would be written as numbers, // which would be fine as well...) unsigned int mask {0x00000FF}; // Keep low order byte // (could just use 0xFF as well, or, of course, 255) ch = packed & mask; // Low order byte std::cout << std::format("{:4}", static_cast(ch)); ch = (packed >> 8) & mask; // 2nd byte std::cout << std::format("{:4}", static_cast(ch)); ch = (packed >> 16) & mask; // 3rd byte std::cout << std::format("{:4}", static_cast(ch)); ch = (packed >> 24) & mask; // 4th byte std::cout << std::format("{:4}", static_cast(ch)) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 03/Soln3_05.cpp ================================================ // Exercise 3-5. Using an enumeration type for colors. // Of course, you have to research the RGB components for the colors. // Biggest complication is that you cannot apply any binary or other arithmetic operations // on values of a scoped enumaration type. For this, you have to first cast them to an integer. import ; import ; int main() { enum class Color : unsigned { Red = 0xFF0000u, Green = 0x00FF00u, Blue = 0x0000FFu, Yellow = 0xFFFF00u, Purple = 0xFF00FFu, Black = 0x000000u, White = 0xFFFFFFu }; const auto format_string { "The components of {:^6} are: red: {:3}, green: {:3}, blue: {:3}\n" }; const Color yellow{ Color::Yellow }; const Color purple{ Color::Purple }; const Color green { Color::Green }; std::cout << std::format(format_string, "yellow", (static_cast(yellow) & static_cast(Color::Red)) >> 16, (static_cast(yellow) & static_cast(Color::Green)) >> 8, (static_cast(yellow) & static_cast(Color::Blue)) ); std::cout << std::format(format_string, "purple", (static_cast(purple) & static_cast(Color::Red)) >> 16, (static_cast(purple) & static_cast(Color::Green)) >> 8, (static_cast(purple) & static_cast(Color::Blue)) ); std::cout << std::format(format_string, "green", (static_cast(green) & static_cast(Color::Red)) >> 16, (static_cast(green) & static_cast(Color::Green)) >> 8, (static_cast(green) & static_cast(Color::Blue)) ); } ================================================ FILE: Exercises/Modules/Chapter 03/Soln3_06.cpp ================================================ // Exercise 3-6. Swapping integers. import ; int main() { int first {}, second {}; std::cout << "Enter two integers separated by a space: "; std::cin >> first >> second; first ^= second; second ^= first; first ^= second; std::cout << "In reverse order they are " << first << " and " << second << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 04/Soln4_01.cpp ================================================ // Exercise 4-1 Testing whether two integer values are equal. import ; int main() { int value1 {}; int value2 {}; std::cout << "Please input two integers, separated by a space: "; std::cin >> value1 >> value2; std::cout << std::endl; if (value1 == value2) std::cout << "The values you entered are the same (two times " << value1 << ")." << std::endl; else std::cout << "The values you entered are not the same (" << value1 << " != " << value2 << ")." << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 04/Soln4_02.cpp ================================================ // Exercise 4-2 Testing for exact division of one integer by another. // We can use an if statement to check that the input is valid // and we can use another to arrange the input as we need. // Then we use an if-else to generate the appropriate output. import ; int main() { int value1 {}; int value2 {}; std::cout << "Please input two positive integers, separated by a space: "; std::cin >> value1 >> value2; std::cout << std::endl; if (value1 <= 0 || value2 <= 0) // Valid input? { std::cout << "Sorry - positive integers only." << std::endl; return 1; } // Ensure that value1 is not smaller than value2 if (value1 < value2) { const auto temp{ value1 }; // swap if necessary value1 = value2; value2 = temp; } if (value1 % value2 == 0) std::cout << value2 << " divides into " << value1 << " exactly. " << std::endl; else std::cout << value1 << " is not exactly divisible by " << value2 << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 04/Soln4_03.cpp ================================================ // Exercise 4-3 Using nested ifs and a logical && to check the value of a number. import ; int main() { double value {}; std::cout << "Please enter a number between 1 and 100: "; std::cin >> value; std::cout << std::endl; if (value >= 1 && value <= 100) { std::cout << "The number you entered is "; if (value > 50) std::cout << "greater than 50"; else if (value < 50) std::cout << "less than 50" << std::endl; else std::cout << "50" << std::endl; std::cout << '.' << std::endl; } else { std::cout << "The number is not between 1 and 100." << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 04/Soln4_04.cpp ================================================ // Exercise 4-4. // As promised, you're to go look for a person "who is over 21, under 35, female, // has a bachelors or masters degree, is unmarried, and who speaks Hindi or Urdu" import ; #include // For std::tolower() / std::toupper() enum class AcademicDegree { none, associate, bachelor, professional, master, doctor }; int main() { unsigned int age {}; // Initialized to 0 char gender {}; // Initialized to '\0' (see Chapter 5) AcademicDegree degree {}; // Initialized to AcademicDegree::none bool married {}; // Initialized to false bool speaksHindi {}; bool speaksUrdu {}; std::cout << "What is your age, if I may ask? "; std::cin >> age; if (age > 120) { std::cout << "Sure it is, joker. Sadly, commedians don't qualify..." << std::endl; return 1; } std::cout << "What is your gender ([m]ale, [f]emale, or [o]ther)? "; std::cin >> gender; gender = std::tolower(gender); if (gender != 'm' && gender != 'f' && gender != 'o') { std::cout << "That was not one of the options... " "The square brackets were not clear, perhaps? We were worried about that..."; return 1; } std::cout << "What is your highest academic degree?\n" << "Possible values are:\n" << "\tn: no academic degree\n" << "\ta: associate's degree\n" << "\tb: bachelor's dehree\n" << "\tp: professional degree\n" << "\tm: master's degree\n" << "\td: doctorate\n"; char degreeChar {}; std::cin >> degreeChar; switch (std::tolower(degreeChar)) { case 'n': degree = AcademicDegree::none; break; case 'a': degree = AcademicDegree::associate; break; case 'b': degree = AcademicDegree::bachelor; break; case 'p': degree = AcademicDegree::professional; break; case 'm': degree = AcademicDegree::master; break; case 'd': degree = AcademicDegree::doctor; break; default: std::cout << "Given that you cannot correctly enter your degree, shall I just note down 'none'?\n"; std::cout << "On second thought: no, I do not believe you qualify. Goodbye." << std::endl; return 1; } // Now we ask a few yes/no questions, and use some variations on how to decide // whether or not the user's input is valid or not. In real code, one should // probably be consistent rather than using a different style each time... char yes_no {}; std::cout << "Are you married (y or n)? "; std::cin >> yes_no; if (yes_no == 'y' || yes_no == 'Y') married = true; else if (yes_no == 'n' || yes_no == 'N') married = false; else { std::cout << "Incapable of entering your marital status. Surely still single then...?" << std::endl; return 1; } std::cout << "Do you speak Hindi (y or n)? "; std::cin >> yes_no; yes_no = std::toupper(yes_no); if (yes_no == 'Y') speaksHindi = true; else if (yes_no == 'N') speaksHindi = false; else { std::cout << "I'm sorry? I didn't catch that. Please answer in English next time..." << std::endl; return 1; } std::cout << "Do you speak Urdu (y or n)? "; std::cin >> yes_no; switch (std::tolower(yes_no)) { case 'y': speaksUrdu = true; break; case 'n': speaksUrdu = false; break; default: std::cout << "I'm sorry? I didn't catch that. Please answer in English next time..." << std::endl; return 1; } // Determine whether the user is someone "who is over 21, under 35, female, has a bachelors or masters degree, // is unmarried, and who speaks Hindi or Urdu" if ((age > 21 && age < 35) && gender == 'f' && (degree == AcademicDegree::bachelor || degree == AcademicDegree::master) && !married && (speaksHindi || speaksUrdu)) { std::cout << "Congratulations: you are precisely the person we were looking for! Are you willing to work for minimum wage?" << std::endl; } else { std::cout << "Sorry. You don't seem to meet our requirements to the letter. Don't call us, we'll call you...?" << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 04/Soln4_05.cpp ================================================ // Exercise 4-05 // Using the conditional operator to select output. import ; import ; int main() { int mice {}; // Count of all mice int brown {}; // Count of brown mice int white {}; // Count of white mice std::cout << "How many brown mice do you have? "; std::cin >> brown; std::cout << "How many white mice do you have? "; std::cin >> white; if (brown < 0 || white < 0) { std::cout << "One cannot have a negative amount of mice..." << std::endl; return 1; } mice = brown + white; std::cout << std::format("You have {} {} in total.\n", mice, mice == 1 ? "mouse" : "mice"); if (mice == 1) { // Mind the parentheses around the conditional expression! std::cout << "It is a " << (brown? "brown" : "white") << " mouse." << std::endl; } else { // No need for parenthese around the conditional expressions here std::cout << std::format("Of these mice, {} {} brown {}.", brown, brown == 1 ? "is a" : "are", brown == 1 ? "mouse" : "mice" ) << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 04/Soln4_06.cpp ================================================ // Exercise 4-6 Finding the range for an integer. // This is just a question of bolting sufficient conditional operators together // in an expression. import ; using std::cin; using std::cout; using std::endl; int main() { int n {}; std::cout << "Enter an integer: "; std::cin >> n; std::cout << "The value is " << (n <= 20 ? "not greater than 20" : n <= 30 ? "greater than 20 and not greater than 30" : n <= 100? "greater than 30 and not exceeding 100" : "greater than 100") << '.' << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 04/Soln4_07.cpp ================================================ // Exercise 4-7 Outputting the binary code for a letter. /* * Most of the program is fairly simple. * The cctype functions make determining upper or lower case easy. * Finding out if it's a vowel is also easy with a switch. * Only getting the binary code needs a little thought, though. * Each of the masks selects a different bit of the ch variable. * If the bit is '1', the expression will be non-zero, which is converted to Boolean true. * If it's '0', the whole expression will be zero, or Boolean false. * Ones and zeros are therefore output as appropriate. */ import ; #include int main() { char entered_letter {}; std::cout << "Enter a letter: "; std::cin >> entered_letter; if (!std::isalpha(entered_letter)) { std::cout << "That's not a letter!" << std::endl; return 1; } // We'll need the lower case letter... const auto lower_case_letter{ static_cast(std::tolower(entered_letter)) }; // Determine upper or lower case. std::cout << "'" << entered_letter << "' is " << (std::islower(entered_letter) ? "lowercase" : "uppercase") << '.' << std::endl; // Determine whether it is a vowel or a consonant. std::cout << "'" << entered_letter << "' is a "; switch (lower_case_letter) { case 'a': case 'e': case 'i': case 'o': case 'u': std::cout << "vowel"; break; default: std::cout << "consonant"; break; } std::cout << '.' << std::endl; // Output the character code as binary std::cout << "The binary code for '" << lower_case_letter << "' is " << ((lower_case_letter & 0b10000000)? 1 : 0) << ((lower_case_letter & 0b01000000)? 1 : 0) << ((lower_case_letter & 0b00100000)? 1 : 0) << ((lower_case_letter & 0b00010000)? 1 : 0) << ((lower_case_letter & 0b00001000)? 1 : 0) << ((lower_case_letter & 0b00000100)? 1 : 0) << ((lower_case_letter & 0b00000010)? 1 : 0) << ((lower_case_letter & 0b00000001)? 1 : 0) << std::endl; return 0; } ================================================ FILE: Exercises/Modules/Chapter 04/Soln4_08.cpp ================================================ // Exercise 4-8 // Dividing a cash amount into quarters, nickels, dimes and cents. import ; int main() { // Declare the constants (amounts of cents) const unsigned quarter {25}; const unsigned dime {10}; const unsigned nickel {5}; double amountInDollars {0.0}; std::cout << std::endl << "Please enter a cash amount between 0 and 10 dollars: $"; std::cin >> amountInDollars; if (amountInDollars >= 0.0 && amountInDollars <= 10.0) { // Multiply dollar amount by 100 ($1 = 100 cents) // We add 0.5 to compensate for errors in binary floating-point representation auto amountInCents {static_cast(amountInDollars * 100.0 + 0.5)}; // Find the number of quarters const auto quarters {amountInCents / quarter}; amountInCents %= quarter; // Get the remainder // Find the number of dimes const auto dimes {amountInCents / dime}; amountInCents %= dime; // Get the remainder // Find the number of nickels const auto nickels {amountInCents / nickel}; amountInCents %= nickel; // Get the remainder // Find the number of pennies const auto pennies {amountInCents}; // The remainder is already in pennies std::cout << std::endl << "The dollar value $" << amountInDollars << " can be broken down into:" << std::endl << quarters << " quarter" << (quarters == 1? "" : "s") << ',' << std::endl << dimes << " dime" << (dimes == 1? "" : "s") << ',' << std::endl << nickels << " nickel" << (nickels == 1? "" : "s") << ',' << std::endl << pennies << " penn" << (pennies == 1? "y" : "ies") << '.' << std::endl; } else { std::cout << std::endl << "You did not enter a dollar amount between 0 and 10." << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 05/Soln5_01.cpp ================================================ // Exercise 5-1 Squaring odd numbers import ; import ; int main() { int limit {}; std::cout << "Enter the upper limit for squared odd numbers: "; std::cin >> limit; for (int i {1}; i <= limit; i += 2) { std::cout << std::format("{:4} squared is {:8}\n", i, i * i); } } ================================================ FILE: Exercises/Modules/Chapter 05/Soln5_02.cpp ================================================ // Exercise 5-2 Summing integers and calculating the average import ; import ; #include int main() { unsigned int count {}; long long total {}; while (true) { std::cout << "Enter an integer: "; int n; std::cin >> n; total += n; ++count; char yesno {}; std::cout << "Do you want to enter another (y/n)?"; std::cin >> yesno; if (std::tolower(yesno) == 'n') break; } std::cout << std::format("The total is {}. The average is {:.2f}.", total, static_cast(total) / count) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 05/Soln5_03.cpp ================================================ // Exercise 5-3 Using a do-while loop to count characters import ; int main() { unsigned count {}; char ch {}; std::cout << "Please enter a sequence of characters terminated by '#':" << std::endl; // We have to read at least one character so do-while is best do { std::cin >> ch; ++count; } while (ch != '#'); // We do not count '#' as a character, so count must be adjusted --count; std::cout << "You entered " << count << " characters (not counting spaces and the terminal #)." << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 05/Soln5_04.cpp ================================================ // Exercise 5-4 Print out characters entered by the user in reverse order import ; int main() { const size_t max_num_characters {1'000}; char string[max_num_characters]; std::cout << "Please enter a string: "; std::cin.getline(string, max_num_characters); // Count the number of characters size_t count {}; for (; count < max_num_characters && string[count] != '\0'; ++count) {} /* Take care: never write the following: for (size_t i = count - 1; i >= 0; --i) ... Because size_t is unsigned, the loop continuation condition i >= 0 shall always and forever be true. That is: every size_t value is always greater or equal to zero, by definition. Subtracting one from zero wraps around to std::numeric_limits::max(), a huge number. Other solutions besides the one we use below include: // Cast to a signed integer (works even for count == 0!) for (int i{ static_cast(i) - 1 }; i != -1; --i) ... // Subtract in second for expression (less readable, fails if count == 0) for (size_t i{ count }; i-- > 0; ) ... // Use a break statement to end the loop (fails if count == 0) for (size_t i{ count }; ; i--) { ... if (i == 0) break; } */ // Print out the characters in reverse order for (size_t i{ 1 }; i <= count; ++i) { std::cout << string[count - i]; } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 05/Soln5_05.cpp ================================================ // Exercise 5-5 Print out characters entered by the user *after* reversing them import ; int main() { const size_t max_num_characters {1'000}; char string[max_num_characters]; std::cout << "Please enter a string: "; std::cin.getline(string, max_num_characters); // Count the number of characters size_t count {}; while (count < max_num_characters && string[count] != '\0') ++count; // Reverse the characters of the string entered by the user for (size_t i{ 0 }; i < count / 2; ++i) { char temp{ string[i] }; string[i] = string[count - i - 1]; string[count - i - 1] = temp; } // Print out all characters, one by one for (size_t i{ 0 }; i < count; ++i) { std::cout << string[i]; } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 05/Soln5_06.cpp ================================================ // Exercise 5-6. Working with a vector container import ; import ; import ; int main() { std::cout << "What is the largest number I should check? "; unsigned bound {}; std::cin >> bound; std::vector values; // Add element values 1 to bound for (unsigned i {1}; i <= bound; ++i) values.push_back(i); size_t count {}; // Number of output values size_t perline {10}; // Number output perline for (auto value : values) { if (value % 7 == 0 || value % 13 == 0) continue; std::cout << std::format("{:5}", value); if (++count % perline == 0) std::cout << "\n"; } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 05/Soln5_07.cpp ================================================ // Exercise 5-7. Outputting product records & cost // Getting the alignment right is tricky. // You have to adjust the field widths until it looks OK. import ; import ; import ; #include int main() { std::vector product_id; std::vector quantity; std::vector unit_cost; // Read the records while (true) { std::cout << "Enter a record - product number, quantity, unit cost separated by spaces: "; size_t id {}; size_t n {}; double cost {}; std::cin >> id >> n >> cost; product_id.push_back(id); quantity.push_back(n); unit_cost.push_back(cost); std::cout << "Do you want to enter another record (Y or N): "; char answer {}; std::cin >> answer; if (std::toupper(answer) == 'N') break; } // Column headings std::cout << std::format("{:10} {:10} {:10} {:10}\n", "Product", "Quantity", "Unit Price", "Cost"); double total_cost {}; for (size_t i {}; i < product_id.size(); ++i) { const auto cost{ quantity[i] * unit_cost[i] }; std::cout << std::format("{:<10} {:<10} ${:<9.2f} ${:<9.2f}\n", product_id[i], quantity[i], unit_cost[i], cost); total_cost += cost; } // Note the little trick to add empty space... std::cout << std::format("{:33}${:<9.2f}\n", "", total_cost); } ================================================ FILE: Exercises/Modules/Chapter 05/Soln5_08.cpp ================================================ // Exercise 5-08. Generate 93 Fibonacci numbers stored in an array. // Of course 93 was not an arbitrary choice for the number of Fibonacci numbers. // Fibonacci number grow fairly rapidly. // 93 is the most that are possible with type unsigned long long on most platforms. import ; import ; // See Appendix A (available online) for static_assert() static_assert(sizeof(unsigned long long) >= 8, "This program assumes the depth of unsigned long long is (at least) 64 bit."); int main() { const size_t n {93}; std::array fib; fib[0] = fib[1] = 1UL; for (size_t i {2}; i < n; ++i) fib[i] = fib[i - 1] + fib[i - 2]; std::cout << "The first " << n << " Fibonacci numbers are:\n"; for (auto number : fib) { std::cout << number << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 06/Soln6_01.cpp ================================================ // Exercise 6-1. Storing odd numbers in an array and accessing them using pointer notation /* Note that the use of pointer notation is just for the sake of the exercise, * * to help you understand the intimate relation between pointers and array names. * * In real code, you'd normally just use array notation, because it is that much easier. */ import ; import ; int main() { const size_t n {50}; size_t odds[n]; for (size_t i {}; i < n; ++i) odds[i] = i * 2 + 1; const size_t perline {10}; std::cout << "The " << n << " first odd numbers are:\n"; for (size_t i {}; i < n; ++i) { std::cout << std::format("{:5}", *(odds + i)); if ((i + 1) % perline == 0) // Uses the loop counter to decide when a newline is required std::cout << std::endl; } std::cout << "\nIn reverse order these numbers are:\n"; for (int i {n - 1}; i >= 0; --i) // This won't work with size_t for the loop counter { // because size_t cannot be negative std::cout << std::format("{:5}", *(odds + i)); if (i % perline == 0) std::cout << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 06/Soln6_02.cpp ================================================ // Exercise 6-2. Traversing arrays using pointer arithmetics // An exercise to further deepen your understanding of the relation // between pointers, pointer arithmetic, and arrays. import ; import ; int main() { const size_t n {50}; size_t odds[n]; for (size_t i {}; i < n; ++i) odds[i] = i * 2 + 1; const size_t perline {10}; std::cout << "The " << n << " first odd numbers are:\n"; size_t* traversal_pointer{ odds }; for (size_t i {}; i < n; ++i) { std::cout << std::format("{:5}", *traversal_pointer++); if ((i + 1) % perline == 0) // Uses the loop counter to decide when a newline is required std::cout << std::endl; } std::cout << "\nIn reverse order these numbers are:\n"; for (size_t i {}; i < n; ++i) // No need to reverse the manipulation of the loop counter now { std::cout << std::format("{:5}", *(--traversal_pointer)); // Use the pre-decrement operator to make sure the pointer is decremented if ((i + 1) % perline == 0) // before it is dereferenced (at the start of this loop, std::cout << std::endl; // the pointer points one passed the last element of the odds array) } } ================================================ FILE: Exercises/Modules/Chapter 06/Soln6_03.cpp ================================================ // Exercise 6-3. Storing numbers in a dynamic array // Btw: notice anything about the result? // Try increasing numbers of array elements... import ; import ; #include int main() { size_t n {}; std::cout << "Enter the number of array elements: "; std::cin >> n; auto* values{ new double[n] }; for (size_t i {}; i < n; ++i) *(values+i) = 1.0 / ((i + 1)*(i + 1)); double sum {}; for (size_t i {}; i < n; ++i) sum += values[i]; std::cout << std::format("The result is {}", std::sqrt(6.0 * sum)) << std::endl; delete[] values; // Don't forget to free the memory! } ================================================ FILE: Exercises/Modules/Chapter 06/Soln6_04.cpp ================================================ // Exercise 6-4. Storing numbers in a vector /* * The result is an approximate value for pi. * The more values you use, the closer it becomes to pi. * In technical speak, this approximation does not converge to pi very fast though, * which basically means that you'll need quite some values * if you want to approximate pi beyond its first few digits. * * We must dereference values to use it with the subscript operator * because it is not a vector but a pointer to a vector. * * Note that allocating a vector<> like this in the free store is not often done. * Dynamic allocation is mostly reserved for objects of other class types, * in particular polymorphic ones. You'll learn all about this in later chapters! */ import ; import ; import ; #include int main() { size_t n {}; std::cout << "Enter the number of vector elements: "; std::cin >> n; auto* values{ new std::vector(n) }; for (size_t i {}; i < n; ++i) (*values)[i] = 1.0 / ((i + 1)*(i + 1)); double sum {}; for (auto value : *values) sum += value; std::cout << std::format("Result is {}", std::sqrt(6.0*sum)) << std::endl; delete values; // It's not an array this time! } ================================================ FILE: Exercises/Modules/Chapter 06/Soln6_05.cpp ================================================ // Exercise 6-5. Managing a dynamic array using a smart pointer // You can no longer use pointer notation now! // (You could, technically, call get() first and then use pointer notation, // though why make the syntax even more convoluted: just use array notation!) import ; import ; import ; #include int main() { size_t n {}; std::cout << "Enter the number of array elements: "; std::cin >> n; auto values{ std::make_unique(n) }; for (size_t i {}; i < n; ++i) values[i] = 1.0 / ((i + 1)*(i + 1)); double sum {}; for (size_t i {}; i < n; ++i) sum += values[i]; std::cout << std::format("The result is {}", std::sqrt(6.0 * sum)) << std::endl; // No need to deallocate the memory yourself anymore: the smart pointer takes care of that for you! } ================================================ FILE: Exercises/Modules/Chapter 06/Soln6_06.cpp ================================================ // Exercise 6-6. Storing a dynamically allocated vector in a smart pointer import ; import ; import ; import ; #include int main() { size_t n {}; std::cout << "Enter the number of vector elements: "; std::cin >> n; auto values{ std::make_unique>(n) }; for (size_t i {}; i < n; ++i) (*values)[i] = 1.0 / ((i + 1)*(i + 1)); double sum {}; for (auto value : *values) sum += value; std::cout << std::format("Result is {}", std::sqrt(6.0*sum)) << std::endl; // No need to deallocate the memory yourself anymore: the smart pointer takes care of that for you! } ================================================ FILE: Exercises/Modules/Chapter 08/Soln8_01.cpp ================================================ // Exercise 8-1 Reading and validating a date of birth. // As always, there are many ways of doing this! import ; import ; import ; int validate_input(int lower, int upper, const std::string& description); int year(); int month(); int date(int month_value, int year_value); std::string ending(int date_day); int main() { std::cout << "Enter your date of birth." << std::endl; int date_year {year()}; int date_month {month()}; int date_day {date(date_month, date_year)}; std::string months[] {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; std::cout << std::endl; std::cout << std::format("You were born on the {} of {}, {}.", std::to_string(date_day) + ending(date_day), months[date_month - 1], date_year ) << std::endl; } // Reads an integer that is between lower and upper inclusive int validate_input(int lower, int upper, const std::string& description) { int data {}; std::cout << std::format("Please enter {} from {} to {}: ", description, lower, upper); std::cin >> data; while (data < lower || data > upper) { std::cout << "Invalid entry; please re-enter " << description << ": "; std::cin >> data; } return data; } // Reads the year int year() { const int low_year {1870}; // Program only works for folks under 150 years old const int high_year {2020}; // and those already born... return validate_input(low_year, high_year, "a year"); } // Reads the month int month() { const int low_month {1}; const int high_month {12}; return validate_input(low_month, high_month, "a month number"); } // Reads in the date in the given month and year int date(int month_number, int year) { const int date_min {1}; const int feb {2}; // Days in month: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec static const int date_max[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // With the above array declared as static, it will only be created the first // time the function is called. Of course, this doesn't save anything in this // example as we only call it once... // Feb has 29 days in a leap year. A leap year is a year that is divible by 4 // except years that are divisible by 100 but not divisible by 400 if (month_number == feb && year % 4 == 0 && !(year % 100 == 0 && year % 400 != 0)) return validate_input(date_min, 29, "a date"); else return validate_input(date_min, date_max[month_number - 1], "a date"); } // Select the ending of the ordinal day number std::string ending(int date_day) { if (date_day == 1 || date_day == 21 || date_day == 31) return "st"; else if (date_day == 2 || date_day == 22) return "nd"; else if (date_day == 3 || date_day == 23) return "rd"; else return "th"; } ================================================ FILE: Exercises/Modules/Chapter 08/Soln8_02.cpp ================================================ // Exercise 8-2 Reversing the order of a string of characters. /****************************************************************** The reverse() function works with an argument of type string, or a C-style string terminated with '\0'. *******************************************************************/ import ; import ; std::string reverse(std::string str); int main() { std::string sentence; std::cout << "Enter a sequence of characters, then press 'Enter': " << std::endl; getline(std::cin, sentence); std::cout << "\nYour sequence in reverse order is:\n"; std::cout << reverse(sentence) << std::endl; std::cout << "\nHere is a demonstration of reverse() working with a C-style string:\n"; char stuff[] {"abcdefg"}; // C-style string std::cout << "\nThe original string is: \"" << stuff << "\"" << "\nReversed it becomes: \"" << reverse(stuff) << "\"" << std::endl; } // Reverse a string in place // The code here is working with a copy of the argument // so the original is not affected. std::string reverse(std::string str) { const size_t length {str.length()}; for (size_t i {}; i < length / 2; ++i) { char temp = str[i]; str[i] = str[length - i - 1]; str[length - i - 1] = temp; } return str; } ================================================ FILE: Exercises/Modules/Chapter 08/Soln8_03.cpp ================================================ // Exercise 8_3 Checking the number of arguments entered at the command line. import ; int main(int numArguments, char* arguments[]) { switch (numArguments - 1) { case 2: case 3: case 4: for (size_t i {1}; i < numArguments; ++i) std::cout << "Argument " << i << " is " << arguments[i] << std::endl; break; default: std::cout << "You entered the incorrect number of arguments.\n" << "Please enter 2, 3 or 4 arguments. " << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 08/Soln8_04.cpp ================================================ // Exercise 8_4 An overloaded plus() function. import ; import ; int plus(int a, int b); double plus(double x, double y); std::string plus(const std::string& s1, const std::string& s2); int main() { const int n {plus(3, 4)}; std::cout << "plus(3, 4) returns " << n << std::endl; const double d {plus(3.2, 4.2)}; std::cout << "plus(3.2, 4.2) returns " << d << std::endl; const std::string s {plus("he", "llo")}; std::cout << "plus(\"he\", \"llo\") returns " << s << std::endl; const std::string s1 {"aaa"}; const std::string s2 {"bbb"}; const std::string s3 {plus(s1, s2)}; std::cout << "With s1 as " << s1 << " and s2 as " << s2 << std::endl; std::cout << "plus(s1, s2) returns " << s3 << std::endl; /* const auto d {plus(3, 4.2)}; This won't compile because there is more than one overloaded plus() function for the arguments. The compiler will not choose so there must be a unique match with a function signature. */ } // Adding integer values int plus(int a, int b) { return a + b; } // Adding floating-point values double plus(double x, double y) { return x + y; } // Adding strings std::string plus(const std::string& s1, const std::string& s2) { return s1 + s2; } ================================================ FILE: Exercises/Modules/Chapter 08/Soln8_05.cpp ================================================ // Exercise 8_5 Listing prime numbers import ; import ; import ; #include // for std::sqrt() bool isPrime(unsigned number); // Option 1: return vector<> // Remember: starting with C++11, returning a vector is efficient. // In modern C++, this is therefore the recommended approach. std::vector generateNumbers(unsigned to, unsigned from = 1); std::vector filterPrimeNumbers(const std::vector& numbers); /* // Option 2: vector<> output variables (not implemented) // This is the traditional approach. // In modern C++, returning using return value is recommended. void generateNumbers(std::vector& output, unsigned to, unsigned from = 1); void filterPrimeNumbers(std::vector& output, const std::vector& numbers); */ int main() { unsigned user_number{}; std::cout << "Would you be so kind as to enter a number? " << std::endl; std::cin >> user_number; const auto numbers{ generateNumbers(user_number) }; const auto primes{ filterPrimeNumbers(numbers) }; unsigned count{}; for (auto& prime : primes) { std::cout << std::format("{:6}", prime); if (++count % 15 == 0) std::cout << '\n'; } std::cout << std::endl; } /* The sqrt() in the for loop below is not required. The following loop would be equally correct, just a bit slower: for (unsigned i = 2; i < number; ++i) { ... } It is a quite common optimisation though to stop testing at the square root of the number. Think about why this is correct! */ bool isPrime(unsigned number) { // a prime number is a natural number strictly greater than 1... if (number <= 1) return false; // ...and with no positive divisors other than 1 and itself for (unsigned i{ 2 }; i < std::sqrt(number); ++i) { if (number % i == 0) { return false; } } return true; } std::vector generateNumbers(unsigned to, unsigned from) { std::vector result; result.reserve(to - from + 1); for (unsigned i{ from }; i <= to; ++i) result.push_back(i); return result; } std::vector filterPrimeNumbers(const std::vector& numbers) { std::vector result; for (auto number : numbers) { if (isPrime(number)) result.push_back(number); } return result; } ================================================ FILE: Exercises/Modules/Chapter 08/Soln8_06.cpp ================================================ // Exercise 8_6 Computing grade statistics /* This is a bigger excercise, and thus many variations will be valid and correct. The focus here isn't on performance, it's on writing and calling functions. Main thing is that you: (1) got the parameter types right---mostly pass-by-reference-to-const for the input parameters, except for sort() which modifies its argument. (2) made sure that the program does something correctly for empty grade lists, small grade lists (< 5 values), and for grade lists of both odd and even size. Some things worth noticing in our solution: - we overloaded the recursive sort() function by adding a helper function that users can call without having to know what the second and third parameters are, and without having to think about the empty input case. This is common with recursive algorithms: the interface that users use will often not contain the actual recursive function. - we defined NOT_AVAILABLE equal to std::numeric_limits::max() (equivalent to static_cast(-1)) to encode missing values in case there are less than 5 inputs, and used NaN values elsewhere in case the user enters no value at all. Other solutions for the former could've been signed values, or in fact any number larger than 100. In Chapter 9 you will learn about std::optional<> which allows you to handle such "not available" or "undefined" cases more elegantly, though. */ import ; import ; import ; import ; // for std::numeric_limits's max() and quiet_nan() #include // for std::sqrt() and std::isnan() void sort(std::vector& numbers); void getHighest(const std::vector& sortedNumbers, unsigned(&highest)[5]); void getLowest(const std::vector& sortedNumbers, unsigned(&lowest)[5]); double computeAverage(const std::vector& numbers); double computeMedian(const std::vector& numbers); double computeStandardDeviation(const std::vector& numbers); double computeVariance(const std::vector& numbers); void printNumber(const std::string& label, double number); void printNumbers(const std::string& label, const unsigned(&numbers)[5]); const unsigned NOT_AVAILABLE = std::numeric_limits::max(); int main() { std::vector grades; std::cout << "Please enter a number of grades (0-100), terminated by a negative one:" << std::endl; while (true) { int number{}; std::cin >> number; if (number < 0) break; else if (number > 100) std::cout << "Only numbers < 100, please..." << std::endl; else grades.push_back(number); } sort(grades); // Note: thruth be told, this is based on an old solution. // It would be better and/or easier // a) to use std::array<> instead of C-style arrays; and // b) to return the values requested rather than using output parameters // Perhaps you can improve our solution accordingly? unsigned highest[5]{}; unsigned lowest[5]{}; getHighest(grades, highest); getLowest(grades, lowest); printNumbers("Five highest grades", highest); printNumbers("Five lowest grades", lowest); printNumber("The grade average", computeAverage(grades)); printNumber("The median grade", computeMedian(grades)); printNumber("The standard deviation of the grades", computeStandardDeviation(grades)); printNumber("The variance of the grades", computeVariance(grades)); } // Swap numbers at position first with address at position second void swap(std::vector& numbers, size_t first, size_t second) { auto temp{ numbers[first] }; numbers[first] = numbers[second]; numbers[second] = temp; } // Recursive helper function to sort numbers in ascending sequence // Numbers to be sorted are from words[start] to words[end] void sort(std::vector& numbers, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle of the [start, end] range to partition swap(numbers, start, (start + end) / 2); // Swap middle number with start // Check values against chosen word size_t current{ start }; for (size_t i{ start + 1 }; i <= end; i++) { if (numbers[i] < numbers[start]) // Is word less than chosen value? swap(numbers, ++current, i); // Yes, so swap to the left } swap(numbers, start, current); // Swap the chosen value with the last swapped number if (current > start) sort(numbers, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(numbers, current + 1, end); // Sort right subset if exists } // Sort numbers in ascending sequence void sort(std::vector& numbers) { if (!numbers.empty()) sort(numbers, 0, numbers.size() - 1); } void getHighest(const std::vector& sortedNumbers, unsigned(&highest)[5]) { const auto numHighest{ static_cast(std::size(highest)) }; for (int i{}; i < numHighest; ++i) { const int numberIndex{ static_cast(sortedNumbers.size()) - numHighest + i }; if (numberIndex >= 0) highest[i] = sortedNumbers[numberIndex]; else highest[i] = NOT_AVAILABLE; } } void getLowest(const std::vector& sortedNumbers, unsigned(&lowest)[5]) { for (size_t i{}; i < std::size(lowest); ++i) { if (i < sortedNumbers.size()) lowest[i] = sortedNumbers[i]; else lowest[i] = NOT_AVAILABLE; } } double computeAverage(const std::vector& numbers) { if (numbers.empty()) return std::numeric_limits::quiet_NaN(); double average{}; for (auto& number : numbers) average += number; return average / numbers.size(); } double computeMedian(const std::vector& numbers) { if (numbers.empty()) return std::numeric_limits::quiet_NaN(); const auto numNumbers{ numbers.size() }; const auto middleIndex{ numNumbers / 2 }; if (numNumbers % 2) { return numbers[middleIndex]; } else { return (numbers[middleIndex] + numbers[middleIndex - 1]) / 2.0; } } double computeStandardDeviation(const std::vector& numbers) { if (numbers.empty()) return std::numeric_limits::quiet_NaN(); const double average{ computeAverage(numbers) }; double sum{}; for (auto& number : numbers) sum += (number - average) * (number - average); return std::sqrt(sum / numbers.size()); } double computeVariance(const std::vector& numbers) { if (numbers.empty()) return std::numeric_limits::quiet_NaN(); const double standardDeviation{ computeStandardDeviation(numbers) }; return standardDeviation * standardDeviation; } void printNumber(const std::string& label, double number) { std::cout << label << ": "; if (std::isnan(number)) std::cout << "n/a"; else std::cout << number; std::cout << std::endl; } void printNumbers(const std::string& label, const unsigned(&numbers)[5]) { std::cout << label << ": "; for (const auto number : numbers) { if (number != NOT_AVAILABLE) std::cout << number << ' '; } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 08/Soln8_07.cpp ================================================ // Exercise 8-7 Computing Fibinacci numbers recursively. // Main thing to realise is that the recursion needs two base cases. // Key is also to use unsigned values (function would fail for negative numbers) // and not to forget about zero either (using n == 1 and n == 2 as base cases // would mean trouble if n == 0 is passed) import ; unsigned long long fib(size_t n); int main() { size_t num{}; std::cout << "Good day, master. How many Fibonacci numbers shall I compute today?" << std::endl; std::cin >> num; for (size_t i{1}; i <= num; ++i) std::cout << "fib(" << i << ") = " << fib(i) << '\n'; } unsigned long long fib(size_t n) { switch (n) { case 0: return 0; case 1: return 1; default: return fib(n-2) + fib(n-1); } } ================================================ FILE: Exercises/Modules/Chapter 08/Soln8_07A.cpp ================================================ // Exercise 8-7 Computing Fibinacci numbers iteratively. // On most systems (it depends on sizeof(unsigned long long)), // you can correctly compute up to 93 Fibonacci numbers with this program. import ; unsigned long long fib(size_t n); int main() { size_t num{}; std::cout << "Good day, master. How many Fibonacci numbers shall I compute today?" << std::endl; std::cin >> num; for (size_t i{1}; i <= num; ++i) std::cout << "fib(" << i << ") = " << fib(i) << '\n'; } unsigned long long fib(size_t n) { // Initialise fib(i) and fib(i+1) for the first iteration of the loop where i == 0 unsigned long long fib_i{0}; // fib(i) = fib(0) = 0 unsigned long long fib_i_1{1}; // fib(i+1) = fib(1) = 1 for (size_t i{}; i < n; ++i) { auto fib_i_2{ fib_i + fib_i_1 }; // fib(i+2) = fib(i) + fib(i+1) // Get ready for the next iteration (mind the order!): fib_i = fib_i_1; fib_i_1 = fib_i_2; } // At the end of the loop, i was equal to n, so fib(i) == fib(n), which is what we needed return fib_i; } ================================================ FILE: Exercises/Modules/Chapter 08/Soln8_08.cpp ================================================ // Exercise 8_8 More efficient recursive version of function for x to the power n, n positive or negative // Based on Ex8_17.cpp import ; import ; long double power(double x, int n); int main() { for (int i {-3}; i <= 3; ++i) // Calculate powers of 8 from -3 to +3 std::cout << std::setw(10) << power(8.0, i); std::cout << std::endl; } // Recursive function to calculate x to the power n long double power(double x, int n) { if (n == 0) return 1.0; else if (n < 0) return 1.0 / power(x, -n); else if (n % 2) return x * power(x, n - 1); // n is odd // If we make it this far, n > 0 and even const auto y{ power(x, n / 2) }; return y * y; } ================================================ FILE: Exercises/Modules/Chapter 08/Soln8_09.cpp ================================================ // Exercise 8_9 Count the number of multiplications performed by // the divide and conquer power() function of Soln8_08.cpp import ; import ; long double power(double x, int n); int main() { std::cout << power(1.5, 1000) << std::endl; } inline auto mult(long double l, long double r) { static size_t count{}; std::cout << ++count << " multiplications" << std::endl; return l * r; } // Recursive function to calculate x to the power n long double power(double x, int n) { if (n == 0) return 1.0; else if (n < 0) return 1.0 / power(x, -n); else if (n % 2) return mult(x, power(x, n - 1)); // x is odd // If we make it this far, x > 0 and even const auto y{ power(x, n / 2) }; return mult(y, y); } ================================================ FILE: Exercises/Modules/Chapter 09/Soln9_01.cpp ================================================ // Exercise 9-1 Working with std::string_view<> import ; // std::optional<> is defined in the module import ; import ; std::optional find_last( std::string_view string, char to_find, std::optional start_index = std::nullopt); // or: ... start_index = {}); int main() { const auto string{ "Growing old is mandatory; growing up is optional." }; const std::optional found_a{ find_last(string, 'a') }; if (found_a) std::cout << "Found the last a at index " << *found_a << std::endl; const auto found_b{ find_last(string, 'b') }; if (found_b.has_value()) std::cout << "Found the last b at index " << found_b.value() << std::endl; // following line gives an error (cannot convert std::optional to size_t) // const size_t found_c{ find_last(string, 'c') }; const auto found_early_i{ find_last(string, 'i', 10) }; if (found_early_i != std::nullopt) std::cout << "Found an early i at index " << *found_early_i << std::endl; } std::optional find_last(std::string_view string, char to_find, std::optional start_index) { // code below will not work for empty strings if (string.empty()) return std::nullopt; // or: 'return std::optional{};' // or: 'return {};' // determine the starting index for the loop that follows: size_t index{ start_index.value_or(string.size() - 1) }; while (true) // never use while (index >= 0) here, as size_t is always >= 0! { if (string[index] == to_find) return index; if (index == 0) return std::nullopt; --index; } } ================================================ FILE: Exercises/Modules/Chapter 09/Soln9_02.cpp ================================================ // Exercise 9-2 Working with std::string_view<> and std::span<> import ; import ; import ; import ; // The function prototype including defaults for parameters void show_data( std::span data, std::string_view title = "Data Values", size_t width = 10, size_t perLine = 5); // Extra overload to output a single value (. void show_data( int data, std::string_view title = "Data Values", size_t width = 10); int main() { int samples[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; int dataItem{ -99 }; show_data({ &dataItem, 1 }); // Call original function directly dataItem = 13; show_data(dataItem, "Unlucky for some!"); // Use extra overload show_data(samples); show_data(samples, "Samples"); show_data(samples, "Samples", 6); show_data(samples, "Samples", 8, 4); } void show_data( std::span data, std::string_view title, size_t width, size_t perLine) { std::cout << title << std::endl; // Display the title // Output the data values for (size_t i{}; i < data.size(); ++i) { std::cout << std::format("{:{}}", data[i], width); // Display a data item if ((i + 1) % perLine == 0) // Newline after perLine values std::cout << '\n'; } std::cout << std::endl; } void show_data(int data, std::string_view title, size_t width) { show_data({ &data, 1 }, title, width); } ================================================ FILE: Exercises/Modules/Chapter 09/Soln9_03.cpp ================================================ // Exercise 9.3. Using vocabulary types // Your solution should use all types you learned about in Chapter 9! import ; import ; import ; import ; import ; void show_data(std::span data, std::string_view title = "Data Values", size_t width = 10, size_t perLine = 5); std::optional largest(std::span data); std::optional smallest(std::span data); std::span shift_range(std::span data, double delta); std::span scale_range(std::span data, double divisor); std::span normalize_range(std::span data); int main() { double samples[] { 11.0, 23.0, 13.0, 4.0, 57.0, 36.0, 317.0, 88.0, 9.0, 100.0, 121.0, 12.0 }; show_data(samples, "Original Values"); // Output original values normalize_range(samples); // Normalize the values show_data(samples, "Normalized Values", 12); // Output normalized values } // Outputs an array of double values void show_data(std::span data, std::string_view title, size_t width, size_t perLine) { std::cout << title << std::endl; // Display the title // Output the data values for (size_t i {}; i < data.size(); ++i) { // Display a data item (uses a dynamic field width: see Chapter 7) std::cout << std::format("{:{}.6g}", data[i], width); if ((i + 1) % perLine == 0) // Newline after perLine values std::cout << '\n'; } std::cout << std::endl; } std::optional smallest(std::span data) { if (data.empty()) return {}; // There is no smallest in an empty sequence size_t index_min {}; for (size_t i {1}; i < data.size(); ++i) if (data[index_min] > data[i]) index_min = i; return data[index_min]; } std::span shift_range(std::span data, double delta) { for (size_t i {}; i < data.size(); ++i) data[i] += delta; return data; } std::optional largest(std::span data) { if (data.empty()) return {}; // There is no largest in an empty array size_t index_max {}; for (size_t i {1}; i < data.size(); ++i) if (data[index_max] < data[i]) index_max = i; return data[index_max]; } std::span scale_range(std::span data, double divisor) { if (!divisor) return data; // Do nothing for a zero divisor for (size_t i{}; i < data.size(); ++i) data[i] /= divisor; return data; } std::span normalize_range(std::span data) { shift_range(data, -(*smallest(data))); return scale_range(data, *largest(data)); } ================================================ FILE: Exercises/Modules/Chapter 09/Soln9_04.cpp ================================================ // Exercise 9-4. Using std::optional<> import ; import ; import ; import ; import ; import ; // Function prototypes std::optional largest(std::span data); std::optional largest(std::span data); std::optional largest(std::span words); int main() { const double array[]{ 1.5, 44.6, 13.7, 21.2, 6.7 }; const std::vector numbers{ 15, 44, 13, 21, 6, 8, 5, 2 }; const std::vector data{ 3.5, 5.0, 6.0, -1.2, 8.7, 6.4 }; const std::array array_data{ 3.5, 5.0, 6.0, -1.2, 8.7, 6.4 }; // Throwing in an std::array for good measure const std::vector names{ "Charles Dickens", "Emily Bronte", "Jane Austen", "Henry James", "Arthur Miller" }; std::cout << "The largest of array is " << *largest(array) << std::endl; // Crashes if nullopt is returned std::cout << "The largest of numbers is " << largest(numbers).value() << std::endl; // Throws exception (see Chapter 16) for nullopt std::cout << "The largest of data is " << largest(data).value() << std::endl; std::cout << "The largest of array_data is (also) " << *largest(array_data) << std::endl; std::cout << "The largest of names is " << largest(names).value_or("") << std::endl; } // Finds the largest of a span of values std::optional largest(std::span data) { if (data.empty()) return {}; // Or return std::nullopt; double max{ data[0] }; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of int values std::optional largest(std::span data) { if (data.empty()) return {}; // Or return std::nullopt; int max{ data[0] }; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of string objects std::optional largest(std::span words) { if (words.empty()) return {}; // Or return std::nullopt; std::string max_word{ words[0] }; for (const auto& word : words) if (max_word < word) max_word = word; return max_word; } ================================================ FILE: Exercises/Modules/Chapter 09/Soln9_05.cpp ================================================ // Exercise 9-5. Using fixed-size std::span<> import ; import ; double average10(std::span data); // Function prototype int main() { double values[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; // double values[]{ 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(values) << std::endl; } // Function to compute an average double average10(std::span data) { double sum{}; // Accumulate total in here for (double val : data) sum += val; // Sum array elements return sum / data.size(); // Return average } ================================================ FILE: Exercises/Modules/Chapter 09/Soln9_06.cpp ================================================ // Exercise 9-6. Passing a vector to a fixed-size std::span<> import ; import ; import ; double average10(std::span data); // Function prototype int main() { std::vector values { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; // double values[]{ 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(std::span{ values.data(), values.size() }) << std::endl; } // Function to compute an average double average10(std::span data) { double sum{}; // Accumulate total in here for (double val : data) sum += val; // Sum array elements return sum / data.size(); // Return average } ================================================ FILE: Exercises/Modules/Chapter 10/Soln10_01.cpp ================================================ // Exercise 10-1.cpp // Create a function template for my_clamp(), // a template that produces functions to clamp values to a given interval. // Different variations are possible, but we opted for the same as in the Standard Library: // namely one where all three arguments are of the same type, and passed by reference-to-const. import ; import ; import ; /* Caution: the actual std::clamp() function uses a different argument order. Where the exercise suggests: my_clamp(value, low, high) but for the Standard Library function the order is as follows: std::clamp(low, value, high) This solution does as instructed, but keep this in mind if ever using std::clamp()! */ template const T& my_clamp(const T& value, const T& low, const T& high); // Function template prototype int main() { std::cout << "2.0 clamped to interval [1.5, 2.5] is " << my_clamp(2.0, 1.5, 2.5) << std::endl; std::cout << "5.0 clamped to interval [1.5, 2.5] is " << my_clamp(5.0, 1.5, 2.5) << std::endl; int big_int {17011983}, small_int {10}, negative_int {-123}; std::cout << std::format("{} clamped to the interval [{},{}] is {}", negative_int, small_int, big_int, my_clamp(negative_int, small_int, big_int)) << std::endl; // And now for a less useful example... std::string a_string {"A"}, z_string {"Z"}; std::string shakespeare{"It is not in the stars to hold our destiny but in ourselves"}; std::cout << "William Shakespeare's quote clamped to [A-Z] is: " << my_clamp(shakespeare, a_string, z_string) << std::endl; } // Template for functions to clamp a value to a closed interval template const T& my_clamp(const T& value, const T& low, const T& high) { if (value < low) return low; else if (value < high) return value; else return high; } ================================================ FILE: Exercises/Modules/Chapter 10/Soln10_02.cpp ================================================ // Exercise 10-2.cpp // Using auto instead of std::string resulted in a type change for a_string and z_string: // they now both have the type const char[], the type given to string literals. // If you instantiate the original template for this type, it therefore becomes // // const char* larger(const char* a, const char* b) // { // return a > b? a : b; // } // // This function now compares the pointers of both string literals rather than the // string literals themselves. To solve this, you should create a specialisation // for const char* arrays. import ; import ; template // Function template prototype T larger(T a, T b); template<> // Function template specialization prototype const char* larger(const char* a, const char* b); int main() { std::cout << "Larger of 1.5 and 2.5 is " << larger(1.5, 2.5) << std::endl; std::cout << "Larger of 3.5 and 4.5 is " << larger(3.5, 4.5) << std::endl; const int big_int {17011983}, small_int {10}; std::cout << "Larger of " << big_int << " and " << small_int << " is " << larger(big_int, small_int) << std::endl; const auto a_string {"A"}, z_string {"Z"}; // now string literals (type const char[])! std::cout << "Larger of \"" << a_string << "\" and \"" << z_string << "\" is " << '"' << larger(a_string, z_string) << '"' << std::endl; } // Template for functions to return the larger of two values template T larger(T a, T b) { return a > b? a : b; } // Template specialization for returning the larger of two character arrays // (such as string literals). // // Note: instead of std::string_view, you could've used std::string as well, // but that would've involved copying the character arrays. // std::string_view is therefore the preferred C++ type to use here. template<> const char* larger(const char* a, const char* b) { return std::string_view{a} > std::string_view{b}? a : b; } /* // Template specialization for returning the larger of two character arrays // (such as string literals). // Alternative implementation using the C-style function strcmp() // (you'll need to include the header to make this compile). // The strcmp() function returns a value larger than zero if a > b; // zero if both arguments are equal, a negative value if a < b. template<> const char* larger(const char* a, const char* b) { return std::strcmp(a, b) > 0; } */ ================================================ FILE: Exercises/Modules/Chapter 10/Soln10_03.cpp ================================================ // Exercise 10-3.cpp // Defining a function template for adding numbers, // and an overload that works for pointer types. // Extra: also make plus() work with string literals... import ; import ; import ; template T plus(const T& a, const T& b) { return a + b; } // Overload with another template for pointer types template T plus(const T* a, const T* b) { return *a + *b; } // Result cannot be const char*, but has to be a new object. // Function template specialization can thus not be used either // (but that is, as you know, never a good idea anyway!). std::string plus(const char* a, const char* b) { return std::string{ a } + b; } int main() { int n{ plus(3, 4) }; std::cout << "plus(3, 4) returns " << n << std::endl; double d{ plus(3.2, 4.2) }; std::cout << "plus(3.2, 4.2) returns " << d << std::endl; std::string s1{ "aaa" }; std::string s2{ "bbb" }; auto s3{ plus(s1, s2) }; std::cout << "With s1 as " << s1 << " and s2 as " << s2 << std::endl; std::cout << "plus(s1, s2) returns " << s3 << std::endl; // The extra part: std::string s{ plus("he", "llo") }; std::cout << "plus(\"he\", \"llo\") returns " << s << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 10/Soln10_04.cpp ================================================ // Exercise 10-4.cpp // Your very own interpretation of std::size() import ; import ; import ; import ; // Look ma, no sizeof()! template size_t my_size(const T (&array)[N]) { return N; } // Overload with two other templates for std::vector<> and array<> template size_t my_size(const std::vector& vector) { return vector.size(); } template size_t my_size(const std::array& array) { return N; } // or array.size(); /* Instead of the latter two templates, you can create one template that would make my_size accept any argument and call size() on it. This will make it work for std::array<> and std::vector<>, as well as std::string and many other containers: template size_t my_size(const Collection& collection) { return collection.size(); } Potential downside is that this will instantiate for int and double type arguments as well, which may result in somewhat verbose compiler errors. This issue can easily be fixed using a requires clause, though (see Chapter 21). template requires requires (const Collection c) { c.size(); } size_t my_size(const Collection& collection) { return collection.size(); } */ int main() { int array[] {4, 8, 15, 16, 23, 42}; std::cout << "Size of numbers is " << my_size(array) << std::endl; // A string literal is also an array: std::cout << "Size of life lesson is " << my_size("Always wear a smile. One size fits all.") << std::endl; std::vector vector{4, 8, 15, 16, 23, 42}; std::cout << "Size of vector is " << my_size(vector) << std::endl; std::array array_object{4, 8, 15, 16, 23, 42}; std::cout << "Size of array_object is " << my_size(array_object) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 10/Soln10_05.cpp ================================================ // Exercise 10-5.cpp // The easiest way to verify this is using a static variable. // If indeed each function is only instantiated once, // then all calls should share the same static variable... // You should see this being reflected in the program's output // as main() starts by calling the function for doubles twice. import ; import ; import ; template T larger(T a, T b); // Function template prototype int main() { std::cout << "Larger of 1.5 and 2.5 is " << larger(1.5, 2.5) << std::endl; std::cout << "Larger of 3.5 and 4.5 is " << larger(3.5, 4.5) << std::endl; int big_int {17011983}, small_int {10}; std::cout << std::format("Larger of {} and {} is {}\n", big_int, small_int, larger(big_int, small_int)); std::string a_string {"A"}, z_string {"Z"}; std::cout << std::format(R"(Larger of "{}" and "{}" is "{}")", a_string, z_string, larger(a_string, z_string)) << std::endl; } // Template for functions to return the larger of two values template T larger(T a, T b) { static size_t counter{}; std::cout << "This instantation has now been called " << ++counter << " time(s)\n"; return a > b? a : b; } ================================================ FILE: Exercises/Modules/Chapter 10/Soln10_06.cpp ================================================ // Exercise 10-6 A Quicksort function template import ; import ; import ; template void swap(std::vector& data, size_t first, size_t second); template void sort(std::vector& data); template void sort(std::vector& data, size_t start, size_t end); template void show(const std::vector& data, size_t width = 5); int main() { std::vector numbers{ -2, 4, -5, 6, 10, -40, 56, 4, 67, 45 }; show(numbers); sort(numbers); std::cout << "\nSorted integers:\n"; show(numbers); std::cout << "\nCharacters to be sorted:\n"; std::vector letters{ 'C', 'd', 'a', 'z', 't', 'S', 'p', 'm', 'D', 'f' }; show(letters, 2); sort(letters); std::cout << "\nSorted characters:\n"; show(letters, 2); std::cout << "\nFloating-point values to be sorted:\n"; std::vector values{ -2.5, 1.4, -2.55, 6.3, 10.1, -40.5, 56.0, 4.7, 67.3, 45.0 }; show(values, 10); sort(values); std::cout << "\nSorted floaating-point values:\n"; show(values, 10); } template void swap(std::vector& data, size_t first, size_t second) { auto temp{data[first]}; data[first] = data[second]; data[second] = temp; } // Sort data in ascending sequence template void sort(std::vector& data) { if (!data.empty()) sort(data, 0, data.size() - 1); } template void sort(std::vector& data, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle address to partition set swap(data, start, (start + end) / 2); // Swap middle element with start element // Check data against chosen element size_t current {start}; for (size_t i {start + 1}; i <= end; i++) { if (data[i] < data[start]) // Is element less than chosen element? swap(data, ++current, i); // Yes, so swap to the left } swap(data, start, current); // Swap chosen and last swapped elements if (current > start) sort(data, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(data, current + 1, end); // Sort right subset if exists } template void show(const std::vector& data, size_t width) { const size_t data_per_line{ 80 / width - 1}; std::cout << std::format("{:{}}", data[0], width); // Output first element size_t data_in_line {}; // Number of data in current line for (size_t i {1}; i < data.size(); ++i) { if (++data_in_line == data_per_line) { data_in_line = 0; std::cout << std::endl; } std::cout << std::format("{:{}}", data[i], width); // Output an element } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_01/Soln11_01.cpp ================================================ // Move all functions from Ex8_17.cpp (and not Ex8_18.cpp as the text says...) // to a words function, exporting only the functions used by main(). import ; import ; import words; int main() { words::Words the_words; /* Renamed to sidestep name clash with words namespace! */ std::string text; // The string to be sorted const auto separators{" ,.!?\"\n"}; // Word delimiters // Read the string to be processed from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); words::extract_words(the_words, text, separators); if (the_words.empty()) { std::cout << "No words in text." << std::endl; return 0; } words::sort(the_words); // Sort the words words::show_words(the_words); // Output the words } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_01/words.cppm ================================================ export module words; import ; import ; import ; import ; import ; export namespace words { using Words = std::vector>; void sort(Words& words); void extract_words(Words& words, const std::string& text, const std::string& separators); void show_words(const Words& words); } namespace words { size_t max_word_length(const Words& words); } void words::extract_words(Words& words, const std::string& text, const std::string& separators) { size_t start {text.find_first_not_of(separators)}; // Start index of first word while (start != std::string::npos) { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of a word if (end == std::string::npos) // Found a separator? end = text.length(); // Yes, so set to end of text words.push_back(std::make_shared(text.substr(start, end - start))); start = text.find_first_not_of(separators, end + 1); // Find start next word } } /* Additional helpers for word::sort(Words&) */ namespace words { void swap(Words& words, size_t first, size_t second) { auto temp{ words[first] }; words[first] = words[second]; words[second] = temp; } void sort(Words& words, size_t start, size_t end); } // Sort strings in ascending sequence void words::sort(Words& words) { if (!words.empty()) sort(words, 0, words.size() - 1); } void words::sort(Words& words, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle address to partition set swap(words, start, (start + end) / 2); // Swap middle address with start // Check words against chosen word size_t current {start}; for (size_t i {start + 1}; i <= end; i++) { if (*words[i] < *words[start]) // Is word less than chosen word? swap(words, ++current, i); // Yes, so swap to the left } swap(words, start, current); // Swap chosen and last swapped words if (current > start) sort(words, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(words, current + 1, end); // Sort right subset if exists } size_t words::max_word_length(const Words& words) { size_t max {}; for (auto& pword : words) if (max < pword->length()) max = pword->length(); return max; } void words::show_words(const Words& words) { const size_t field_width {max_word_length(words) + 1}; const size_t words_per_line {8}; std::cout << std::format("{:{}}", *words[0], field_width); // Output first word size_t words_in_line {}; // Number of words in current line for (size_t i {1}; i < words.size(); ++i) { // Output newline when initial letter changes or after 8 per line if ((*words[i])[0] != (*words[i - 1])[0] || ++words_in_line == words_per_line) { words_in_line = 0; std::cout << std::endl; } std::cout << std::format("{:{}}", *words[i], field_width); // Output a word } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_02/Soln11_02.cpp ================================================ // Moving the implementation of functions from the module interface file // to a module implementation file. import ; import ; import words; int main() { words::Words the_words; std::string text; // The string to be sorted const auto separators{" ,.!?\"\n"}; // Word delimiters // Read the string to be processed from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); words::extract_words(the_words, text, separators); if (the_words.empty()) { std::cout << "No words in text." << std::endl; return 0; } words::sort(the_words); // Sort the words words::show_words(the_words); // Output the words } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_02/words.cpp ================================================ module words; import ; import ; size_t max_word_length(const words::Words& words); void words::extract_words(Words& words, const std::string& text, const std::string& separators) { size_t start {text.find_first_not_of(separators)}; // Start index of first word while (start != std::string::npos) { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of a word if (end == std::string::npos) // Found a separator? end = text.length(); // Yes, so set to end of text words.push_back(std::make_shared(text.substr(start, end - start))); start = text.find_first_not_of(separators, end + 1); // Find start next word } } /* Additional helpers for word::sort(Words&) */ void swap(words::Words& words, size_t first, size_t second) { auto temp{ words[first] }; words[first] = words[second]; words[second] = temp; } void sort(words::Words& words, size_t start, size_t end); // Sort strings in ascending sequence void words::sort(Words& words) { if (!words.empty()) ::sort(words, 0, words.size() - 1); } void sort(words::Words& words, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle address to partition set swap(words, start, (start + end) / 2); // Swap middle address with start // Check words against chosen word size_t current {start}; for (size_t i {start + 1}; i <= end; i++) { if (*words[i] < *words[start]) // Is word less than chosen word? swap(words, ++current, i); // Yes, so swap to the left } swap(words, start, current); // Swap chosen and last swapped words if (current > start) sort(words, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(words, current + 1, end); // Sort right subset if exists } size_t max_word_length(const words::Words& words) { size_t max {}; for (auto& pword : words) if (max < pword->length()) max = pword->length(); return max; } void words::show_words(const Words& words) { const size_t field_width {max_word_length(words) + 1}; const size_t words_per_line {8}; std::cout << std::format("{:{}}", *words[0], field_width); // Output first word size_t words_in_line {}; // Number of words in current line for (size_t i {1}; i < words.size(); ++i) { // Output newline when initial letter changes or after 8 per line if ((*words[i])[0] != (*words[i - 1])[0] || ++words_in_line == words_per_line) { words_in_line = 0; std::cout << std::endl; } std::cout << std::format("{:{}}", *words[i], field_width); // Output a word } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_02/words.cppm ================================================ export module words; import ; import ; import ; export namespace words { using Words = std::vector>; void sort(Words& words); void extract_words(Words& words, const std::string& text, const std::string& separators); void show_words(const Words& words); } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_03/Soln11_03.cpp ================================================ // Splitting a module in multiple submodules import words; import ; import ; int main() { words::Words the_words; std::string text; // The string to be sorted const auto separators{" ,.!?\"\n"}; // Word delimiters // Read the string to be processed from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); words::utils::extract_words(the_words, text, separators); if (the_words.empty()) { std::cout << "No words in text." << std::endl; return 0; } words::sorting::sort(the_words); // Sort the words words::utils::show_words(the_words); // Output the words } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_03/words.cppm ================================================ export module words; export import words.sorting; export import words.utils; ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_03/words.sorting.cpp ================================================ module words.sorting; /* Additional helpers for word::sort(Words&) */ void swap(words::Words& words, size_t first, size_t second) { auto temp{ words[first] }; words[first] = words[second]; words[second] = temp; } void sort(words::Words& words, size_t start, size_t end); // Sort strings in ascending sequence void words::sorting::sort(Words& words) { if (!words.empty()) ::sort(words, 0, words.size() - 1); } void sort(words::Words& words, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle address to partition set swap(words, start, (start + end) / 2); // Swap middle address with start // Check words against chosen word size_t current {start}; for (size_t i {start + 1}; i <= end; i++) { if (*words[i] < *words[start]) // Is word less than chosen word? swap(words, ++current, i); // Yes, so swap to the left } swap(words, start, current); // Swap chosen and last swapped words if (current > start) sort(words, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(words, current + 1, end); // Sort right subset if exists } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_03/words.sorting.cppm ================================================ export module words.sorting; import ; import ; import ; export namespace words { using Words = std::vector>; namespace sorting { void sort(Words& words); } } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_03/words.utils.cpp ================================================ module words.utils; import ; import ; size_t max_word_length(const words::Words& words) { size_t max{}; for (auto& pword : words) if (max < pword->length()) max = pword->length(); return max; } void words::utils::extract_words(Words& words, const std::string& text, const std::string& separators) { size_t start {text.find_first_not_of(separators)}; // Start index of first word while (start != std::string::npos) { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of a word if (end == std::string::npos) // Found a separator? end = text.length(); // Yes, so set to end of text words.push_back(std::make_shared(text.substr(start, end - start))); start = text.find_first_not_of(separators, end + 1); // Find start next word } } void words::utils::show_words(const Words& words) { const size_t field_width {max_word_length(words) + 1}; const size_t words_per_line {8}; std::cout << std::format("{:{}}", *words[0], field_width); // Output first word size_t words_in_line {}; // Number of words in current line for (size_t i {1}; i < words.size(); ++i) { // Output newline when initial letter changes or after 8 per line if ((*words[i])[0] != (*words[i - 1])[0] || ++words_in_line == words_per_line) { words_in_line = 0; std::cout << std::endl; } std::cout << std::format("{:{}}", *words[i], field_width); // Output a word } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_03/words.utils.cppm ================================================ export module words.utils; import ; import ; import ; namespace words { export using Words = std::vector>; } export namespace words::utils { void extract_words(Words& words, const std::string& text, const std::string& separators); void show_words(const Words& words); } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_04/Soln11_04.cpp ================================================ // Creating a module implementation partition import ; import ; import words; int main() { words::Words the_words; std::string text; // The string to be sorted const auto separators{" ,.!?\"\n"}; // Word delimiters // Read the string to be processed from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); words::extract_words(the_words, text, separators); if (the_words.empty()) { std::cout << "No words in text." << std::endl; return 0; } words::sort(the_words); // Sort the words words::show_words(the_words); // Output the words } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_04/internals.cpp ================================================ module words:internals; import words; // For use of words::Words void swap(words::Words& words, size_t first, size_t second) { auto temp{ words[first] }; words[first] = words[second]; words[second] = temp; } size_t max_word_length(const words::Words& words) { size_t max {}; for (auto& pword : words) if (max < pword->length()) max = pword->length(); return max; } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_04/words.cpp ================================================ module words; import ; import ; import :internals; void words::extract_words(Words& words, const std::string& text, const std::string& separators) { size_t start {text.find_first_not_of(separators)}; // Start index of first word while (start != std::string::npos) { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of a word if (end == std::string::npos) // Found a separator? end = text.length(); // Yes, so set to end of text words.push_back(std::make_shared(text.substr(start, end - start))); start = text.find_first_not_of(separators, end + 1); // Find start next word } } /* Additional helper for word::sort(Words&) */ void sort(words::Words& words, size_t start, size_t end); // Sort strings in ascending sequence void words::sort(Words& words) { if (!words.empty()) ::sort(words, 0, words.size() - 1); } void sort(words::Words& words, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle address to partition set swap(words, start, (start + end) / 2); // Swap middle address with start // Check words against chosen word size_t current {start}; for (size_t i {start + 1}; i <= end; i++) { if (*words[i] < *words[start]) // Is word less than chosen word? swap(words, ++current, i); // Yes, so swap to the left } swap(words, start, current); // Swap chosen and last swapped words if (current > start) sort(words, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(words, current + 1, end); // Sort right subset if exists } void words::show_words(const Words& words) { const size_t field_width {max_word_length(words) + 1}; const size_t words_per_line {8}; std::cout << std::format("{:{}}", *words[0], field_width); // Output first word size_t words_in_line {}; // Number of words in current line for (size_t i {1}; i < words.size(); ++i) { // Output newline when initial letter changes or after 8 per line if ((*words[i])[0] != (*words[i - 1])[0] || ++words_in_line == words_per_line) { words_in_line = 0; std::cout << std::endl; } std::cout << std::format("{:{}}", *words[i], field_width); // Output a word } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_04/words.cppm ================================================ export module words; import ; import ; import ; export namespace words { using Words = std::vector>; void sort(Words& words); void extract_words(Words& words, const std::string& text, const std::string& separators); void show_words(const Words& words); } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_05/Soln11_05.cpp ================================================ // Splitting a module interface into partitions import ; import ; import words; int main() { words::Words the_words; /* Renamed to sidestep name clash with words namespace! */ std::string text; // The string to be sorted const auto separators{" ,.!?\"\n"}; // Word delimiters // Read the string to be processed from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); words::extract_words(the_words, text, separators); if (the_words.empty()) { std::cout << "No words in text." << std::endl; return 0; } words::sort(the_words); // Sort the words words::show_words(the_words); // Output the words } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_05/alias.cppm ================================================ export module words:alias; import ; import ; import ; namespace words { export using Words = std::vector>; } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_05/internals.cpp ================================================ module words:internals; import :alias; // For use of words::Words void swap(words::Words& words, size_t first, size_t second) { auto temp{ words[first] }; words[first] = words[second]; words[second] = temp; } size_t max_word_length(const words::Words& words) { size_t max {}; for (auto& pword : words) if (max < pword->length()) max = pword->length(); return max; } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_05/sorting.cppm ================================================ // A module interface partition containing words::sort() export module words:sorting; import :alias; // For the use of words::Words import :internals; export namespace words { void sort(Words& words); } /* Recursive helper for word::sort(Words&) */ namespace words { void sort(Words& words, size_t start, size_t end); } // Sort strings in ascending sequence void words::sort(Words& words) { if (!words.empty()) sort(words, 0, words.size() - 1); } void words::sort(Words& words, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle address to partition set swap(words, start, (start + end) / 2); // Swap middle address with start // Check words against chosen word size_t current {start}; for (size_t i {start + 1}; i <= end; i++) { if (*words[i] < *words[start]) // Is word less than chosen word? swap(words, ++current, i); // Yes, so swap to the left } swap(words, start, current); // Swap chosen and last swapped words if (current > start) sort(words, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(words, current + 1, end); // Sort right subset if exists } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_05/utils.cppm ================================================ // A module interface partition containing some extra words-related utilities export module words:utils; import :alias; // For use of words::Words alias import :internals; import ; import ; export namespace words { void extract_words(Words& words, const std::string& text, const std::string& separators); void show_words(const Words& words); } void words::extract_words(Words& words, const std::string& text, const std::string& separators) { size_t start {text.find_first_not_of(separators)}; // Start index of first word while (start != std::string::npos) { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of a word if (end == std::string::npos) // Found a separator? end = text.length(); // Yes, so set to end of text words.push_back(std::make_shared(text.substr(start, end - start))); start = text.find_first_not_of(separators, end + 1); // Find start next word } } void words::show_words(const Words& words) { const size_t field_width {max_word_length(words) + 1}; const size_t words_per_line {8}; std::cout << std::format("{:{}}", *words[0], field_width); // Output first word size_t words_in_line {}; // Number of words in current line for (size_t i {1}; i < words.size(); ++i) { // Output newline when initial letter changes or after 8 per line if ((*words[i])[0] != (*words[i - 1])[0] || ++words_in_line == words_per_line) { words_in_line = 0; std::cout << std::endl; } std::cout << std::format("{:{}}", *words[i], field_width); // Output a word } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_05/words.cppm ================================================ export module words; export import :alias; export import :sorting; export import :utils; ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_06/Soln11_06.cpp ================================================ // Defining alternate names for a namespace import ; import ; import words; namespace wrds { // A using directive us not a declaration, // and can therefore not be exported from a module. // Namespace aliases are therefore the preferred over // using directives inside a namespace... using namespace words; } int main() { wrds::Words the_words; std::string text; // The string to be sorted const auto separators{" ,.!?\"\n"}; // Word delimiters // Read the string to be processed from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); w::extract_words(the_words, text, separators); if (the_words.empty()) { std::cout << "No words in text." << std::endl; return 0; } wrds::sort(the_words); // Sort the words w::show_words(the_words); // Output the words } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_06/words.cpp ================================================ module words; import ; import ; size_t max_word_length(const words::Words& words); void words::extract_words(Words& words, const std::string& text, const std::string& separators) { size_t start {text.find_first_not_of(separators)}; // Start index of first word while (start != std::string::npos) { size_t end{ text.find_first_of(separators, start + 1) }; // Find end of a word if (end == std::string::npos) // Found a separator? end = text.length(); // Yes, so set to end of text words.push_back(std::make_shared(text.substr(start, end - start))); start = text.find_first_not_of(separators, end + 1); // Find start next word } } /* Additional helpers for word::sort(Words&) */ void swap(words::Words& words, size_t first, size_t second) { auto temp{ words[first] }; words[first] = words[second]; words[second] = temp; } void sort(words::Words& words, size_t start, size_t end); // Sort strings in ascending sequence void words::sort(Words& words) { if (!words.empty()) ::sort(words, 0, words.size() - 1); } void sort(words::Words& words, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle address to partition set swap(words, start, (start + end) / 2); // Swap middle address with start // Check words against chosen word size_t current {start}; for (size_t i {start + 1}; i <= end; i++) { if (*words[i] < *words[start]) // Is word less than chosen word? swap(words, ++current, i); // Yes, so swap to the left } swap(words, start, current); // Swap chosen and last swapped words if (current > start) sort(words, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(words, current + 1, end); // Sort right subset if exists } size_t max_word_length(const words::Words& words) { size_t max {}; for (auto& pword : words) if (max < pword->length()) max = pword->length(); return max; } void words::show_words(const Words& words) { const size_t field_width {max_word_length(words) + 1}; const size_t words_per_line {8}; std::cout << std::format("{:{}}", *words[0], field_width); // Output first word size_t words_in_line {}; // Number of words in current line for (size_t i {1}; i < words.size(); ++i) { // Output newline when initial letter changes or after 8 per line if ((*words[i])[0] != (*words[i - 1])[0] || ++words_in_line == words_per_line) { words_in_line = 0; std::cout << std::endl; } std::cout << std::format("{:{}}", *words[i], field_width); // Output a word } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 11/Soln11_06/words.cppm ================================================ export module words; import ; import ; import ; export namespace words { using Words = std::vector>; void sort(Words& words); void extract_words(Words& words, const std::string& text, const std::string& separators); void show_words(const Words& words); } export namespace w = words; /* This does not work: namespace wrds { // A using directive is not a declaration, // and can therefore not be exported from a module. // Namespace aliases are therefore preferred over // using directives inside a namespace... export using namespace words; } */ ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_01/Integer.cpp ================================================ module integer; import ; Integer::Integer(int value) : m_value{value} { std::cout << "Object created." << std::endl; } void Integer::show() const { std::cout << "Value is " << m_value << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_01/Integer.cppm ================================================ export module integer; export class Integer { public: Integer(int value); int getValue() const { return m_value; } void setValue(int value) { m_value = value; } void show() const; private: int m_value; }; ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_01/Soln12_01.cpp ================================================ // Implementing an Integer class import integer; import ; int main() { std::cout << "Create i with the value 10." << std::endl; Integer i {10}; i.show(); std::cout << "Change value of i to 15." << std::endl; // i.m_value = 15; // Cannot assign directly to m_value i.setValue(15); i.show(); std::cout << "Create j with a value that is 150 times that of i." << std::endl; const Integer j {150 * i.getValue()}; j.show(); std::cout << "Set value of j to ." << std::endl; // j.setValue(5000); // Cannot call setValue() on const object // (show() and getValue() work, though) std::cout << "Create k with the value 789." << std::endl; Integer k {789}; k.show(); std::cout << "Set value of k to sum of i and j values." << std::endl; k.setValue(i.getValue() + j.getValue()); k.show(); } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_02/Integer.cpp ================================================ module integer; import ; // Constructor Integer::Integer(int value) : m_value{ value } { std::cout << "Object created." << std::endl; } // Copy constructor Integer::Integer(const Integer& obj) : m_value{ obj.m_value } { std::cout << "Object created by copy constructor." << std::endl; } // Compare function with reference parameter int Integer::compare(const Integer& obj) const { if (m_value < obj.m_value) return -1; else if (m_value == obj.m_value) return 0; else return 1; } void Integer::show() const { std::cout << "Value is " << m_value << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_02/Integer.cppm ================================================ /***************************************************************\ This file shows two distinct ways of allowing an Integer to be constructed without an argument: 1) The first is to add a default constructor. To make sure m_value is zero, it adds zero initialization to the declaration of m_value. 2) The second (at the bottom of the file) is commented out and uses a default parameter value for the existing single-argument constructor. \***************************************************************/ export module integer; // Option 1: zero-initialize n and add a default constructor export class Integer { public: Integer() = default; // Zero-arg constructor Integer(int value); // Contructor with given value Integer(const Integer& obj); // Copy constructor int getValue() const { return m_value; } void setValue(int value) { m_value = value; } // int compare(Integer obj) const; // Compare function with value parameter int compare(const Integer& obj) const; // Compare function with reference parameter void show() const; private: int m_value{}; }; // Option 2: use zero a default parameter value /* export class Integer { public: Integer(int value = 0); // Contructor with given value int getValue() const { return m_value; } void setValue(int value) { m_value = value; } // int compare(Integer obj) const; // Compare function with value parameter int compare(const Integer& obj) const; // Compare function with reference parameter void show() const; private: int m_value; }; */ ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_02/Soln12_02.cpp ================================================ // Using a function with a reference parameter in the Integer class import ; import integer; /*****************************************************************\ Using the version of compare() with the pass-by-value parameter, the copy constructor is called because a copy of the argument is passed to the function. Using the version with the reference parameter a reference to the object is passed to the function so no constructor call is necessary. You cannot overload a function with a reference parameter with a function that has a non-reference parameter because the compiler cannot tell which function should be called in any particular instance. \*****************************************************************/ int main() { std::cout << "Create i with the value 0." << std::endl; Integer i; i.show(); std::cout << "Change value of i to 15." << std::endl; i.setValue(15); i.show(); std::cout << "Create j from object i." << std::endl; Integer j {i}; j.show(); std::cout << "Set value of j to 150 times that of i." << std::endl; j.setValue(150 * i.getValue()); j.show(); std::cout << "Create k with the value 789." << std::endl; Integer k {789}; k.show(); std::cout << "Set value of k to sum of i and j values." << std::endl; k.setValue(i.getValue() + j.getValue()); k.show(); std::cout << "Result of comparing i and j is " << i.compare(j) << std::endl; std::cout << "Result of comparing k and j is " << k.compare(j) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_03/Integer.cpp ================================================ module integer; import ; // Constructor Integer::Integer(int value) : m_value{ value } { } // Copy constructor Integer::Integer(const Integer& obj) : m_value{obj.m_value} { } Integer& Integer::add(const Integer& obj) { m_value += obj.m_value; return *this; } Integer& Integer::subtract(const Integer& obj) { m_value -= obj.m_value; return *this; } Integer& Integer::multiply(const Integer& obj) { m_value *= obj.m_value; return *this; } int Integer::compare(const Integer& obj) const { if (m_value < obj.m_value) return -1; else if (m_value == obj.m_value) return 0; else return 1; } void Integer::show() const { std::cout << "Value is " << m_value << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_03/Integer.cppm ================================================ export module integer; export class Integer { public: Integer(int value = 0); Integer(const Integer& obj); int getValue() const { return m_value; } void setValue(int value) { m_value = value; } Integer& add(const Integer& obj); Integer& subtract(const Integer& obj); Integer& multiply(const Integer& obj); int compare(const Integer& obj) const; void show() const; private: int m_value; }; ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_03/Soln12_03.cpp ================================================ // Implementing add(), subtract() and multiply() import ; import integer; /*********************************************************** By returning the dereferenced this pointer in the functions we can call the functions successively in a single statement. Note the parameter is a reference-to-const; const because the argument is not changed by the function and a reference to avoid the overhead of copying objects. The only other requirement for achieving the calculation in a single statement is figuring out how to sequence to operations to allow this. ***********************************************************/ int main() { // Create the even operands as Integers, // and use implicit conversions from int for the odd values const Integer four{4}; const Integer six{6}; const Integer eight{8}; // We can calculate 4*5*5*5+6*5*5+7*5+8 as: // ((4*5+6)*5+7)*5+8 Integer result {four}; // Set result object as copy of four std::cout << "Result is " << result.multiply(5).add(six).multiply(5).add(7).multiply(5).add(eight).getValue() << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_04/Integer.cpp ================================================ module integer; import ; /****************************************************************\ Implementing compare() as a friend is quite simple. We must declare the function as a friend in the class definition. We now need both objects as arguments and the code in the body of the function just compares the n members of the arguments. Both parameters are references-to-const. However, other than the need for you to exercise friend functions, there's no real reason for the compare() function to be a friend of the Integer class: it can be implemented perfectly fine using the public getValue() function as well. The nonFriendCompare() function given below is therefore preferred over a friend function. \****************************************************************/ // Constructor Integer::Integer(int value) : m_value{value} { std::cout << "Object created." << std::endl; } // Copy constructor Integer::Integer(const Integer& obj) : m_value{ obj.m_value } { std::cout << "Object created by copy constructor." << std::endl; } void Integer::show() const { std::cout << "Value is " << m_value << std::endl; } // friend compare function int compare(const Integer& obj1, const Integer& obj2) { if (obj1.m_value < obj2.m_value) return -1; else if (obj1.m_value == obj2.m_value) return 0; else return 1; } // non-friend compare function int nonFriendCompare(const Integer& obj1, const Integer& obj2) { if (obj1.getValue() < obj2.getValue()) return -1; else if (obj1.getValue() == obj2.getValue()) return 0; else return 1; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_04/Integer.cppm ================================================ export module integer; export class Integer { public: Integer(int value = 0); Integer(const Integer& obj); int getValue() const { return m_value; } void setValue(int value) { m_value = value; } void show() const; friend int compare(const Integer& obj1, const Integer& obj2); // friend compare function private: int m_value; }; // A non-friend function that implements the same function export int nonFriendCompare(const Integer& obj1, const Integer& obj2); ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_04/Soln12_04.cpp ================================================ // Using a friend function import ; import integer; int main() { std::cout << "Create i with the value 10." << std::endl; Integer i {10}; i.show(); std::cout << "Change value of i to 15." << std::endl; i.setValue(15); i.show(); std::cout << "Create j from object i." << std::endl; Integer j {i}; j.show(); std::cout << "Set value of j to 150 times that of i." << std::endl; j.setValue(150 * i.getValue()); j.show(); std::cout << "Create k with the value 789." << std::endl; Integer k {789}; k.show(); std::cout << "Set value of k to sum of i and j values." << std::endl; k.setValue(i.getValue() + j.getValue()); k.show(); std::cout << "Result of comparing i and j is " << compare(i, j) << std::endl; std::cout << "Result of comparing k and j is " << compare(k, j) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_05/Integer.cpp ================================================ /*****************************************************************\ To implement printCount(), you first need a static member variable to store the object count. Every constructor should then increment this count, and you need to add a destructor that decrements it. \*****************************************************************/ module integer; import ; // Constructor Integer::Integer(int value) : m_value{value} { ++s_count; std::cout << "Object created." << std::endl; } // Copy constructor Integer::Integer(const Integer& obj) : m_value{obj.m_value} { ++s_count; std::cout << "Object created by copy constructor." << std::endl; } // Destructor Integer::~Integer() { --s_count; std::cout << "Object deleted." << std::endl; } void Integer::show() const { std::cout << "Value is " << m_value << std::endl; } int Integer::compare(const Integer& obj) const { if (m_value < obj.m_value) return -1; else if (m_value == obj.m_value) return 0; else return 1; } void Integer::printCount() { std::cout << "There are now " << s_count << " Integer object(s)." << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_05/Integer.cppm ================================================ export module integer; export class Integer { public: Integer(int value = 0); // Contructor with given value Integer(const Integer& obj); // Copy constructor ~Integer(); // Destructor int getValue() const { return m_value; } void setValue(int value) { m_value = value; } int compare(const Integer& obj) const; // Compare function with reference parameter void show() const; static void printCount(); private: int m_value; static inline unsigned int s_count {}; }; ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_05/Soln12_05.cpp ================================================ // Using static members and a destructor to keep track of an object count import ; import integer; void showIntegerVal(Integer it) { it.show(); Integer::printCount(); // passed by value, so object count temporarily increases } void showIntegerRef(const Integer& it) { it.show(); Integer::printCount(); // passed by reference, so object count did not increase } int main() { std::cout << "Create i with the value 0." << std::endl; Integer i; i.show(); Integer::printCount(); // 1 object if (i.getValue() == 0) { std::cout << "Create j from object i." << std::endl; Integer j {i}; j.show(); Integer::printCount(); // 2 objects } Integer::printCount(); // 1 object again (Integer j was deleted because its scope ended) Integer array[] { 1, 2, 3 }; Integer::printCount(); // 4 objects showIntegerRef(array[0]); showIntegerVal(array[1]); Integer::printCount(); // 4 objects again } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_06/Box.cppm ================================================ export module box; import ; import ; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } void listBox() const { std::cout << std::format("Box({:.1f},{:.1f},{:.1f})", m_length, m_width, m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_06/RandomBoxes.cppm ================================================ export module box.random; import box; import ; // For random number generation import ; // For std::bind() import ; // For std::make_shared<>() and std::shared_ptr<>; // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } export Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } export auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_06/Soln12_06.cpp ================================================ // Creating a nested iterator class import box.random; import truckload; import ; /* Turns out moving the getFirstBox() and getLastBox() functions to the nested iterator class is easy. We used the same names for the member variables, so it is just a matter of changing "Truckload::" into "Truckload::Iterator::". Makes sense: the logic for iterating the linked lists has not changed. The only difference is that now there are m_current and m_head pointers per iteration, instead of only the one pair in the Truckload object. This allows nested iterations, concurrent iterations, and so on. You will encounter this iterator pattern again in Chapters 19 and 20. Btw: if it strikes you as sub-optimal that our findLargestBox() and findSmallestBox() are so similar, do not despair: in Chapter 19 we teach you the techniques you need to avoid this so-called code duplication. */ SharedBox findLargestBox(const Truckload& truckload); SharedBox findSmallestBox(const Truckload& truckload); int main() { Truckload load1; // Create an empty list // Add 12 random Box objects to the list const size_t boxCount{ 12 }; for (size_t i{}; i < boxCount; ++i) load1.addBox(randomSharedBox()); std::cout << "The first list:\n"; load1.listBoxes(); // Copy the truckload Truckload copy{ load1 }; std::cout << "The copied truckload:\n"; copy.listBoxes(); // Find the largest Box in the list const auto largestBox{ findLargestBox(load1) }; std::cout << "\nThe largest box in the first list is "; largestBox->listBox(); std::cout << std::endl; load1.removeBox(largestBox); std::cout << "\nAfter deleting the largest box, the list contains:\n"; load1.listBoxes(); const size_t nBoxes{ 20 }; // Number of vector elements std::vector boxes; // Array of Box objects for (size_t i{}; i < nBoxes; ++i) boxes.push_back(randomSharedBox()); Truckload load2{ boxes }; std::cout << "\nThe second list:\n"; load2.listBoxes(); const auto smallestBox{ findSmallestBox(load2) }; std::cout << "\nThe smallest box in the second list is "; smallestBox->listBox(); std::cout << std::endl; } SharedBox findLargestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator SharedBox largestBox{ iterator.getFirstBox() }; SharedBox nextBox{ iterator.getNextBox() }; while (nextBox) { if (nextBox->compare(*largestBox) > 0) largestBox = nextBox; nextBox = iterator.getNextBox(); } return largestBox; } SharedBox findSmallestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator SharedBox smallestBox{ iterator.getFirstBox() }; SharedBox nextBox{ iterator.getNextBox() }; while (nextBox) { if (nextBox->compare(*smallestBox) < 0) smallestBox = nextBox; nextBox = iterator.getNextBox(); } return smallestBox; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_06/Truckload.cpp ================================================ module truckload; import ; // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } void Truckload::listBoxes() const { const size_t boxesPerLine{ 4 }; size_t count {}; for (Package* package{m_head}; package; package = package->m_next) { std::cout << ' '; package->m_box->listBox(); if (! (++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_06/Truckload.cppm ================================================ export module truckload; import box; import ; import ; export using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload void listBoxes() const; // Output the Boxes private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the module interface) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_07/Box.cppm ================================================ export module box; import ; import ; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } void listBox() const { std::cout << std::format("Box({:.1f},{:.1f},{:.1f})", m_length, m_width, m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_07/RandomBoxes.cppm ================================================ export module box.random; import box; import ; // For random number generation import ; // For std::bind() import ; // For std::make_shared<>() and std::shared_ptr<>; // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } export Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } export auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_07/Soln12_07.cpp ================================================ // Create a doubly-linked list of Packages import box.random; import truckload; import ; /* To show reverse iteration, we've modified findSmallestBox() to iterate in reverse order */ SharedBox findLargestBox(const Truckload& truckload); SharedBox findSmallestBox(const Truckload& truckload); int main() { Truckload load; // Create an empty list // Add 12 random Box objects to the list const size_t boxCount{ 12 }; for (size_t i{}; i < boxCount; ++i) load.addBox(randomSharedBox()); std::cout << "The random truckload:\n"; load.listBoxes(); std::cout << std::endl; std::cout << "The same random truckload in reverse:\n"; load.listBoxesReversed(); std::cout << std::endl; std::cout << "The largest box (found using forward iteration) is "; findLargestBox(load)->listBox(); std::cout << std::endl; std::cout << "The smallest box (found using reverse iteration) is "; findSmallestBox(load)->listBox(); std::cout << std::endl; } SharedBox findLargestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator SharedBox largestBox{ iterator.getFirstBox() }; SharedBox nextBox{ iterator.getNextBox() }; while (nextBox) { if (nextBox->compare(*largestBox) > 0) largestBox = nextBox; nextBox = iterator.getNextBox(); } return largestBox; } SharedBox findSmallestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator SharedBox smallestBox{ iterator.getLastBox() }; SharedBox nextBox{ iterator.getPreviousBox() }; while (nextBox) { if (nextBox->compare(*smallestBox) < 0) smallestBox = nextBox; nextBox = iterator.getPreviousBox(); } return smallestBox; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_07/Truckload.cpp ================================================ module truckload; import ; // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package* m_previous; // Pointer to the previous Package in the list Package(SharedBox box) : m_box{ box }, m_next{}, m_previous{} {} ~Package() { delete m_next; } }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } void Truckload::listBoxes() const { const size_t boxesPerLine{ 4 }; size_t count {}; for (Package* package{m_head}; package; package = package->m_next) { std::cout << ' '; package->m_box->listBox(); if (! (++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } void Truckload::listBoxesReversed() const { const size_t boxesPerLine{ 4 }; size_t count{}; for (Package* package{ m_tail }; package; package = package->m_previous) { std::cout << ' '; package->m_box->listBox(); if (!(++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head, m_tail }; } SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } SharedBox Truckload::Iterator::getLastBox() { // Return m_tail's box (or nullptr if the list is empty) m_current = m_tail; return m_current ? m_current->m_box : nullptr; } SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } SharedBox Truckload::Iterator::getPreviousBox() { if (!m_current) // If there's no current... return getLastBox(); // ...return the last Box m_current = m_current->m_previous; // Move to the next package return m_current ? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty { package->m_previous = m_tail; // The package is added after the old tail m_tail->m_next = package; // Append the new object to the tail } else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { // No need for a trailing pointer anymore! // (We can go back one using the m_previous pointer of the doubly-linked list...) Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // Update the doubly-linked list pointers // (make a sketch of this to better see what is going on here!) if (current->m_previous) current->m_previous->m_next = current->m_next; if (current->m_next) current->m_next->m_previous = current->m_previous; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = current->m_previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } current = current->m_next; // Move current along to the next Package } return false; // Return false: boxToRemove was not found } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_07/Truckload.cppm ================================================ export module truckload; import box; import ; import ; export using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload void listBoxes() const; // Output the Boxes void listBoxesReversed() const; // Output the Boxes in reversed order private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the module interface) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getLastBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box SharedBox getPreviousBox(); // Get the previous Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_tail; // The tail of the linked list (needed for getLastBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head, Package* tail) : m_head{ head } , m_tail{ tail } , m_current{ nullptr } {} }; ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_08/Box.cppm ================================================ export module box; import ; import ; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } void listBox() const { std::cout << std::format("Box({:.1f},{:.1f},{:.1f})", m_length, m_width, m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_08/RandomBoxes.cppm ================================================ export module box.random; import box; import ; // For random number generation import ; // For std::bind() import ; // For std::make_shared<>() and std::shared_ptr<>; // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } export Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } export auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_08/Soln12_08.cpp ================================================ // Remove Boxes from a Truckload without searching a second time import box.random; import truckload; import ; /********************************************************************************\ The key to the solution is that the Iterator object already contains a pointer to the Package that needs to be removed. Of course, external code cannot use a Package, but it can use the Iterator. To exploit the knowledge contained in the Iterator, we add an overload of the removeBox() function with an Iterator parameter instead of a Box. To facilitate things, we also added getCurrentBox() to Iterator, which allowed us to remove some duplicated code in the various other getters of that class. Both findLargestBox() and findSmallestBox() are updated to return an Iterator as well. Note that the same technique is used by all Standard Library containers and algorithms (see Chapter 20)! Notice also that we created removePackage() to avoid duplicating this code in the two overloads of removeBox(). An alternative solution that also does not duplicate the removal logic consists of using an Iterator in removeBox(SharedBox), and then invoke removeBox(Iterator) once the box is found. removePackage() is then no longer required. We recommend you give this a try! \**********************************************************************************/ Truckload::Iterator findLargestBox(const Truckload& truckload); Truckload::Iterator findSmallestBox(const Truckload& truckload); int main() { Truckload load; // Create an empty list // Add 12 random Box objects to the list const size_t boxCount{ 12 }; for (size_t i{}; i < boxCount; ++i) load.addBox(randomSharedBox()); std::cout << "The random truckload:\n"; load.listBoxes(); std::cout << std::endl; const auto largestIter{ findLargestBox(load) }; const auto smallestIter{ findSmallestBox(load) }; std::cout << "The largest box (found using forward iteration) is "; largestIter.getCurrentBox()->listBox(); std::cout << '\n' << std::endl; load.removeBox(largestIter); std::cout << "The truckload without its largest box:\n"; load.listBoxes(); std::cout << std::endl; std::cout << "The smallest box (found using reverse iteration) is "; smallestIter.getCurrentBox()->listBox(); std::cout << '\n' << std::endl; load.removeBox(smallestIter); std::cout << "The truckload without its smallest box (in reverse order):\n"; load.listBoxesReversed(); } Truckload::Iterator findLargestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator iterator.getFirstBox(); auto largestBoxIterator{ iterator }; while (iterator.getNextBox()) { if (iterator.getCurrentBox()->compare(*largestBoxIterator.getCurrentBox()) > 0) { largestBoxIterator = iterator; } } return largestBoxIterator; } Truckload::Iterator findSmallestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator iterator.getLastBox(); auto smallestBoxIterator{ iterator }; while (iterator.getPreviousBox()) { if (iterator.getCurrentBox()->compare(*smallestBoxIterator.getCurrentBox()) < 0) { smallestBoxIterator = iterator; } } return smallestBoxIterator; } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_08/Truckload.cpp ================================================ module truckload; import ; // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package* m_previous; // Pointer to the previous Package in the list Package(SharedBox box) : m_box{ box }, m_next{}, m_previous{} {} ~Package() { delete m_next; } }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } void Truckload::listBoxes() const { const size_t boxesPerLine{ 4 }; size_t count {}; for (Package* package{m_head}; package; package = package->m_next) { std::cout << ' '; package->m_box->listBox(); if (! (++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } void Truckload::listBoxesReversed() const { const size_t boxesPerLine{ 4 }; size_t count{}; for (Package* package{ m_tail }; package; package = package->m_previous) { std::cout << ' '; package->m_box->listBox(); if (!(++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head, m_tail }; } SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return getCurrentBox(); } SharedBox Truckload::Iterator::getLastBox() { // Return m_tail's box (or nullptr if the list is empty) m_current = m_tail; return getCurrentBox(); } SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return getCurrentBox(); } SharedBox Truckload::Iterator::getPreviousBox() { if (!m_current) // If there's no current... return getLastBox(); // ...return the last Box m_current = m_current->m_previous; // Move to the next package return getCurrentBox(); } SharedBox Truckload::Iterator::getCurrentBox() const { return m_current ? m_current->m_box : nullptr; } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty { package->m_previous = m_tail; // The package is added after the old tail m_tail->m_next = package; // Append the new object to the tail } else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { for (auto* current{ m_head }; current != nullptr; current = current->m_next) { if (current->m_box == boxToRemove) // We found the Box! { removePackage(current); return true; } } return false; // Return false: boxToRemove was not found } bool Truckload::removeBox(Iterator iter) { if (iter.m_current) { removePackage(iter.m_current); return true; } else { return false; } } void Truckload::removePackage(Package* package) { // Update the doubly-linked list pointers if (package->m_previous) package->m_previous->m_next = package->m_next; if (package->m_next) package->m_next->m_previous = package->m_previous; // Update pointers in member variables where required: if (package == m_head) m_head = package->m_next; if (package == m_tail) m_tail = package->m_previous; package->m_next = nullptr; // Disconnect the current Package from the list delete package; // and delete it } ================================================ FILE: Exercises/Modules/Chapter 12/Soln12_08/Truckload.cppm ================================================ export module truckload; import box; import ; import ; export using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload bool removeBox(Iterator iter); // Remove the Box pointed to by this Iterator void listBoxes() const; // Output the Boxes void listBoxesReversed() const; // Output the Boxes in reversed order private: class Package; void removePackage(Package* package); Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the module interface) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getLastBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box SharedBox getPreviousBox(); // Get the previous Box SharedBox getCurrentBox() const; // Get the current Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_tail; // The tail of the linked list (needed for getLastBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head, Package* tail) : m_head{ head } , m_tail{ tail } , m_current{ nullptr } {} }; ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_01/Box.cpp ================================================ module box; import ; import ; // For the min() and max() function templates double Box::volume() const { return m_length * m_width * m_height; } Box Box::operator+(const Box& aBox) const { // New object has larger length and width, and sum of heights return Box{ std::max(m_length, aBox.m_length), std::max(m_width, aBox.m_width), m_height + aBox.m_height }; } Box Box::operator*(double factor) const { return Box{ m_length * factor, m_width * factor, m_height * factor, }; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_01/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream export class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box operator+(const Box& aBox) const; // Function to add two Box objects Box operator*(double factor) const; // Function to multiply a Box with a given factor private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; export std::ostream& operator<<(std::ostream& stream, const Box& box); ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_01/Soln13_01.cpp ================================================ // Exercise 13-1 // Implementing the * operator for the Box class // to post-multiply by an integer import ; import box; int main() { Box box {2, 3, 4}; std::cout << "Box is " << box << std::endl; unsigned n {3}; Box newBox{ box * n }; std::cout << "After multiplying by " << n << " box is " << newBox << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_02/Box.cpp ================================================ module box; import ; import ; // For the min() and max() function templates double Box::volume() const { return m_length * m_width * m_height; } Box Box::operator+(const Box& aBox) const { // New object has larger length and width, and sum of heights return Box{ std::max(m_length, aBox.m_length), std::max(m_width, aBox.m_width), m_height + aBox.m_height }; } Box Box::operator*(double factor) const { return Box{ m_length * factor, m_width * factor, m_height * factor }; } Box operator*(double factor, const Box& box) { // Or: return box * factor; return Box{ box.getLength() * factor, box.getWidth() * factor, box.getHeight() * factor }; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_02/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream export class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box operator+(const Box& aBox) const; // Function to add two Box objects Box operator*(double factor) const; // Function to multiply a Box with a given factor private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; // Function to pre-multiply a Box with a given factor export Box operator*(double factor, const Box& box); export std::ostream& operator<<(std::ostream& stream, const Box& box); ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_02/Soln13_02.cpp ================================================ // Exercise 13-2 // Implementing the * operator for the Box class to pre-multiply by a number import ; import box; int main() { Box box {2, 3, 4}; std::cout << "Box is " << box << std::endl; unsigned n {3}; Box newBox{ n * box }; std::cout << "After pre-multiplying by " << n << " box is " << newBox << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_03/Box.cpp ================================================ module box; import ; import ; // For the min() and max() function templates double Box::volume() const { return m_length * m_width * m_height; } Box Box::operator+(const Box& aBox) const { Box copy{ *this }; // Implement in terms of += operator (avoid duplication) copy += aBox; return copy; // Return new object (convention) } Box Box::operator*(double factor) const { Box copy{ *this }; // Implement in terms of *= operator (avoid duplication) copy *= factor; return copy; // Return new object (convention) } Box Box::operator/(double divisor) const { Box copy{ *this }; // Implement in terms of /= operator (avoid duplication) copy /= divisor; return copy; // Return new object (convention) } Box& Box::operator+=(const Box& aBox) { // New object has larger length and width, and sum of heights m_length = std::max(m_length, aBox.m_length); m_width = std::max(m_width, aBox.m_width); m_height += aBox.m_height; return *this; // Return a reference to the left operand (convention) } Box& Box::operator*=(double factor) { m_length *= factor; m_width *= factor; m_height *= factor; return *this; // Return a reference to the left operand (convention) } Box& Box::operator/=(double divisor) { m_length /= divisor; m_width /= divisor; m_height /= divisor; return *this; // Return a reference to the left operand (convention) } Box operator*(double factor, const Box& box) { return box * factor; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_03/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream export class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box operator+(const Box& aBox) const; // Function to add two Box objects Box operator*(double factor) const; // Function to multiply a Box with a given factor Box operator/(double divisor) const; // Function to divide a Box by a given divisor Box& operator+=(const Box& aBox); Box& operator*=(double factor); Box& operator/=(double divisor); private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; // Function to pre-multiply a Box with a given factor export Box operator*(double factor, const Box& box); export std::ostream& operator<<(std::ostream& stream, const Box& box); ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_03/Soln13_03.cpp ================================================ // Exercise 13-3 // Implementing the missing /, *=, +=, and /= operators for the Box class import ; import box; int main() { Box box {2, 3, 4}; std::cout << "Box is " << box << std::endl; size_t n {3}; box *= 3; std::cout << "After multiplying by " << n << " box is " << box << std::endl; box /= 3; std::cout << "After dividing by " << n << ", the box is again " << box << std::endl; Box newBox {2 * box}; std::cout << "Twice " << box << " is " << newBox << std::endl; std::cout << "Half that is again " << (newBox / 2) << std::endl; std::cout << "Adding both boxes gives " << (box + newBox) << std::endl; box += newBox; std::cout << "The same can be obtained by usign += as well: " << box << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_04/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream import ; export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; bool operator==(double otherVolume) const { return volume() == otherVolume; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; export std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_04/Soln13_04.cpp ================================================ // Exercise 13-4 // Adding support for equality operators that compare Boxes and volumes import ; import box; /* Because the C++20 compiler automatically rewrites != expression in terms of == and/or even reverses the order of the operands, only one additional operator overload definition is required to make it all work. */ int main() { Box box1{ 1, 2, 3 }; Box box2{ 3, 2, 1 }; Box box3{ 1, 2, 3 }; // Try out all == and != operators (old and new, the latter in both directions) std::cout << "box1 and box2 are " << (box1 == box2 ? "" : "not ") << "equal\n"; std::cout << "box1 and box3 are " << (box1 != box3 ? "not " : "") << "equal\n"; std::cout << "box1 is " << (box1 == 6.0 ? "" : "not ") << "equal to 6.0\n"; std::cout << "10.0 is " << (10 != box2 ? "not " : "") << "equal to box2\n"; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_05/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream import ; export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; // Unary negation operator (!box is true if the Box has no volume) bool operator!() const { return volume() == 0; } // Type conversion operator (converts a Box to a Boolean; true if it has volume) operator bool() const { return volume() != 0; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; export std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_05/Soln13_05.cpp ================================================ // Exercise 13-5 // Implementing the obvious operators for the Box class // to allow it to be used, for instance, in if statements. // A Box is "true" if and only if its volume is non-zero. import ; import box; void testBox(const Box& box) { std::cout << "The box's volume is " << box.volume() << ".\n"; if (box) std::cout << "This volume is non-zero."; if (!box) std::cout << "This volume is zero."; std::cout << std::endl; } int main() { Box box1{2, 3, 4}; std::cout << "box1 is " << box1 << std::endl; testBox(box1); std::cout << std::endl;; Box box2{0, 0, 0}; std::cout << "box2 is " << box2 << std::endl; testBox(box2); } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_06/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream import ; export class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; // Explicit type conversion operator (converts a Box to a Boolean; true if it has volume) explicit operator bool() const { return volume() != 0; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; export std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_06/Soln13_06.cpp ================================================ // Exercise 13-6 // In canonical C++, you should only implement a single operator to allow // objects to be used in if statements, and in Boolean expressions in general: // a conversion operator for type bool. // // This conversion operator, moreover, is normally qualified as explicit. // This is far from obvious: despite the explicit qualifier, // objects still implicitly convert to bool in, for instance, if statements. // As illustrated at the bottom of main(), however, // simply assigning a Box to a variable of type bool indeed no longer works // without an explicit type conversion (in most cases this is the desired behavior). import ; import box; void testBox(const Box& box) { std::cout << "The box's volume is " << box.volume() << ".\n"; if (box) std::cout << "This volume is non-zero."; if (!box) std::cout << "This volume is zero."; std::cout << std::endl; } int main() { Box box1{2, 3, 4}; std::cout << "box1 is " << box1 << std::endl; testBox(box1); std::cout << std::endl;; Box box2{0, 0, 0}; std::cout << "box2 is " << box2 << std::endl; testBox(box2); // bool b1{ box1 }; /* Does not compile! */ bool b2{ static_cast(box2) }; // Needs an explicit type conversion } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_07/Rational.cppm ================================================ export module rational; import ; export class Rational { public: // Constructor Rational(int numerator = 0, int denominator = 1) : m_numerator {numerator}, m_denominator {denominator} {} // Accessors int getNumerator() const { return m_numerator; } int getDenominator() const { return m_denominator; } // Mutators void setNumerator(int numerator) { m_numerator = numerator; } void setDenominator(int denominator) { m_denominator = denominator; } // Casting operators (could be non-explicit as well: is a matter of taste) explicit operator double() const { return static_cast(m_numerator) / m_denominator; } explicit operator float() const { return static_cast(m_numerator) / m_denominator; } // Full support for all comparison operators with both Rational and double // through the magic of the spaceship operator. // The only other operator you need to define here is equality // (because the spaceship operator cannot be defaulted). auto operator<=>(const Rational& other) { return m_numerator * other.m_denominator <=> other.m_numerator * m_denominator; } auto operator<=>(double value) { return static_cast(*this) <=> value; } bool operator==(const Rational& other) { return m_numerator * other.m_denominator == other.m_numerator * m_denominator; } bool operator==(double value) { return static_cast(*this) == value; } // Unary arithmetic operator Rational operator-() const { return Rational{-m_numerator, m_denominator}; } // A Rational is false if and only if its numerator equals zero. // Note: for operator bool(), we used explicit here to sidestep ambiguities with other operators. // We did not add operator!, // because this conversion operator covers all use in Boolean expressions // (see also previous exercise). explicit operator bool() const { return m_numerator != 0; } // Compound assignment operators Rational& operator+=(const Rational& other) { m_numerator = m_numerator * other.m_denominator + other.m_numerator * m_denominator; m_denominator = m_denominator * other.m_denominator; return *this; }; Rational& operator-=(const Rational& other) { m_numerator = m_numerator * other.m_denominator - other.m_numerator * m_denominator; m_denominator = m_denominator * other.m_denominator; return *this; }; Rational& operator*=(const Rational& other) { m_numerator *= other.m_numerator; m_denominator *= other.m_denominator; return *this; }; Rational& operator/=(const Rational& other) { m_numerator *= other.m_denominator; m_denominator *= other.m_numerator; return *this; }; // Prefix and postfix increment and decrement operators Rational& operator++() { m_numerator += m_denominator; return *this; } const Rational operator++(int) { auto copy(*this); // Create a copy of the current object ++(*this); // Increment the current object using the prefix operator... return copy; // Return the unincremented copy } Rational& operator--() { m_numerator -= m_denominator; return *this; } const Rational operator--(int) { auto copy(*this); // Create a copy of the current object --(*this); // Increment the current object using the prefix operator... return copy; // Return the unincremented copy } private: int m_numerator, m_denominator; }; // Stream output operator export std::ostream& operator<<(std::ostream& stream, const Rational& r) { return stream << r.getNumerator() << '/' << r.getDenominator(); } // Binary arithmetic operators: non-member functions to allow for implicit conversions // This allows expressions such as 2 * (myRationale + 1) export Rational operator+(const Rational& one, const Rational& other) { auto copy{ one }; return copy += other; } export Rational operator-(const Rational& one, const Rational& other) { auto copy{ one }; return copy -= other; } export Rational operator*(const Rational& one, const Rational& other) { auto copy{ one }; return copy *= other; } export Rational operator/(const Rational& one, const Rational& other) { auto copy{ one }; return copy /= other; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_07/Soln13_07.cpp ================================================ // Exercise 13-7 // Rational operators import ; import rational; int main() { Rational x{3, 4}; Rational y{1, 2}; std::cout << "x = " << x << std::endl; std::cout << "y = " << y << std::endl; std::cout << "x = " << static_cast(x) << std::endl; std::cout << "y = " << static_cast(y) << std::endl; std::cout << "-x = " << -x << std::endl; std::cout << "x + y = " << x + y << std::endl; std::cout << "x - y = " << x - y << std::endl; std::cout << "x * y = " << x * y << std::endl; std::cout << "x / y = " << x / y << std::endl; std::cout << "x + 2 = " << x + 2 << std::endl; std::cout << "3 - y = " << 3 - y << std::endl; std::cout << "x * 4 = " << x * 4 << std::endl; std::cout << "5 / y = " << 5 / y << std::endl; std::cout << std::boolalpha; // Print true and false as "true" and "false" instead of "1" and "0" std::cout << "x < y = " << (x < y) << std::endl; std::cout << "x > y = " << (x > y) << std::endl; std::cout << "x == y = " << (x == y) << std::endl; std::cout << "x != y = " << (x != y) << std::endl; std::cout << "x >= y = " << (x >= y) << std::endl; std::cout << "x >= y = " << (x <= y) << std::endl; std::cout << "x < 1 = " << (x < 1) << std::endl; std::cout << "2 > y = " << (2 > y) << std::endl; std::cout << "x == 3 = " << (x == 3) << std::endl; std::cout << "4 != y = " << (4 != y) << std::endl; std::cout << "x >= 5 = " << (x >= 5) << std::endl; std::cout << "6 >= y = " << (6 <= y) << std::endl; std::cout << "x < 1.0 = " << (x < 1.0) << std::endl; std::cout << "2 > y = " << (2.0 > y) << std::endl; std::cout << "x == 0.75 = " << (x == 0.75) << std::endl; std::cout << "1.5 != y = " << (1.5 != y) << std::endl; std::cout << "x >= 5 = " << (x >= 5.0) << std::endl; std::cout << "6 >= y = " << (6.0 <= y) << std::endl; x += Rational(1, 4); std::cout << "x += 1/4 --> x = " << x << std::endl; x *= 2; std::cout << "x *= 2 --> x = " << x << std::endl; y += 1; std::cout << "y += 1 --> y = " << y << std::endl; std::cout << "y++ = " << y++ << std::endl; std::cout << "y = " << y << std::endl; std::cout << "--y = " << --y << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_08/Box.cpp ================================================ module; #include // For the min() and max() function templates module box; import ; double Box::volume() const { return m_length * m_width * m_height; } // Overloaded += operator Box& Box::operator+=(const Box& aBox) { // New object has larger length and width, and sum of heights m_length = std::max(m_length, aBox.m_length); m_width = std::max(m_width, aBox.m_width); m_height += aBox.m_height; return *this; } // Function to add two Box objects Box Box::operator+(const Box& aBox) const { Box copy{ *this }; copy += aBox; return copy; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_08/Box.cppm ================================================ export module box; import ; // For std::partial_ordering (see Chapter 4) import ; // For std::ostream export class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box& operator+=(const Box& aBox); // Function to add a Box objects Box operator+(const Box& aBox) const; // Function to add two Box objects private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; export std::ostream& operator<<(std::ostream& stream, const Box& box); ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_08/PRNG.cppm ================================================ export module PRNG; export class PseudoRandomNumberGenerator { public: PseudoRandomNumberGenerator(int n = 0) : m_n{ n } {} // A function call operator (no parameters, return type int) // Unlike many function call operators, this one alters the state of the object. int operator()() { const int current{ m_n }; m_n = (m_n * 41 + 7) % 100; // See chapter 12 return current; } private: int m_n; }; ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_08/Soln13_08.cpp ================================================ // Exercise 13-8 // Creating a simply pseudo-random number generator (PRNG) functor class. // Ours is a very limited one that generates numbers between 0 and 100. // Seeding and using it requires some type conversions, // but other than that our PRNG functor acts as a drop-in replacement to the original one! import ; import ; import ; import ; // For random number generation import ; // For std::bind() import box; import PRNG; int main() { const double limit {99}; // Upper limit on Box dimensions std::random_device seeder; // True random number generator to obtain a seed (slow) auto random{ PseudoRandomNumberGenerator{ static_cast(seeder()) } }; const size_t boxCount {20}; // Number of Box object to be created std::vector boxes; // Vector of Box objects // Create 20 Box objects for (size_t i {}; i < boxCount; ++i) boxes.push_back(Box{ static_cast(random()), static_cast(random()), static_cast(random()) }); size_t first {}; // Index of first Box object of pair size_t second {1}; // Index of second Box object of pair double minVolume {(boxes[first] + boxes[second]).volume()}; for (size_t i {}; i < boxCount - 1; ++i) { for (size_t j {i + 1}; j < boxCount; j++) { if (boxes[i] + boxes[j] < minVolume) { first = i; second = j; minVolume = (boxes[i] + boxes[j]).volume(); } } } std::cout << "The two boxes that sum to the smallest volume are " << boxes[first] << " and " << boxes[second] << '\n'; std::cout << std::format("The volume of the first box is {:.1f}\n", boxes[first].volume()); std::cout << std::format("The volume of the second box is {:.1f}\n", boxes[second].volume()); std::cout << "The sum of these boxes is " << (boxes[first] + boxes[second]) << '\n'; std::cout << std::format("The volume of the sum is {:.1f}", minVolume) << std::endl; Box sum{ 0, 0, 0 }; // Start from an empty Box for (const auto& box : boxes) // And then add all randomly generated Box objects sum += box; std::cout << "The sum of " << boxCount << " random boxes is " << sum << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_09/Box.cppm ================================================ export module box; import ; import ; import ; // For the std::min()/max() function templates export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f},{:.1f},{:.1f})", box.m_length, box.m_width, box.m_height); } Box operator+(const Box& aBox) const // Function to add two Box objects { return Box{ std::max(m_length, aBox.m_length), std::max(m_width, aBox.m_width), m_height + aBox.m_height }; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_09/Soln13_09.cpp ================================================ // Exercise 13-9 // Adding an assignment operator for Truckload import ; import ; import ; // For random number generation import ; // For std::bind() import truckload; // See Chapter 12 for an explanation of this function auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 1.0, max }; // Generate in [1, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const double limit{ 99.0 }; // Upper limit on Box dimensions auto random{ createUniformPseudoRandomNumberGenerator(limit) }; Truckload load; const size_t boxCount {20}; // Number of Box object to be created // Create boxCount Box objects for (size_t i {}; i < boxCount; ++i) load.addBox(std::make_shared(random(), random(), random())); std::cout << "The boxes in the Truckload are:\n"; std::cout << load << std::endl; Truckload copied; copied = load; // Use copy assignment std::cout << "The boxes in the copied Truckload are:\n"; std::cout << copied; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_09/Truckload.cpp ================================================ module truckload; import ; // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } Truckload& Truckload::operator=(const Truckload& other) { if (&other != this) // Do not forget: avoid issues with self-assignment! { delete m_head; // Delete all current packages m_head = m_tail = nullptr; // Reset both pointers // Same as copy constructor // (see Chapter 17 for the copy-and-swap idiom that allows you to avoid // duplicating logic like this...) for (Package* package{ other.m_head }; package; package = package->m_next) { addBox(package->m_box); } } return *this; } // Destructor: clean up the list Truckload::~Truckload() { delete m_head; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox& Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } return nullBox; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Exercises/Modules/Chapter 13/Soln13_09/Truckload.cppm ================================================ export module truckload; import box; import ; import ; import ; using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor Truckload& operator=(const Truckload& other); // Copy assignment operator ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list static inline SharedBox nullBox{}; // Pointer to nullptr }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; export std::ostream& operator<<(std::ostream& stream, const Truckload& load); ================================================ FILE: Exercises/Modules/Chapter 14/Soln14_01/Animals.cppm ================================================ // Animal class and classes derived from Animal export module animals; // Note: Animal, Lion, and Aardvark would typically be defined each in their own (sub)module. // This example shows that this does not have to be the case; that is: // - file and module names do not have to match the name of a class; and // - multiple classes may be declared and defined in the same module import ; import ; import ; export class Animal { public: Animal(std::string_view name, int weight) // Constructor : m_name{ name }, m_weight{ weight } {} void who() const // Display name and weight { std::cout << "My name is " << m_name << " and I weigh " << m_weight << "lbs." << std::endl; } private: std::string m_name; // Name of the animal int m_weight; // Weight of the animal }; export class Lion : public Animal { public: // Define Lion constructor that calls base class constructor Lion(std::string_view name, int weight) : Animal{name, weight} {} }; export class Aardvark : public Animal { public: using Animal::Animal; // Inherit constructor instead (preferred) }; ================================================ FILE: Exercises/Modules/Chapter 14/Soln14_01/Soln14_01.cpp ================================================ // Exercise 14-1 Exercising the Animal classes // The solution shows two options, with the second one being the preferred option. import animals; int main() { Lion myLion{"Leo", 400}; Aardvark myAardvark{"Algernon", 50}; myLion.who(); myAardvark.who(); } ================================================ FILE: Exercises/Modules/Chapter 14/Soln14_02/Animals.cppm ================================================ // Animal class and classes derived from Animal export module animals; import ; import ; import ; export class Animal { public: Animal(std::string_view name, int weight) // Constructor : m_name{ name }, m_weight{ weight } {} protected: void who() const // Display name and weight { std::cout << "My name is " << m_name << " and I weigh " << m_weight << "lbs." << std::endl; } private: std::string m_name; // Name of the animal int m_weight; // Weight of the animal }; export class Lion : public Animal { public: // Define Lion constructor that calls base class constructor Lion(std::string_view name, int weight) : Animal{name, weight} {} // Create function in derived class that hides and explicitly // invokes the protected function of the base class void who() { return Animal::who(); } }; export class Aardvark : public Animal { public: using Animal::Animal; // Inherit constructor instead (preferred) using Animal::who; // Use using to alter acccess specifier instead (preferred) }; ================================================ FILE: Exercises/Modules/Chapter 14/Soln14_02/Soln14_02.cpp ================================================ // Exercise 14-2 The who() function for the base class has the protected access specifier, // so we ensure the derived classes allow public access to the who() function. // The solution shows two alternatives, the second one being the preferred option. import animals; int main() { Lion myLion{"Leo", 400}; Aardvark myAardvark{"Algernon", 50}; myLion.who(); myAardvark.who(); } ================================================ FILE: Exercises/Modules/Chapter 14/Soln14_03/Animals.cpp ================================================ // Exercise 14-3 - Animals.cpp // Implementations of the Animal class and classes derived from Animal module animals; import ; // Identify the animal void Animal::who() const { std::cout << "My name is " << m_name << " and I weigh " << m_weight << "lbs." << std::endl; } // Identify the Lion void Lion::who() const { std::cout << "Rrroarrrr... I am a lion. "; Animal::who(); // Call base function } // Identify the Aardvark void Aardvark::who() const { Animal::who(); // Call base function std::cout << "Oh. And I'm an aardvark. " << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 14/Soln14_03/Animals.cppm ================================================ // Animal class and classes derived from Animal export module animals; import ; import ; export class Animal { public: Animal(std::string_view name, int weight) // Constructor : m_name{ name }, m_weight{ weight } {} void who() const; // Display name and weight private: std::string m_name; // Name of the animal int m_weight; // Weight of the animal }; export class Lion : public Animal { public: // Define Lion constructor that calls base class constructor Lion(std::string_view name, int weight) : Animal{name, weight} {} void who() const; // Define Lion-specific function }; export class Aardvark : public Animal { public: using Animal::Animal; // Inherit constructor void who() const; // Define Aardvark-specific function }; ================================================ FILE: Exercises/Modules/Chapter 14/Soln14_03/Soln14_03.cpp ================================================ // Exercise 14-3 By adding a few lines to the test program, we can see the difference // between the calls to the base class and derived class who() functions. import ; import animals; int main() { Lion myLion{"Leo", 400}; Aardvark myAardvark{"Algernon", 50}; std::cout << "Calling derived versions of who():\n"; myLion.who(); myAardvark.who(); std::cout << "\nCalling base versions of who():\n"; myLion.Animal::who(); // By qualifying the base class static_cast(myAardvark).who(); // By casting } ================================================ FILE: Exercises/Modules/Chapter 14/Soln14_04/Person.cpp ================================================ // Person class implementation module person; import ; Person::Person(size_t age, std::string_view name, Gender gender) : m_age {age}, m_name {name}, m_gender {gender} { // Instead of just initializing the members with the argument values, // you could validate the arguments by doing reasonableness checks. // e.g. Name mustn't be empty, and age should be less than 130 say. // To handle a failure sensibly we really need exceptions, // but we don't get to those until chapter 16. } // Display details of Person object void Person::who() const { std::cout << "\nThis is " << m_name << " who is " << m_age << " years old." << std::endl; } void Person::haveBirthday() { ++m_age; } std::string_view Person::getGenderString() const { switch (m_gender) { case Gender::male: return "a male"; case Gender::female: return "a female"; case Gender::other: return "an other-gendered"; } // Unreachable return statement required by some compilers // (switch statement is exhaustive) return {}; } // Display details of Employee object void Employee::who() const { std::cout << getName() << " is " << getGenderString() << " employee aged " << getAge() << "." << std::endl; } // Display details of Executive object (execs are particularly sensitive about their age...) void Executive::who() const { std::cout << getName() << " is " << getGenderString() << " executive." << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 14/Soln14_04/Person.cppm ================================================ // Person class and classes derived from Person export module person; import ; import ; /* We covered the advantages of the principle of data hiding extensively in Chapter 12. In practice, a common convention is to make all data members of a class private. This should thus probably become your default as well. Getter and setter member functions can then be added to either the public or protected interface to control access and/or modifications. Alternatively, you could make the member variables of Person and Employee themselves protected. Often, people take this shortcut because it is somewhat less work: then you wouldn't need the protected getter functions. However, protected data is a bad idea, much for the same reason as public ones are. We refer to the corresponding section in Chapter 14 for a more detailed motivation. */ // Possible alternatives for representing a gender include: // - a Boolean value, but limited, and which is the 'true' gender? // - a char value (say 'm', 'f', 'o'), but then how to enforce that no other values are assigned? // - a string ("male", "female", etc), but same problem as with chars (and also excessively expensive) // The safest, most readable solution is therefore probably an enumeration: export enum class Gender { male, female, other }; export class Person { public: Person() = default; // Default constructor - necessary to define arrays Person(size_t age, std::string_view name, Gender gender); void who() const; // Display details void haveBirthday(); protected: // Functions to allow derived classes to access to a Person's details size_t getAge() const { return m_age; } const std::string& getName() const { return m_name; } Gender getGender() const { return m_gender; } // Convenience function: std::string_view getGenderString() const; private: size_t m_age{}; // Age in years std::string m_name; Gender m_gender{ Gender::female }; }; export class Employee : public Person { public: Employee() = default; // Default constructor - necessary to define arrays Employee(size_t age, std::string_view name, Gender gender, long num) : Person{age, name, gender}, m_personnelNumber {num} {} void who() const; // Display details protected: long getPersonnelNumber() { return m_personnelNumber; } private: long m_personnelNumber{}; }; export class Executive : public Employee { public: using Employee::Employee; // Inherit all constructors void who() const; // Display details }; ================================================ FILE: Exercises/Modules/Chapter 14/Soln14_04/Soln14_04.cpp ================================================ // Exercise 14-4 Working with Employee and Executive objects import ; import ; import person; int main() { std::vector employees { Employee{ 21, "Randy Marathon", Gender::male, 34567 }, Employee{ 32, "Anna Pothecary", Gender::female, 34578 }, Employee{ 46, "Peter Out", Gender::male, 34589 }, Employee{ 37, "Sheila Rangeit", Gender::female, 34598 }, Employee{ 65, "Jack Ittin", Gender::male, 34667 } }; for (const auto& employee : employees) employee.who(); std::cout << std::endl; // Note: explicitly specifying the type in front of every {...} // in a vector's initializer list, like we did for Employees, // is actually not required... std::vector executives { { 44, "Irwin Pootlemeyer", Gender::other, 35567 }, { 32, "Alexa Workwell", Gender::female, 35578 }, { 42, "Steve Stove", Gender::male, 35589 }, { 33, "Sue Neenuf", Gender::female, 35598 }, { 29, "Melanie Clair", Gender::female, 35667 } }; for (const auto& executive : executives) { executive.who(); executive.Employee::who(); // Executive, I shall know thy age! } } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_01/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal module animals; // Constructor Animal::Animal(std::string_view name, unsigned weight) : m_name{ name }, m_weight{ weight } {} // Return string describing the animal std::string Animal::who() const { return "My name is " + m_name + ". My weight is " + std::to_string(m_weight) + " lbs."; } // Make like a sheep std::string_view Sheep::sound() const { return "Baaaa!!"; } // Make like a dog std::string_view Dog::sound() const { return "Woof woof!!"; } // Make like a cow std::string_view Cow::sound() const { return "Mooooo!!"; } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_01/Animals.cppm ================================================ // Exercise 15-1 Animals.cppm // Animal classes export module animals; import ; import ; export class Animal { public: Animal(std::string_view name, unsigned weight);// Constructor virtual ~Animal() = default; // Very important: a virtual destructor! virtual std::string who() const; // Return string containing name and weight virtual std::string_view sound() const = 0; // Return the sound of an animal private: std::string m_name; // Name of the animal unsigned m_weight; // Weight of the animal }; export class Sheep : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a sheep }; export class Dog : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a dog }; export class Cow : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a cow }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_01/Soln15_01.cpp ================================================ // Exercise 15-1 Exercising Zoo and Animal classes import zoo; import ; // For random number generation import ; // For std::bind() import ; import ; import ; // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_name_options{ 10 }; using NameOptions = std::array; const NameOptions dogNames { "Fido", "Rover" , "Lassie" , "Lambikins", "Poochy", "Spit", "Gnasher", "Samuel" , "Wellington", "Patch" }; const NameOptions sheepNames { "Bozo", "Killer", "Tasty", "Pete", "Chops", "Blackie", "Whitey", "Eric" , "Sean", "Shep" }; const NameOptions cowNames { "Dolly", "Daisy", "Shakey", "Amy", "Dilly", "Dizzy", "Eleanor", "Zippy" , "Zappy", "Happy" }; const unsigned minDogWt{ 1 }; // Minimum weight of a dog in pounds const unsigned maxDogWt{ 120 }; // Maximum weight of a dog in pounds const unsigned minSheepWt{ 80 }; // Minimum weight of a dog in pounds const unsigned maxSheepWt{ 150 }; // Maximum weight of a dog in pounds const unsigned minCowWt{ 800 }; // Minimum weight of a dog in pounds const unsigned maxCowWt{ 1500 }; // Maximum weight of a dog in pounds auto randomAnimalType { createUniformPseudoRandomNumberGenerator(0, 2) }; // 0, 1, or 2 auto randomNameIndex { createUniformPseudoRandomNumberGenerator(0, num_name_options - 1) }; auto randomDogWeight { createUniformPseudoRandomNumberGenerator(minDogWt, maxDogWt) }; auto randomSheepWeight{ createUniformPseudoRandomNumberGenerator(minSheepWt, maxSheepWt) }; auto randomCowWeight { createUniformPseudoRandomNumberGenerator(minCowWt, maxCowWt) } ; std::vector animals; // Stores smart pointers to animals size_t numAnimals {}; // Number of animals to be created std::cout << "How many animals in the zoo? "; std::cin >> numAnimals; Zoo zoo; // Create an empty Zoo // Create random animals and add them to the Zoo for (size_t i {}; i < numAnimals; ++i) { switch (randomAnimalType()) { case 0: // Create a sheep zoo.addAnimal(std::make_shared(sheepNames[randomNameIndex()], randomSheepWeight())); break; case 1: // Create a dog zoo.addAnimal(std::make_shared(dogNames[randomNameIndex()], randomDogWeight())); break; case 2: // Create a cow zoo.addAnimal(std::make_shared(cowNames[randomNameIndex()], randomCowWeight())); break; } } zoo.showAnimals(); // Display the animals } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_01/Zoo.cpp ================================================ // Implementations of the Zoo class that stores pointers to Animals module zoo; import animals; import ; import ; // For operator<< import ; // For operator<< // Constructor from a vector of animals Zoo::Zoo(const std::vector& animals) : m_animals{ animals } {} // Add an animal to the zoo void Zoo::addAnimal(AnimalPtr animal) { m_animals.push_back(animal); } // Output the animals and the sound they make void Zoo::showAnimals() const { for (const auto& animal : m_animals) { std::cout << animal->who() << ' ' << animal->sound() << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_01/Zoo.cppm ================================================ // The Zoo class representing a collection of animals export module zoo; import animals; import ; import ; export using AnimalPtr = std::shared_ptr; // Define a type alias for convenience export class Zoo { public: Zoo() = default; // Default constructor for an empty zoo Zoo(const std::vector& new_animals); // Constructor from a vector of animals virtual ~Zoo() = default; // Add a virtual destructor to allow classes to safely derive from Zoo; // possible examples of Zoo specializations include SafariPark, PettingZoo, ... void addAnimal(AnimalPtr animal); // Add an animal to the zoo void showAnimals() const; // Output the animals and the sound they make private: std::vector m_animals; // Stores pointers to the animals }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_02/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal module animals; // Constructor Animal::Animal(std::string_view name, unsigned weight) : m_name{ name }, m_weight{ weight } {} // Return string describing the animal std::string Animal::who() const { return "My name is " + m_name + ". My weight is " + std::to_string(m_weight) + " lbs."; } // Sheep constructors Sheep::Sheep(std::string_view name, unsigned weight) : Sheep{name, weight, static_cast(0.1 * weight)} { } Sheep::Sheep(std::string_view name, unsigned weight, unsigned wool) : Animal{name, weight} , m_wool_weight{wool} { } // Override the behaviour of who() for Sheep to prepend "Woolly " to their name, // and subtract the weight of their wool from weight. std::string Sheep::who() const { return "My name is Woolly " + getName() + ". My weight is " + std::to_string(getWeight() - m_wool_weight) + " lbs."; } // Make like a sheep std::string_view Sheep::sound() const { return "Baaaa!!"; } // Make like a dog std::string_view Dog::sound() const { return "Woof woof!!"; } // Override the behaviour of who() for Cows to hide their weight std::string Cow::who() const { return "My name is " + getName() + "."; } // Make like a cow std::string_view Cow::sound() const { return "Mooooo!!"; } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_02/Animals.cppm ================================================ // Animal classes export module animals; import ; import ; export class Animal { public: Animal(std::string_view name, unsigned weight); // Constructor virtual ~Animal() = default; // Very important: a virtual destructor! virtual std::string who() const; // Return string containing name and weight virtual std::string_view sound() const = 0; // Return the sound of an animal protected: // Protected getters for use in derived classes const std::string& getName() const { return m_name; } unsigned getWeight() const { return m_weight; } private: std::string m_name; // Name of the animal unsigned m_weight; // Weight of the animal }; export class Sheep : public Animal { public: Sheep(std::string_view name, unsigned weight); Sheep(std::string_view name, unsigned weight, unsigned wool); std::string who() const override; // Override the behaviour of who() for Sheep std::string_view sound() const override; // Return the sound of a sheep private: unsigned m_wool_weight; }; export class Dog : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a dog }; export class Cow : public Animal { public: using Animal::Animal; // Inherit constructor std::string who() const override; // Override the behaviour of who() for Cows std::string_view sound() const override; // Return the sound of a cow }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_02/Soln15_02.cpp ================================================ // Exercise 15-2 Exercising Zoo and Animal classes import zoo; import ; // For random number generation import ; // For std::bind() import ; import ; import ; // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_name_options{ 10 }; using NameOptions = std::array; const NameOptions dogNames { "Fido", "Rover" , "Lassie" , "Lambikins", "Poochy", "Spit", "Gnasher", "Samuel" , "Wellington", "Patch" }; const NameOptions sheepNames { "Bozo", "Killer", "Tasty", "Pete", "Chops", "Blackie", "Whitey", "Eric" , "Sean", "Shep" }; const NameOptions cowNames { "Dolly", "Daisy", "Shakey", "Amy", "Dilly", "Dizzy", "Eleanor", "Zippy" , "Zappy", "Happy" }; const unsigned minDogWt{ 1 }; // Minimum weight of a dog in pounds const unsigned maxDogWt{ 120 }; // Maximum weight of a dog in pounds const unsigned minSheepWt{ 80 }; // Minimum weight of a dog in pounds const unsigned maxSheepWt{ 150 }; // Maximum weight of a dog in pounds const unsigned minCowWt{ 800 }; // Minimum weight of a dog in pounds const unsigned maxCowWt{ 1500 }; // Maximum weight of a dog in pounds auto randomAnimalType { createUniformPseudoRandomNumberGenerator(0, 2) }; // 0, 1, or 2 auto randomNameIndex { createUniformPseudoRandomNumberGenerator(0, num_name_options - 1) }; auto randomDogWeight { createUniformPseudoRandomNumberGenerator(minDogWt, maxDogWt) }; auto randomSheepWeight{ createUniformPseudoRandomNumberGenerator(minSheepWt, maxSheepWt) }; auto randomCowWeight { createUniformPseudoRandomNumberGenerator(minCowWt, maxCowWt) }; std::vector animals; // Stores smart pointers to animals size_t numAnimals {}; // Number of animals to be created std::cout << "How many animals in the zoo? "; std::cin >> numAnimals; Zoo zoo; // Create an empty Zoo // Create random animals and add them to the Zoo for (size_t i {}; i < numAnimals; ++i) { switch (randomAnimalType()) { case 0: // Create a sheep zoo.addAnimal(std::make_shared(sheepNames[randomNameIndex()], randomSheepWeight())); break; case 1: // Create a dog zoo.addAnimal(std::make_shared(dogNames[randomNameIndex()], randomDogWeight())); break; case 2: // Create a cow zoo.addAnimal(std::make_shared(cowNames[randomNameIndex()], randomCowWeight())); break; } } zoo.showAnimals(); // Display the animals } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_02/Zoo.cpp ================================================ // Implementations of the Zoo class that stores pointers to Animals module zoo; import animals; import ; import ; // For operator<< import ; // For operator<< // Constructor from a vector of animals Zoo::Zoo(const std::vector& animals) : m_animals{ animals } {} // Add an animal to the zoo void Zoo::addAnimal(AnimalPtr animal) { m_animals.push_back(animal); } // Output the animals and the sound they make void Zoo::showAnimals() const { for (const auto& animal : m_animals) { std::cout << animal->who() << ' ' << animal->sound() << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_02/Zoo.cppm ================================================ // The Zoo class representing a collection of animals export module zoo; import animals; import ; import ; export using AnimalPtr = std::shared_ptr; // Define a type alias for convenience export class Zoo { public: Zoo() = default; // Default constructor for an empty zoo Zoo(const std::vector& new_animals); // Constructor from a vector of animals virtual ~Zoo() = default; // Add a virtual destructor to allow classes to safely derive from Zoo; // possible examples of Zoo specializations include SafariPark, PettingZoo, ... void addAnimal(AnimalPtr animal); // Add an animal to the zoo void showAnimals() const; // Output the animals and the sound they make private: std::vector m_animals; // Stores pointers to the animals }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_03/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal module animals; // Constructor Animal::Animal(std::string_view name, unsigned weight) : m_name{ name }, m_weight{ weight } {} // Return string describing the animal std::string Animal::who() const { return "My name is " + m_name + ". My weight is " + std::to_string(m_weight) + " lbs."; } // Sheep constructors Sheep::Sheep(std::string_view name, unsigned weight) : Sheep{name, weight, static_cast(0.1 * weight)} { } Sheep::Sheep(std::string_view name, unsigned weight, unsigned wool) : Animal{name, weight} , m_wool_weight{wool} { } // Override the behaviour of getName() for Sheep to prepend "Woolly " std::string Sheep::getName() const { return "Woolly " + Animal::getName(); } // Override getWeight() to subtract the weight of their wool from weight. unsigned Sheep::getWeight() const { return Animal::getWeight() - m_wool_weight; } // Make like a sheep std::string_view Sheep::sound() const { return "Baaaa!!"; } // Make like a dog std::string_view Dog::sound() const { return "Woof woof!!"; } // Override the behaviour of who() for Cows to hide their weight std::string Cow::who() const { return "My name is " + getName() + "."; } // Make like a cow std::string_view Cow::sound() const { return "Mooooo!!"; } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_03/Animals.cppm ================================================ // Animal classes export module animals; import ; import ; export class Animal { public: Animal(std::string_view name, unsigned weight); // Constructor virtual ~Animal() = default; // Very important: a virtual destructor! virtual std::string who() const; // Return string containing name and weight virtual std::string_view sound() const = 0; // Return the sound of an animal protected: // Protected getters for use in derived classes virtual std::string getName() const { return m_name; } virtual unsigned getWeight() const { return m_weight; } private: std::string m_name; // Name of the animal unsigned m_weight; // Weight of the animal }; export class Sheep : public Animal { public: Sheep(std::string_view name, unsigned weight); Sheep(std::string_view name, unsigned weight, unsigned wool); std::string_view sound() const override; // Return the sound of a sheep protected: std::string getName() const override; unsigned getWeight() const override; private: unsigned m_wool_weight; }; export class Dog : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a dog }; export class Cow : public Animal { public: using Animal::Animal; // Inherit constructor std::string who() const override; // Override the behaviour of who() for Cows std::string_view sound() const override; // Return the sound of a cow }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_03/Soln15_03.cpp ================================================ // Exercise 15-3 Exercising Zoo and Animal classes import zoo; import ; // For random number generation import ; // For std::bind() import ; import ; import ; // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_name_options{ 10 }; using NameOptions = std::array; const NameOptions dogNames { "Fido", "Rover" , "Lassie" , "Lambikins", "Poochy", "Spit", "Gnasher", "Samuel" , "Wellington", "Patch" }; const NameOptions sheepNames { "Bozo", "Killer", "Tasty", "Pete", "Chops", "Blackie", "Whitey", "Eric" , "Sean", "Shep" }; const NameOptions cowNames { "Dolly", "Daisy", "Shakey", "Amy", "Dilly", "Dizzy", "Eleanor", "Zippy" , "Zappy", "Happy" }; const unsigned minDogWt{ 1 }; // Minimum weight of a dog in pounds const unsigned maxDogWt{ 120 }; // Maximum weight of a dog in pounds const unsigned minSheepWt{ 80 }; // Minimum weight of a dog in pounds const unsigned maxSheepWt{ 150 }; // Maximum weight of a dog in pounds const unsigned minCowWt{ 800 }; // Minimum weight of a dog in pounds const unsigned maxCowWt{ 1500 }; // Maximum weight of a dog in pounds auto randomAnimalType { createUniformPseudoRandomNumberGenerator(0, 2) }; // 0, 1, or 2 auto randomNameIndex { createUniformPseudoRandomNumberGenerator(0, num_name_options - 1) }; auto randomDogWeight { createUniformPseudoRandomNumberGenerator(minDogWt, maxDogWt) }; auto randomSheepWeight{ createUniformPseudoRandomNumberGenerator(minSheepWt, maxSheepWt) }; auto randomCowWeight { createUniformPseudoRandomNumberGenerator(minCowWt, maxCowWt) }; std::vector animals; // Stores smart pointers to animals size_t numAnimals {}; // Number of animals to be created std::cout << "How many animals in the zoo? "; std::cin >> numAnimals; Zoo zoo; // Create an empty Zoo // Create random animals and add them to the Zoo for (size_t i {}; i < numAnimals; ++i) { switch (randomAnimalType()) { case 0: // Create a sheep zoo.addAnimal(std::make_shared(sheepNames[randomNameIndex()], randomSheepWeight())); break; case 1: // Create a dog zoo.addAnimal(std::make_shared(dogNames[randomNameIndex()], randomDogWeight())); break; case 2: // Create a cow zoo.addAnimal(std::make_shared(cowNames[randomNameIndex()], randomCowWeight())); break; } } zoo.showAnimals(); // Display the animals } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_03/Zoo.cpp ================================================ // Implementations of the Zoo class that stores pointers to Animals module zoo; import animals; import ; import ; // For operator<< import ; // For operator<< // Constructor from a vector of animals Zoo::Zoo(const std::vector& animals) : m_animals{ animals } {} // Add an animal to the zoo void Zoo::addAnimal(AnimalPtr animal) { m_animals.push_back(animal); } // Output the animals and the sound they make void Zoo::showAnimals() const { for (const auto& animal : m_animals) { std::cout << animal->who() << ' ' << animal->sound() << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_03/Zoo.cppm ================================================ // The Zoo class representing a collection of animals export module zoo; import animals; import ; import ; export using AnimalPtr = std::shared_ptr; // Define a type alias for convenience export class Zoo { public: Zoo() = default; // Default constructor for an empty zoo Zoo(const std::vector& new_animals); // Constructor from a vector of animals virtual ~Zoo() = default; // Add a virtual destructor to allow classes to safely derive from Zoo; // possible examples of Zoo specializations include SafariPark, PettingZoo, ... void addAnimal(AnimalPtr animal); // Add an animal to the zoo void showAnimals() const; // Output the animals and the sound they make private: std::vector m_animals; // Stores pointers to the animals }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_04/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal /* Because the Animal::m_weight member currently represents the total weight of the animal (we kept this from previous versions of these classes), we need to update the Animal's (total) weight during Sheep::shear(). In an alternate, arguably more elegant solution: - Animal::m_weight could reprent the "true" weight of the animal - Sheep::getWeight() could return Animal::getWeight() + m_wool_weight - Sheep::shear() could simply set m_wool_weight to 0 */ module animals; // Constructor Animal::Animal(std::string_view name, unsigned weight) : m_name{ name }, m_weight{ weight } {} // Return string describing the animal std::string Animal::who() const { return "My name is " + getName() + ". My weight is " + std::to_string(getWeight()) + " lbs."; } void Animal::setWeight(unsigned weight) { m_weight = weight; } // Sheep constructors Sheep::Sheep(std::string_view name, unsigned weight) : Sheep{name, weight, static_cast(0.1 * weight)} { } Sheep::Sheep(std::string_view name, unsigned weight, unsigned wool) : Animal{name, weight} , m_wool_weight{wool} { } // Override the behaviour of getName() for Sheep to prepend "Woolly " std::string Sheep::getName() const { return "Woolly " + Animal::getName(); } // Override getWeight() to subtract the weight of their wool from weight. unsigned Sheep::getWeight() const { return Animal::getWeight() - m_wool_weight; } unsigned Sheep::shear() { // Somewhat odd statement that updates the total weight (stored in the Animal subobject) // to the actual weight of the sheep (see Sheep::getWeight()). // Of course, we do this *before* setting the wool's weight to 0. setWeight(getWeight()); const unsigned wool_weight{ m_wool_weight }; m_wool_weight = 0; return wool_weight; // Alternative: use std::exchange() (see Exercise 18-5) //return std::exchange(m_wool_weight, 0); } // Make like a sheep std::string_view Sheep::sound() const { return "Baaaa!!"; } // Make like a dog std::string_view Dog::sound() const { return "Woof woof!!"; } // Override the behaviour of who() for Cows to hide their weight std::string Cow::who() const { return "My name is " + getName() + "."; } // Make like a cow std::string_view Cow::sound() const { return "Mooooo!!"; } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_04/Animals.cppm ================================================ // Animal classes export module animals; import ; import ; export class Animal { public: Animal(std::string_view name, unsigned weight); // Constructor virtual ~Animal() = default; // Very important: a virtual destructor! virtual std::string who() const; // Return string containing name and weight virtual std::string_view sound() const = 0; // Return the sound of an animal protected: // Protected getters for use in derived classes virtual std::string getName() const { return m_name; } virtual unsigned getWeight() const { return m_weight; } void setWeight(unsigned weight); private: std::string m_name; // Name of the animal unsigned m_weight; // Weight of the animal }; export class Sheep : public Animal { public: Sheep(std::string_view name, unsigned weight); Sheep(std::string_view name, unsigned weight, unsigned wool); std::string_view sound() const override; // Return the sound of a sheep unsigned shear(); protected: std::string getName() const override; unsigned getWeight() const override; private: unsigned m_wool_weight; }; export class Dog : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a dog }; export class Cow : public Animal { public: using Animal::Animal; // Inherit constructor std::string who() const override; // Override the behaviour of who() for Cows std::string_view sound() const override; // Return the sound of a cow }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_04/Soln15_04.cpp ================================================ // Exercise 15-4 Exercising Zoo and Animal classes import zoo; import ; // For random number generation import ; // For std::bind() import ; import ; import ; // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_name_options{ 10 }; using NameOptions = std::array; const NameOptions dogNames { "Fido", "Rover" , "Lassie" , "Lambikins", "Poochy", "Spit", "Gnasher", "Samuel" , "Wellington", "Patch" }; const NameOptions sheepNames { "Bozo", "Killer", "Tasty", "Pete", "Chops", "Blackie", "Whitey", "Eric" , "Sean", "Shep" }; const NameOptions cowNames { "Dolly", "Daisy", "Shakey", "Amy", "Dilly", "Dizzy", "Eleanor", "Zippy" , "Zappy", "Happy" }; const unsigned minDogWt{ 1 }; // Minimum weight of a dog in pounds const unsigned maxDogWt{ 120 }; // Maximum weight of a dog in pounds const unsigned minSheepWt{ 80 }; // Minimum weight of a dog in pounds const unsigned maxSheepWt{ 150 }; // Maximum weight of a dog in pounds const unsigned minCowWt{ 800 }; // Minimum weight of a dog in pounds const unsigned maxCowWt{ 1500 }; // Maximum weight of a dog in pounds auto randomAnimalType { createUniformPseudoRandomNumberGenerator(0, 2) }; // 0, 1, or 2 auto randomNameIndex { createUniformPseudoRandomNumberGenerator(0, num_name_options - 1) }; auto randomDogWeight { createUniformPseudoRandomNumberGenerator(minDogWt, maxDogWt) }; auto randomSheepWeight{ createUniformPseudoRandomNumberGenerator(minSheepWt, maxSheepWt) }; auto randomCowWeight { createUniformPseudoRandomNumberGenerator(minCowWt, maxCowWt) }; std::vector animals; // Stores smart pointers to animals size_t numAnimals {}; // Number of animals to be created std::cout << "How many animals in the zoo? "; std::cin >> numAnimals; Zoo zoo; // Create an empty Zoo // Create random animals and add them to the Zoo for (size_t i {}; i < numAnimals; ++i) { switch (randomAnimalType()) { case 0: // Create a sheep zoo.addAnimal(std::make_shared(sheepNames[randomNameIndex()], randomSheepWeight())); break; case 1: // Create a dog zoo.addAnimal(std::make_shared(dogNames[randomNameIndex()], randomDogWeight())); break; case 2: // Create a cow zoo.addAnimal(std::make_shared(cowNames[randomNameIndex()], randomCowWeight())); break; } } zoo.showAnimals(); // Display the animals std::cout << "\nHerding and shearing all sheep..." << std::endl; for (auto* sheep : zoo.herd()) { sheep->shear(); } zoo.showAnimals(); // Display the animals again } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_04/Zoo.cpp ================================================ // Implementations of the Zoo class that stores pointers to Animals module zoo; import animals; import ; import ; // For operator<< import ; // For operator<< // Constructor from a vector of animals Zoo::Zoo(const std::vector& animals) : m_animals{ animals } {} // Add an animal to the zoo void Zoo::addAnimal(AnimalPtr animal) { m_animals.push_back(animal); } // Output the animals and the sound they make void Zoo::showAnimals() const { for (const auto& animal : m_animals) { std::cout << animal->who() << ' ' << animal->sound() << std::endl; } } // Collect all Sheep in the Zoo using dynamic cast (the recommended way) std::vector Zoo::herd() const { std::vector sheep; for (auto animal : m_animals) { auto* casted{ dynamic_cast(animal.get()) }; if (casted) { sheep.push_back(casted); } } return sheep; } /* // Collect all Sheep in the Zoo using the typeid() operator and a static cast // (for the sake of the exercise only) import ; // required when using typeid() std::vector Zoo::herd() const { std::vector sheep; for (auto animal : m_animals) { if (typeid(*animal) == typeid(Sheep)) { sheep.push_back(static_cast(animal.get())); } } return sheep; } */ ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_04/Zoo.cppm ================================================ // The Zoo class representing a collection of animals export module zoo; import animals; import ; import ; export using AnimalPtr = std::shared_ptr; // Define a type alias for convenience export class Zoo { public: Zoo() = default; // Default constructor for an empty zoo Zoo(const std::vector& new_animals); // Constructor from a vector of animals virtual ~Zoo() = default; // Add a virtual destructor to allow classes to safely derive from Zoo; // possible examples of Zoo specializations include SafariPark, PettingZoo, ... void addAnimal(AnimalPtr animal); // Add an animal to the zoo void showAnimals() const; // Output the animals and the sound they make std::vector herd() const; // Collect all Sheep in the Zoo private: std::vector m_animals; // Stores pointers to the animals }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_05/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal /* Because the Animal::m_weight member currently represents the total weight of the animal (we kept this from previous versions of these classes), we need to update the Animal's (total) weight during Sheep::shear(). In an alternate, arguably more elegant solution: - Animal::m_weight could reprent the "true" weight of the animal - Sheep::getWeight() could return Animal::getWeight() + m_wool_weight - Sheep::shear() could simply set m_wool_weight to 0 */ module animals; // Constructor Animal::Animal(std::string_view name, unsigned weight) : m_name{ name }, m_weight{ weight } {} // Return string describing the animal std::string Animal::who() const { return "My name is " + getName() + ". My weight is " + std::to_string(getWeight()) + " lbs."; } void Animal::setWeight(unsigned weight) { m_weight = weight; } // Sheep constructors Sheep::Sheep(std::string_view name, unsigned weight) : Sheep{name, weight, static_cast(0.1 * weight)} { } Sheep::Sheep(std::string_view name, unsigned weight, unsigned wool) : Animal{name, weight} , m_wool_weight{wool} { } // Override the behaviour of getName() for Sheep to prepend "Woolly " std::string Sheep::getName() const { return "Woolly " + Animal::getName(); } // Override getWeight() to subtract the weight of their wool from weight. unsigned Sheep::getWeight() const { return Animal::getWeight() - m_wool_weight; } unsigned Sheep::shear() { // Somewhat odd statement that updates the total weight (stored in the Animal subobject) // to the actual weight of the sheep (see Sheep::getWeight()). // Of course, we do this *before* setting the wool's weight to 0. setWeight(getWeight()); const unsigned wool_weight{ m_wool_weight }; m_wool_weight = 0; return wool_weight; // Alternative: use std::exchange() (see Exercise 18-5) //return std::exchange(m_wool_weight, 0); } // Make like a sheep std::string_view Sheep::sound() const { return "Baaaa!!"; } // Make like a dog std::string_view Dog::sound() const { return "Woof woof!!"; } // Override the behaviour of who() for Cows to hide their weight std::string Cow::who() const { return "My name is " + getName() + "."; } // Make like a cow std::string_view Cow::sound() const { return "Mooooo!!"; } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_05/Animals.cppm ================================================ // Animal classes export module animals; import ; import ; export class Animal { public: Animal(std::string_view name, unsigned weight); // Constructor virtual ~Animal() = default; // Very important: a virtual destructor! virtual std::string who() const; // Return string containing name and weight virtual std::string_view sound() const = 0; // Return the sound of an animal protected: // Protected getters for use in derived classes virtual std::string getName() const { return m_name; } virtual unsigned getWeight() const { return m_weight; } void setWeight(unsigned weight); private: std::string m_name; // Name of the animal unsigned m_weight; // Weight of the animal }; export class Sheep : public Animal { public: Sheep(std::string_view name, unsigned weight); Sheep(std::string_view name, unsigned weight, unsigned wool); std::string_view sound() const override; // Return the sound of a sheep unsigned shear(); protected: std::string getName() const override; unsigned getWeight() const override; private: unsigned m_wool_weight; }; export class Dog : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a dog }; export class Cow : public Animal { public: using Animal::Animal; // Inherit constructor std::string who() const override; // Override the behaviour of who() for Cows std::string_view sound() const override; // Return the sound of a cow }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_05/Soln15_05.cpp ================================================ // Exercise 15-5 Exercising Zoo and Animal classes import zoo; import ; // For random number generation import ; // For std::bind() import ; import ; import ; // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_name_options{ 10 }; using NameOptions = std::array; const NameOptions dogNames { "Fido", "Rover" , "Lassie" , "Lambikins", "Poochy", "Spit", "Gnasher", "Samuel" , "Wellington", "Patch" }; const NameOptions sheepNames { "Bozo", "Killer", "Tasty", "Pete", "Chops", "Blackie", "Whitey", "Eric" , "Sean", "Shep" }; const NameOptions cowNames { "Dolly", "Daisy", "Shakey", "Amy", "Dilly", "Dizzy", "Eleanor", "Zippy" , "Zappy", "Happy" }; const unsigned minDogWt{ 1 }; // Minimum weight of a dog in pounds const unsigned maxDogWt{ 120 }; // Maximum weight of a dog in pounds const unsigned minSheepWt{ 80 }; // Minimum weight of a dog in pounds const unsigned maxSheepWt{ 150 }; // Maximum weight of a dog in pounds const unsigned minCowWt{ 800 }; // Minimum weight of a dog in pounds const unsigned maxCowWt{ 1500 }; // Maximum weight of a dog in pounds auto randomAnimalType { createUniformPseudoRandomNumberGenerator(0, 2) }; // 0, 1, or 2 auto randomNameIndex { createUniformPseudoRandomNumberGenerator(0, num_name_options - 1) }; auto randomDogWeight { createUniformPseudoRandomNumberGenerator(minDogWt, maxDogWt) }; auto randomSheepWeight{ createUniformPseudoRandomNumberGenerator(minSheepWt, maxSheepWt) }; auto randomCowWeight { createUniformPseudoRandomNumberGenerator(minCowWt, maxCowWt) }; std::vector animals; // Stores smart pointers to animals size_t numAnimals {}; // Number of animals to be created std::cout << "How many animals in the zoo? "; std::cin >> numAnimals; Zoo zoo; // Create an empty Zoo // Create random animals and add them to the Zoo for (size_t i {}; i < numAnimals; ++i) { switch (randomAnimalType()) { case 0: // Create a sheep zoo.addAnimal(std::make_shared(sheepNames[randomNameIndex()], randomSheepWeight())); break; case 1: // Create a dog zoo.addAnimal(std::make_shared(dogNames[randomNameIndex()], randomDogWeight())); break; case 2: // Create a cow zoo.addAnimal(std::make_shared(cowNames[randomNameIndex()], randomCowWeight())); break; } } zoo.showAnimals(); // Display the animals std::cout << "\nHerding and shearing all sheep..." << std::endl; for (auto sheep : zoo.herd()) { sheep->shear(); } zoo.showAnimals(); // Display the animals again } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_05/Zoo.cpp ================================================ // Implementations of the Zoo class that stores pointers to Animals module zoo; import animals; import ; import ; // For operator<< import ; // For operator<< // Constructor from a vector of animals Zoo::Zoo(const std::vector& animals) : m_animals{ animals } {} // Add an animal to the zoo void Zoo::addAnimal(AnimalPtr animal) { m_animals.push_back(animal); } // Output the animals and the sound they make void Zoo::showAnimals() const { for (const auto& animal : m_animals) { std::cout << animal->who() << ' ' << animal->sound() << std::endl; } } // Collect all Sheep in the Zoo using dynamic cast (the recommended way) std::vector Zoo::herd() const { std::vector sheep; for (auto animal : m_animals) { auto casted{ std::dynamic_pointer_cast(animal) }; if (casted) { sheep.push_back(casted); } } return sheep; } ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_05/Zoo.cppm ================================================ // The Zoo class representing a collection of animals export module zoo; import animals; import ; import ; export using AnimalPtr = std::shared_ptr; // Define a type alias for convenience export using SheepPtr = std::shared_ptr; export class Zoo { public: Zoo() = default; // Default constructor for an empty zoo Zoo(const std::vector& new_animals); // Constructor from a vector of animals virtual ~Zoo() = default; // Add a virtual destructor to allow classes to safely derive from Zoo; // possible examples of Zoo specializations include SafariPark, PettingZoo, ... void addAnimal(AnimalPtr animal); // Add an animal to the zoo void showAnimals() const; // Output the animals and the sound they make std::vector herd() const; // Collect all Sheep in the Zoo private: std::vector m_animals; // Stores pointers to the animals }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_06/Point.cppm ================================================ // A simple class for 2-dimensional points export module point; export class Point final { public: Point(double x, double y) : m_x{x}, m_y{y} {} double getX() const { return m_x; } double getY() const { return m_y; } void setX(double x) { m_x = x; } void setY(double y) { m_y = y; } private: double m_x; double m_y; }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_06/Shapes.cppm ================================================ // Shape classes export module shapes; import point; import ; // Generic base class for shapes export class Shape { public: Shape(const Point& position) : m_position {position} {} virtual ~Shape() = default; // Remember: always use virtual destructors for base classes! virtual double area() const = 0; // Pure virtual function to compute a shape's area virtual double perimeter() const = 0; // Pure virtual function to compute a shape's perimeter virtual void scale(double factor) = 0; // Pure virtual function to scale a shape // Regular virtual function to move a shape virtual void move(const Point& position) { m_position = position; }; private: Point m_position; // Position of a shape }; // Class defining a circle export class Circle : public Shape { public: Circle(const Point& center, double radius) : Shape{center}, m_radius{radius} {} double area() const override { return m_radius * m_radius * std::numbers::pi; } double perimeter() const override { return 2 * std::numbers::pi * m_radius; } void scale(double factor) override { m_radius *= factor; } private: double m_radius; // Radius of a circle }; // Class defining a rectangle export class Rectangle : public Shape { public: Rectangle(const Point& center, double rectWidth, double rectHeight) : Shape{center}, m_width{rectWidth}, m_height{rectHeight} {} double area() const override { return m_width * m_height; } double perimeter() const override { return 2 * (m_width + m_height); } void scale(double factor) override { m_width *= factor; m_height *= factor; } private: double m_width; // Width of a rectangle double m_height; // Height of a rectangle }; ================================================ FILE: Exercises/Modules/Chapter 15/Soln15_06/Soln15_06.cpp ================================================ // Exercise 15-6 Exercising Shape classes import shapes; import point; import ; import ; double calculateSumAreas(const std::vector& shapes); double calculateSumPerimeters(const std::vector& shapes); void printSums(const std::vector& shapes); int main() { Circle c1{ Point{1, 1}, 1 }; Circle c2{ Point{2, 2}, 2 }; Circle c3{ Point{3, 3}, 3 }; Rectangle r1{ {4, 5}, 4, 5 }; // Shorter notation (omitted Point type) could of course be used for Circles as well! Rectangle r2{ {6, 7}, 6, 7 }; Rectangle r3{ {8, 9}, 8, 9 }; std::vector shapes{ &c1, &r1, &r2, &c2, &c3, &r3 }; printSums(shapes); for (auto* shape : shapes) shape->scale(1.5); std::cout << std::endl; printSums(shapes); } double calculateSumAreas(const std::vector& shapes) { double sum {}; for (auto* shape : shapes) { sum += shape->area(); } return sum; } double calculateSumPerimeters(const std::vector& shapes) { double sum {}; for (auto* shape : shapes) { sum += shape->perimeter(); } return sum; } void printSums(const std::vector& shapes) { std::cout << "Sum of areas: " << calculateSumAreas(shapes) << std::endl; std::cout << "Sum of perimeters: " << calculateSumPerimeters(shapes) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 16/Exer16_06/Customer.cpp ================================================ // A simple C++ customer class module customer; Customer::Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city) : m_surname{ surname } , m_name{ name } , m_street{ street } , m_streetNumber{ streetNumber } , m_city{ city } {} std::string Customer::toString() const { std::string result; result += m_surname; result += ' '; result += m_name; result += ", "; result += m_street; result += ' '; result += std::to_string(m_streetNumber); result += ", "; result += m_city; return result; } ================================================ FILE: Exercises/Modules/Chapter 16/Exer16_06/Customer.cppm ================================================ // A simple C++ customer class export module customer; import ; import ; export class Customer { public: Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city ); std::string toString() const; private: std::string m_surname; std::string m_name; std::string m_street; int m_streetNumber; std::string m_city; }; ================================================ FILE: Exercises/Modules/Chapter 16/Exer16_06/DB.cpp ================================================ // A mocked database #include "DB.h" #include // For std::strcmp() #include #include namespace { struct QueryResult { std::vector> data; size_t index {}; }; class Database { public: Database() = default; bool hasConnection() const { return m_connected; } void connect() { m_connected = true; } void disconnect() { m_connected = false; } QueryResult* query(const char* query); private: bool m_connected{}; }; } DB_CONNECTION* db_connect() { // We only have one single database, which allows only one single connection: static Database theDatabase; if (theDatabase.hasConnection()) { return nullptr; } else { theDatabase.connect(); return &theDatabase; } } void db_disconnect(DB_CONNECTION* connection) { // reinterpret_cast<> is used to cast between pointers/references of // unrelated types (such as void* and Database*) reinterpret_cast(connection)->disconnect(); } DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query) { return reinterpret_cast(connection)->query(query); } QueryResult* Database::query(const char* query) { if (!hasConnection()) { return nullptr; } // Our database only understands one single query! if (std::strcmp(query, "SELECT * FROM CUSTOMER_TABEL") != 0) { return nullptr; } auto result{ std::make_unique() }; result->data = std::vector>{ { "Sherlock", "Holmes", "Baker Street", "221", "London" }, { "Joe", "Biden", "Pennsylvania Avenue", "1600", "Washington DC" }, { "Donald", "Duck", "Webfoot Walk", "1313", "Duckville" }, { "Sirius", "Black", "Grimmauld Place", "12", "London" }, { "Nemo", "Clownfish", "Wallaby Way", "42", "Sydney" }, { "Sam", "Malone", "Beacon Street", "112", "Boston" } }; return result.release(); } int db_num_fields(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->data.empty()) { return -1; } else { return static_cast(theResult->data.front().size()); } } DB_ROW db_fetch_row(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->index >= theResult->data.size()) { return nullptr; } else { return theResult->data[theResult->index++].data(); } } void db_free_result(DB_QUERY_RESULT* result) { delete reinterpret_cast(result); } ================================================ FILE: Exercises/Modules/Chapter 16/Exer16_06/DB.h ================================================ // A C-style interface to a mock database // (a simplified subset of the MySQL C interface) #ifndef DB_INCLUDES #define DB_INCLUDES using DB_CONNECTION = void; using DB_QUERY_RESULT = void; using DB_ROW = const char**; /*! Make a connection to the database * Do not forget to close the database connection (db_disconnect()) once done with it. * \return a DB_CONNECTION handle; nullptr upon failure */ DB_CONNECTION* db_connect(); /*! Query the database * \param[in] connection A database connection handle returned by db_connect() * \param[in] query A SQL query * \return A handle to the result of the query (memory to be freed using db_free_result()!) */ DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query); /*! Retrieve the number of fields per row stored by the given query result * \param[in] result A handle returned by db_query * \return The number of fields per row stored by the result; -1 upon failure */ int db_num_fields(DB_QUERY_RESULT* result); /*! Fetch a single row from the result. * \param[in] result A handle returned by db_query * \return An array of strings. Each field is represented as a string (zero-terminated char array). * Let row be the result, then the first field is accessed using result[0]. */ DB_ROW db_fetch_row(DB_QUERY_RESULT* result); /*! Release the memory allocated for the result. * \param[in] result A handle returned by db_query() */ void db_free_result(DB_QUERY_RESULT* result); /*! Disconnect from the database * \param[in] connection A connection handle returned by db_connect() */ void db_disconnect(DB_CONNECTION* connection); #endif ================================================ FILE: Exercises/Modules/Chapter 16/Exer16_06/DBException.cppm ================================================ // A simple C++ exception type export module db_exception; import ; export class DatabaseException : public std::runtime_error { public: using std::runtime_error::runtime_error; // Inherit constructor }; ================================================ FILE: Exercises/Modules/Chapter 16/Exer16_06/Exer16_06.cpp ================================================ /* Creating RAII classes to manage resource handles returned by a C interface Remember: RAII is not just for dynamic memory: every resource should be managed by an object! */ import ; import ; #include "DB.h" import db_exception; import customer; void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream int main() { auto* connection{ db_connect() }; try { auto* result{ db_query(connection, "SELECT * FROM CUSTOMER_TABEL") }; if (!result) { db_disconnect(connection); throw DatabaseException{"Query failed"}; } std::vector customers{ readCustomers(result) }; if (customers.empty()) { std::cerr << "No customers found?" << std::endl; return 2; } for (auto& customer : customers) { print(std::cout, customer); } db_free_result(result); } catch (std::exception& caught) { std::cerr << caught.what() << std::endl; return 1; } db_disconnect(connection); return 0; } std::vector readCustomers(DB_QUERY_RESULT* result) { // Sanity check // (if the number of fields does not match 5, // the code below would crash!) verifyCustomerFields(result); std::vector customers; auto row{ db_fetch_row(result) }; while (row) { customers.push_back(Customer{ row[0], // Surname row[1], // Name row[2], // Street std::stoi(row[3]), // Street number row[4] // City }); row = db_fetch_row(result); } return customers; } void verifyCustomerFields(DB_QUERY_RESULT* result) { const int numFields{ db_num_fields(result) }; if (numFields < 0) { throw DatabaseException{"db_num_fields() failed"}; } if (numFields != 5) { throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; } } void print(std::ostream& stream, const Customer& customer) { stream << customer.toString() << std::endl; if (std::cout.fail()) { std::cout.clear(); throw std::runtime_error("Failed to output customer"); } } ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_01/Curveball.cppm ================================================ // Definition of Curveball exception class export module curveball; import ; export class Curveball : public std::exception { public: const char* what() const noexcept override { return "Curveball exception"; } }; ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_01/Soln16_01.cpp ================================================ // Throwing and catching Curveballs import curveball; import ; import ; // For random number generation import ; // For std::bind() void throwCurveballSometimes(); // Suggested solution void throwCurveballSometimesBernouilli(); // Alternate solution int main() { size_t number {1000}; // Number of loop iterations size_t exceptionCount {}; // Count of exceptions thrown for (size_t i {}; i < number; ++i) { try { throwCurveballSometimes(); // throwCurveballSometimesBernouilli(); } catch (const Curveball&) { exceptionCount++; } } std::cout << "Curveball exception thrown " << exceptionCount << " times out of " << number << ".\n"; } // See Chapter 12 for an explanation of this function principle. auto createUniformPseudoRandomNumberGenerator(double min, double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ min, max }; // Generate in [min, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } // Throw a Curveball exception 25% of the time void throwCurveballSometimes() { static auto random{ createUniformPseudoRandomNumberGenerator(0, 100) }; if (random() < 25) throw Curveball{}; } /* Alternate solution: instead of generating numbers in the interval [0,100) using a std::uniform_real_distribution, and comparing against 25, you could also generate Boolean values directly using a std::bernoulli_distribution (see https://en.cppreference.com/w/cpp/numeric/random/bernoulli_distribution; named after https://en.wikipedia.org/wiki/Bernoulli_distribution): */ auto createUniformPseudoRandomBooleanGenerator(double probabilityOfTrue) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::bernoulli_distribution distribution{ probabilityOfTrue }; // The name says it all... return std::bind(distribution, generator); //... and in the darkness bind them! } // Throw a Curveball exception 25% of the time void throwCurveballSometimesBernouilli() { static auto random{ createUniformPseudoRandomBooleanGenerator(0.25) }; if (random()) throw Curveball{}; } ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_02/Curveball.cppm ================================================ // Definition of Curveball exception class export module curveball; import ; export class Curveball : public std::exception { public: const char* what() const noexcept override { return "Curveball exception"; } }; ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_02/Soln16_02.cpp ================================================ // Throwing and catching Curveballs and throwing TooManyExceptions import curveball; import too_many; import ; import ; // For random number generation import ; // For std::bind() void throwCurveballSometimes(); // This program will terminate abnormally when the TooManyExceptions exception is thrown. int main() { size_t number{ 1000 }; // Number of loop iterations size_t exceptionCount {}; // Count of exceptions thrown const size_t maxExceptions{ 10 }; // Maximum number of exceptions for (size_t i {}; i < number; ++i) { try { throwCurveballSometimes(); } catch(Curveball& e) { std::cout << e.what() << std::endl; if (++exceptionCount > maxExceptions) throw TooManyExceptions{ maxExceptions }; } } } // See Soln16_01 (to generate Booleans, a bernoulli_distribution is actually most appropriate) auto createUniformPseudoRandomBooleanGenerator(double probabilityOfTrue) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::bernoulli_distribution distribution{ probabilityOfTrue }; // The name says it all... return std::bind(distribution, generator); //... and in the darkness bind them! } // Throw a Curveball exception 25% of the time void throwCurveballSometimes() { static auto random{ createUniformPseudoRandomBooleanGenerator(0.25) }; if (random()) throw Curveball{}; } ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_02/TooManyExceptions.cppm ================================================ // Definition of TooManyExceptions exception class // We added an extra "how many" members to illustrate derived // exceptions can carry extra information regarding their cause. // Notice how the constructor is explicit (implicit conversions // from size_t to TooManyExceptions are not desired). export module too_many; import ; import ; // For std::string / std::to_string() export class TooManyExceptions : public std::exception { public: explicit TooManyExceptions(size_t howMany) : m_how_many{ howMany } , m_message{ "Too many exceptions occurred: " + std::to_string(m_how_many) } {} const char* what() const noexcept override { return m_message.c_str(); } size_t howMany() const noexcept { return m_how_many; } private: size_t m_how_many; std::string m_message; }; ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_03/Box.cppm ================================================ export module box; import ; import ; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.m_length, box.m_width, box.m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_03/RandomBoxes.cppm ================================================ export module box.random; import box; import ; // For random number generation import ; // For std::bind() import ; // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } export Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } export auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_03/Soln16_03.cpp ================================================ // Using exceptions to signal index-out-of-bounds errors. import ; import ; import truckload; import box.random; int main() { Truckload load; const size_t boxCount {20}; // Number of Box object to be created // Create boxCount Box objects for (size_t i {}; i < boxCount; ++i) load.addBox(randomSharedBox()); try { std::cout << "The truckload contains the following boxes: " << std::endl; for (size_t i {}; i < 100; ++i) { std::cout << *load[i] << std::endl; } } catch (const std::exception& caughtException) { std::cerr << "Oops: " << caughtException.what() << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_03/Truckload.cpp ================================================ module truckload; import ; import ; // For standard exception type std::out_of_range import ; // For std::string and std::to_string() // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } Truckload& Truckload::operator=(const Truckload& other) { if (&other != this) // Do not forget: avoid issues with self-assignment! { delete m_head; // Delete all current packages m_head = m_tail = nullptr; // Reset both pointers // Same as copy constructor // (see Chapter 17 for the copy-and-swap idiom that allows you to avoid // duplicating logic like this...) for (Package* package{ other.m_head }; package; package = package->m_next) { addBox(package->m_box); } } return *this; } // Destructor: clean up the list Truckload::~Truckload() { delete m_head; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox& Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } throw std::out_of_range{ "Index too large: " + std::to_string(index) }; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_03/Truckload.cppm ================================================ export module truckload; import box; import ; import ; import ; export using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor Truckload& operator=(const Truckload& other); // Copy assignment operator ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in a module interface file) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; export std::ostream& operator<<(std::ostream& stream, const Truckload& load); ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_04/Soln16_04.cpp ================================================ // Throwing and catching standard exceptions import ; import ; import ; import ; import ; import ; /* This solution triggers all exceptions mentioned in the text accompanying the Table with all Standard Library exceptions, in order. To know how to trigger any other type, you will have to consult your Standard Library reference. Note: while you'll typically catch exceptions by reference-to-const-std::exception (unless more specific handling for concrete types is required, of course), we specify the concrete exception type instead here for clarity. */ // First, some dummy class types... class BaseClass { public: virtual ~BaseClass() = default; }; class DerivedClass1 : public BaseClass {}; class DerivedClass2 : public BaseClass {}; int main() { try { std::vector v{ 1, 2, 3, 4, 5 }; std::cout << v.at(10) << std::endl; } catch (const std::out_of_range& exception) { std::cout << "std::out_of_range: " << exception.what() << std::endl; } try { std::cout << std::format("Hello {:g}!\n", "World"); } catch (const std::format_error& exception) { std::cout << "std::format_error: " << exception.what() << std::endl; } try { // Remember: a polymorphic class is a class with at least one virtual function. BaseClass* polymorphic{ nullptr }; std::cout << typeid(*polymorphic).name(); } catch (const std::bad_typeid& exception) { std::cout << "std::bad_typeid: " << exception.what() << std::endl; } try { DerivedClass1 derived; BaseClass& reference_to_base{ derived }; dynamic_cast(reference_to_base); } catch (const std::bad_cast& exception) { std::cout << "std::bad_cast: " << exception.what() << std::endl; } try { std::optional empty; std::cout << empty.value() << std::endl; } catch (const std::bad_optional_access& exception) { std::cout << "std::bad_optional_access: " << exception.what() << std::endl; } try { int size{ -1 }; new int[size]; } catch (const std::bad_alloc& exception) { std::cout << "std::bad_alloc: " << exception.what() << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_05/Curveball.cppm ================================================ // Definition of Curveball exception class export module curveball; import ; export class Curveball : public std::exception { public: const char* what() const noexcept override { return "Curveball exception"; } }; ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_05/DomainExceptions.cppm ================================================ // Definitions of various domain exception classes export module domain_exceptions; import ; import ; /* std::domain_error is one of the exception types defined by the Standard Libray. It is intended to be used mostly inside mathematical functions in case an argument is provided for which the function is not defined (for instance, should a regular square root function be called with a negative number) */ export class NotANumber : public std::domain_error { public: explicit NotANumber(const std::string& nan) : std::domain_error{"Not a number: " + nan} {} }; export class NegativeNumber : public std::domain_error { public: explicit NegativeNumber(int number) : std::domain_error{"A negative number was entered: " + std::to_string(number)} {} }; export class OddNumber : public std::domain_error { public: explicit OddNumber(int number) : std::domain_error{"An odd number was entered: " + std::to_string(number)} {} }; ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_05/Soln16_05.cpp ================================================ // Exercise 16-5 /* Somewhat artificial example to practice lists of different, * related exception types (mind the order + catch-by-reference!) * and rethrowing (catch-by-reference + "throw;" !) */ import ; import ; // For random number generation import ; // For std::bind() import ; import ; import curveball; import domain_exceptions; void askEvenNumber(); // Ask the user to provide an even number int main() { try { askEvenNumber(); } catch (const Curveball& /*caught*/) { std::cerr << "...hit it out of the park!" << std::endl; } } /* Helper functions for askEvenNumber() */ void throwCurveballSometimes(); // Throw a Curveball exception 25% of the time int readEvenNumber(); // Reads an even number from std::cin and verifies the input // (throws upon failure) // Option 1: use recursion void askEvenNumber() { try { std::cout << "Please enter an even number: "; const int read = readEvenNumber(); std::cout << std::format("Well done. {} is a beautiful even number. Thank you!\n", read); } catch (const NotANumber& nan) { std::cerr << nan.what() << std::endl; return; } catch (const std::domain_error& domainException) { std::cerr << domainException.what() << std::endl; askEvenNumber(); // Recursive call } catch (const std::exception& exception) { std::cerr << exception.what() << std::endl; throw; } } /* // Option 2: use a loop void askEvenNumber() { while (true) { try { std::cout << "Please enter an even number: "; const int read = readEvenNumber(); std::cout << std::format("Well done. {} is a beautiful even number. Thank you!\n", read); break; } catch (const NotANumber& nan) { std::cerr << nan.what() << std::endl; return; } catch (const std::out_of_range& range) { std::cerr << range.what() << std::endl; } catch (const std::exception& exception) { std::cerr << exception.what() << std::endl; throw; } } } */ int readEvenNumber() { int number; std::cin >> number; if (std::cin.fail()) // Check whether the user has effectively entered a number { std::cin.clear(); // Reset the stream's failure state std::string line; // Read the erroneous input and discard it std::getline(std::cin, line); throw NotANumber{line}; } throwCurveballSometimes(); if (number < 0) throw NegativeNumber{number}; if (number % 2) throw OddNumber{number}; return number; } // See Soln16_01 for an explanation of this function auto createUniformPseudoRandomBooleanGenerator(double probabilityOfTrue) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::bernoulli_distribution distribution{ probabilityOfTrue }; // The name says it all... return std::bind(distribution, generator); //... and in the darkness bind them! } // Throw a Curveball exception 25% of the time void throwCurveballSometimes() { static auto random{ createUniformPseudoRandomBooleanGenerator(0.25) }; if (random()) throw Curveball{}; } ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_06/Customer.cpp ================================================ // A simple C++ customer class module customer; Customer::Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city) : m_surname{ surname } , m_name{ name } , m_street{ street } , m_streetNumber{ streetNumber } , m_city{ city } {} std::string Customer::toString() const { std::string result; result += m_surname; result += ' '; result += m_name; result += ", "; result += m_street; result += ' '; result += std::to_string(m_streetNumber); result += ", "; result += m_city; return result; } ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_06/Customer.cppm ================================================ // A simple C++ customer class export module customer; import ; import ; export class Customer { public: Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city ); std::string toString() const; private: std::string m_surname; std::string m_name; std::string m_street; int m_streetNumber; std::string m_city; }; ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_06/DB.cpp ================================================ // A mocked database #include "DB.h" #include // For std::strcmp() import ; import ; namespace { struct QueryResult { std::vector> data; size_t index {}; }; class Database { public: Database() = default; bool hasConnection() const { return m_connected; } void connect() { m_connected = true; } void disconnect() { m_connected = false; } QueryResult* query(const char* query); private: bool m_connected{}; }; } DB_CONNECTION* db_connect() { // We only have one single database, which allows only one single connection: static Database theDatabase; if (theDatabase.hasConnection()) { return nullptr; } else { theDatabase.connect(); return &theDatabase; } } void db_disconnect(DB_CONNECTION* connection) { // reinterpret_cast<> is used to cast between pointers/references of // unrelated types (such as void* and Database*) reinterpret_cast(connection)->disconnect(); } DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query) { return reinterpret_cast(connection)->query(query); } QueryResult* Database::query(const char* query) { if (!hasConnection()) { return nullptr; } // Our database only understands one single query! if (std::strcmp(query, "SELECT * FROM CUSTOMER_TABEL") != 0) { return nullptr; } auto result{ std::make_unique() }; result->data = std::vector>{ { "Sherlock", "Holmes", "Baker Street", "221", "London" }, { "Joe", "Biden", "Pennsylvania Avenue", "1600", "Washington DC" }, { "Donald", "Duck", "Webfoot Walk", "1313", "Duckville" }, { "Sirius", "Black", "Grimmauld Place", "12", "London" }, { "Nemo", "Clownfish", "Wallaby Way", "42", "Sydney" }, { "Sam", "Malone", "Beacon Street", "112", "Boston" } }; return result.release(); } int db_num_fields(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->data.empty()) { return -1; } else { return static_cast(theResult->data.front().size()); } } DB_ROW db_fetch_row(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->index >= theResult->data.size()) { return nullptr; } else { return theResult->data[theResult->index++].data(); } } void db_free_result(DB_QUERY_RESULT* result) { delete reinterpret_cast(result); } ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_06/DB.h ================================================ // A C-style interface to a mock database // (a simplified subset of the MySQL C interface) #ifndef DB_INCLUDES #define DB_INCLUDES using DB_CONNECTION = void; using DB_QUERY_RESULT = void; using DB_ROW = const char**; /*! Make a connection to the database * Do not forget to close the database connection (db_disconnect()) once done with it. * \return a DB_CONNECTION handle; nullptr upon failure */ DB_CONNECTION* db_connect(); /*! Query the database * \param[in] connection A database connection handle returned by db_connect() * \param[in] query A SQL query * \return A handle to the result of the query (memory to be freed using db_free_result()!) */ DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query); /*! Retrieve the number of fields per row stored by the given query result * \param[in] result A handle returned by db_query * \return The number of fields per row stored by the result; -1 upon failure */ int db_num_fields(DB_QUERY_RESULT* result); /*! Fetch a single row from the result. * \param[in] result A handle returned by db_query * \return An array of strings. Each field is represented as a string (zero-terminated char array). * Let row be the result, then the first field is accessed using result[0]. */ DB_ROW db_fetch_row(DB_QUERY_RESULT* result); /*! Release the memory allocated for the result. * \param[in] result A handle returned by db_query() */ void db_free_result(DB_QUERY_RESULT* result); /*! Disconnect from the database * \param[in] connection A connection handle returned by db_connect() */ void db_disconnect(DB_CONNECTION* connection); #endif ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_06/DBException.cppm ================================================ // A simple C++ exception type export module db.exception; import ; export class DatabaseException : public std::runtime_error { public: using std::runtime_error::runtime_error; // Inherit constructor }; ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_06/DB_RAII.cppm ================================================ // RAII classes for handles returned by DB.h C interface functions /* In this solution we have the RAII classes accept the resource handle in their constructor. Alternatively, you could also acquire the resources inside the constructors of the RAII class. For instance: you could call db_connect() from within the DBConnectionRAII() contructor. */ module; #include "DB.h" export module db.raii; /** * RAII object that ensures that a given database connection is closed * once the RAII object goes out of scope. */ export class DBConnectionRAII { public: DBConnectionRAII(DB_CONNECTION* connection) noexcept : m_connection{ connection } { } ~DBConnectionRAII() // implicitly noexcept { if (m_connection) { db_disconnect(m_connection); } } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_CONNECTION*() const noexcept { return m_connection; } private: DB_CONNECTION* m_connection; }; /* RAII object that takes a DB_QUERY_RESULT and ensures it is closed */ export class DBQueryResultRAII { public: DBQueryResultRAII(DB_QUERY_RESULT* result) noexcept : m_result{ result } { } ~DBQueryResultRAII() // implicitly noexcept { if (m_result) { db_free_result(m_result); } } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_QUERY_RESULT* () const noexcept { return m_result; } private: DB_QUERY_RESULT* m_result; }; ================================================ FILE: Exercises/Modules/Chapter 16/Soln16_06/Soln16_06.cpp ================================================ // Exercise 16-6 /* Creating RAII classes to manage resource handles returned by a C interface Remember: RAII is not just for dynamic memory: every resource should be managed by an object! Leaks in the original code: - The database connection was not disconnected if an exception occurred indirectly in the main try-catch block. For the DatabaseException thrown in the block of db_query() the connection was correctly disconnected. Possible other exceptions, though, include: a) verifyCustomerFields() discovers a problem. This verification step may've been added later by someone trying to make the program more robuust, but not familiar enough with the surrounding program... b) std::stoi() throws std::invalid_argument because an empty string was passed, or a string that does not start with a number c) Customer::toString() throws std::bad_alloc because memory allocation failed for the new string d) If the output stream used fails for whatever reason, a std::runtime_exception occurs. While for std::cout this is less likely, perhaps the program is changed later to output to some other stream that writes to a GUI element or a file. These might fail... - The memory leaks for the query result are analogous - If no customers are found, someone decided to add an extra return statement. This again leaks all resources... Bottom line: the larger the program becomes, the more resources there are to keep track of, and at the same time the more exceptions and return statements there appear. Moreover, in real life often many different people collaborate on the same code, often less familiar with the original program and the resources it uses. It is just too easy to forget one case, and introduce a leak. Even the most disciplined developer will make mistakes this way---believe us! Hence: always use some form of RAII to manage a resource! */ import ; import ; #include "DB.h" import db.exception; import db.raii; import customer; void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream int main() { DBConnectionRAII connection{ db_connect() }; try { DBQueryResultRAII result{ db_query(connection, "SELECT * FROM CUSTOMER_TABEL") }; if (!result) { throw DatabaseException{"Query failed"}; } std::vector customers{ readCustomers(result) }; if (customers.empty()) { std::cerr << "No customers found?" << std::endl; return 2; } for (auto& customer : customers) { print(std::cout, customer); } } catch (const std::exception& caught) { std::cerr << caught.what() << std::endl; return 1; } } std::vector readCustomers(DB_QUERY_RESULT* result) { // Sanity check // (if the number of fields does not match 5, // the code below would crash!) verifyCustomerFields(result); std::vector customers; auto row{ db_fetch_row(result) }; while (row) { customers.push_back(Customer{ row[0], // Surname row[1], // Name row[2], // Street std::stoi(row[3]), // Street number row[4] // City }); row = db_fetch_row(result); } return customers; } void verifyCustomerFields(DB_QUERY_RESULT* result) { const int numFields{ db_num_fields(result) }; if (numFields < 0) { throw DatabaseException{"db_num_fields() failed"}; } if (numFields != 5) { throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; } } void print(std::ostream& stream, const Customer& customer) { stream << customer.toString() << std::endl; if (std::cout.fail()) { std::cout.clear(); throw std::runtime_error("Failed to output customer"); } } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_01/Array.cppm ================================================ // Same as Ex17_01B, but with a push_back() member and a default constructor export module array; import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() export template class Array { public: Array(); // Default constructor Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Copy assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays void push_back(const T& element); // Function to add new element to the end size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Template for functions that add new element to the end of the array // Uses the 'copy-and-swap' idiom. // If either the Array<> constructor throws // (std::bad_alloc or some exception from T's default constructor) // or any of the copy assignments of a T element throw, // then the original Array<> remains untouched. template void Array::push_back(const T& newElement) { Array copy{ m_size + 1 }; // Copy... for (size_t i {}; i < m_size; ++i) copy[i] = m_elements[i]; copy[m_size] = newElement; // ... modify ... swap(copy); // ... and swap! (noexcept) } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_01/Soln17_01.cpp ================================================ // Adding a push_back() member function and a default constructor to the Array<> // class template. Using copy-and-swap for a memory-safe push_back(). import array; import ; int main() { const unsigned numElements{ 100 }; Array squares; // default construction for (unsigned i {}; i < numElements; ++i) squares.push_back(i * i); // push_back() std::cout << squares.getSize() << " squares were added." << std::endl; std::cout << "For instance: 13 squared equals " << squares[13] << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_02/Pair.cppm ================================================ export module pair; import ; export template class Pair { public: // Public members + no m_ prefix analogous to std::pair<> (see module) First first; Second second; Pair() = default; Pair(const First& f, const Second& s); // In retrospect, this is no longer such an interesting exercise on writing // member function templates now that the compiler generates all lexicographical // comparison operators for you. // // Note: not all compiler may fully support this generation yet (C++20)... // (replacing auto with std::strong_ordering may make Soln17_02.cpp compile then, // but in general auto is better because First and/or Second could be types // that are not strongly ordered, such as a floating-point types) auto operator<=>(const Pair& other) const = default; }; // Constructor template Pair::Pair(const First& f, const Second& s) : first{f}, second{s} {} ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_02/Soln17_02.cpp ================================================ // Exercise 16-2 // Create a basic Pair template import pair; import ; import ; int main() { auto my_pair{ Pair{122, "abc"} }; ++my_pair.first; std::cout << "my_pair equals (" << my_pair.first << ", " << my_pair.second << ')' << std::endl; auto pair1{ Pair{0, "def"} }; using namespace std::string_literals; // To make s suffix work (see below) // CTAD works as well. The deduced type for both pair2 and pair3 is Pair: auto pair2{ Pair{123, std::string{"abc"}} }; // Option 1: specify std::string yourself (otherwise the type is const char[]) auto pair3{ Pair{123, "def"s} }; // Option 2: use string literals: s suffix creates a std::string object std::cout << (pair1 < pair2 && pair2 < pair3? "operator< seems to be working" : "oops") << std::endl; std::cout << (pair1 == pair2? "oops" : "operator== works as well") << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_03/Pair.cppm ================================================ export module pair; import ; import ; export template class Pair { public: // Public members + no m_ prefix analogous to std::pair<> (see module) First first; Second second; Pair() = default; Pair(const First& f, const Second& s); // Note: not all compiler may fully support this generation yet (C++20)... // (replacing auto with std::strong_ordering may make Soln17_02.cpp compile then, // but in general auto is better because First and/or Second could be types // that are not strongly ordered, such as a floating-point types) auto operator<=>(const Pair& other) const = default; }; // Constructor template Pair::Pair(const First& f, const Second& s) : first{f}, second{s} {} export template std::ostream& operator<<(std::ostream& out, const Pair& pair) { return out << '(' << pair.first << ", " << pair.second << ')'; } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_03/Soln17_03.cpp ================================================ // Create a << operator template for Pairs import pair; import ; import ; int main() { // To make the s string literal suffix work // (facilitates creation of Pairs using CTAD, or Constructor Template Argument Deduction). // This syntactic sugar for creating std::string objects is not really covered in the book. using namespace std::string_literals; auto my_pair{ Pair{122, "abc"s} }; ++my_pair.first; std::cout << "my_pair equals " << my_pair << std::endl; auto pair1{ Pair{ 0, "def"s} }; auto pair2{ Pair{123, "abc"s} }; auto pair3{ Pair{123, "def"s} }; std::cout << (pair1 < pair2 && pair2 < pair3? "operator< seems to be working" : "oops") << std::endl; std::cout << (pair1 == pair2? "oops" : "operator== works as well") << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_04/Pair.cppm ================================================ export module pair; import ; import ; export template class Pair { public: // Public members + no m_ prefix analogous to std::pair<> (see module) First first; Second second; Pair() = default; Pair(const First& f, const Second& s); // Note: not all compiler may fully support this generation yet (C++20)... // (replacing auto with std::strong_ordering may make Soln17_02.cpp compile then, // but in general auto is better because First and/or Second could be types // that are not strongly ordered, such as a floating-point types) auto operator<=>(const Pair& other) const = default; }; // Constructor template Pair::Pair(const First& f, const Second& s) : first{f}, second{s} {} export template std::ostream& operator<<(std::ostream& out, const Pair& pair) { return out << '(' << pair.first << ", " << pair.second << ')'; } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_04/Soln17_04.cpp ================================================ // Exercising the SparseArray class template // We create a sparse array of integers, populate 20 of its entries // (checking for duplicates among the randomly generated indices) // and output the resulting index/value pairs. import sparse_array; import ; import ; // For random number generation import ; // For std::bind() import ; // For std::make_shared<>() and std::shared_ptr<> // See Chapter 12 for an explanation of this principle. // The main difference here is that we need a std::uniform_int_distribution // instead of a std::uniform_real_distribution to generate integers instead // of float-point (or, "real") numbers. auto createUniformPseudoRandomNumberGenerator(int min, int max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t count {20}; // Number of elements to be created const int min_value{32}; const int max_value{212}; const size_t max_index{499}; // Create the (pseudo)-random number generators // (we use +1 because these generate integers in a half-open interval [min,max)...) auto generate_random_index{ createUniformPseudoRandomNumberGenerator(0, max_index + 1) }; auto generate_random_value{ createUniformPseudoRandomNumberGenerator(min_value, max_value + 1) }; SparseArray numbers; // Create empty sparse array for (size_t i {}; i < count; ++i) // Create count entries in numbers array { size_t index {}; // Stores new index value // Must ensure that indexes after the first are not duplicates do { index = generate_random_index(); // Get a random index 0 to max_index-1 } while (numbers.element_exists_at(index)); numbers[index] = generate_random_value(); // Store value at new index position } for (size_t i {}; i <= max_index; ++i) // Create count entries in numbers array { if (numbers.element_exists_at(i)) std::cout << "Element at index " << i << " equals " << numbers.at(i) << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_04/SparseArray.cppm ================================================ // SparseArray class template definition // Note the use of the find() helper function and the const-and-back-again idiom to minimize code duplication export module sparse_array; import pair; import ; import ; // For std::to_string() import ; // For std::as_const() import ; export template class SparseArray { public: T& operator[](size_t index); // Subscript operator (creates default-constructed value if no value exists for the given index) T& at(size_t index); // Access function (throws std::out_of_range if no value exists for the given index) const T& at(size_t index) const; // const overload of at() bool element_exists_at(size_t index) const; // Return true iff an element exists at the given index private: T* find(size_t index); // Helper function (returns nullptr if no value exists for the given index) const T* find(size_t index) const; std::vector> m_values; }; template T& SparseArray::operator[](size_t index) { if (auto* found{ find(index) }; found) // Using C++17 initialization statements for if statement { // (see at() function for common, traditional equivalent) return *found; } else { m_values.push_back({ index, T{} }); // Or push_back(Pair{...}), or push_back(Pair{...}) return m_values.back().second; // Remember: std::vector<>::back() returns a reference to its last element } } template const T& SparseArray::at(size_t index) const { const auto* found{ find(index) }; if (found) { return *found; } else { throw std::out_of_range{"No value exists at index " + std::to_string(index)}; } } template T& SparseArray::at(size_t index) { return const_cast(std::as_const(*this).at(index)); } template bool SparseArray::element_exists_at(size_t index) const { return find(index) != nullptr; } template const T* SparseArray::find(size_t index) const { for (auto& pair : m_values) { if (pair.first == index) return &pair.second; } return nullptr; } template T* SparseArray::find(size_t index) { return const_cast(std::as_const(*this).find(index)); } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_05/LinkedList.cppm ================================================ export module linked_list; /* This LinkedList template follows some (not all) conventions of Standard Library containers: some member function names are analogous, as is the choice not to work with exceptions in case you do something wrong (such as calling back() or pop_front() on an empty list). The latter has the implication that if you mis-use the container, your program will likely crash... */ import ; // for std::swap() export template class LinkedList { public: LinkedList() = default; // Default constructor (all pointers are initialised to nullptr) ~LinkedList(); LinkedList(const LinkedList& list); // Copy constructor LinkedList& operator=(LinkedList list); // Assignment operator void push_front(const T& value); // Add an object to the head void push_back(const T& value); // Add an object to the tail void pop_back(); // Removes the last element from the list (undefined behavior for empty lists) void pop_front(); // Removes the first element from the list (undefined behavior for empty lists) T& front(); // Get the object at the head (undefined behavior for empty lists) T& back(); // Get the object at the tail (undefined behavior for empty lists) const T& front() const; // Get the object at the head (undefined behavior for empty lists) const T& back() const; // Get the object at the tail (undefined behavior for empty lists) bool empty() const; // Checks whether the list is empty or not void clear(); // Function to remove all elements from the list size_t size() const; // Get the number of elements from the list class Iterator; // Nested Iterator class declaration (definition below) Iterator front_iterator() const; // Get an Iterator that starts at the head Iterator back_iterator() const; // Get an Iterator that starts at the tail void swap(LinkedList& other) noexcept; // Swap function private: class Node // Node class definition { public: Node(const T& theValue) : m_value{ theValue }, m_next{}, m_previous{} {} T m_value; Node* m_next; Node* m_previous; }; Node* m_head{}; // Pointer to first node Node* m_tail{}; // Pointer to last node size_t m_size{}; }; // Non-member swap function. // Convention dictates swap is present as non-member function. // For class types it is often easiest (and recommended) // to implement it using a member swap function. export template void swap(LinkedList& one, LinkedList& other) { one.swap(other); } // --------------------------------------------- // Member definitions // --------------------------------------------- // Destructor template template LinkedList::~LinkedList() { clear(); } // Copy constructor template template LinkedList::LinkedList(const LinkedList& list) { // Use existing members (iteration, push_back()) to implement the copying // This avoids duplicating any code that manipulates the list's pointers / size members. for (auto iterator{ list.front_iterator() }; iterator; iterator.next()) push_back(iterator.value()); } // Assignment operator template (uses copy-and-swap idiom) template LinkedList& LinkedList::operator=(LinkedList copy) { swap(*this, copy); return *this; } // Template for member functions that add an object to the head of the list template void LinkedList::push_front(const T& value) { // Note: this function is strongly exception-safe because // any and all change occur after the line annotated with (*), // which is the only line that may throw. Node* oldHead{ m_head }; m_head = new Node{ value }; // (*) ++m_size; if (oldHead) { oldHead->m_previous = m_head; m_head->m_next = oldHead; } else // list was empty before, and now has one element { m_tail = m_head; } } // Template for member functions that add an object to the tail of the list template void LinkedList::push_back(const T& value) { // Note: this function is strongly exception-safe because // any and all change occur after the line annotated with (*), // which is the only line that may throw. Node* oldTail{ m_tail }; m_tail = new Node{value}; // (*) ++m_size; if (oldTail) { oldTail->m_next = m_tail; m_tail->m_previous = oldTail; } else // list was empty before, and now has one element { m_head = m_tail; } } // Template for member functions that remove an object from the head of the list template void LinkedList::pop_front() { Node* oldHead{ m_head }; if (oldHead == m_tail) { m_head = m_tail = nullptr; } else { m_head = oldHead->m_next; m_head->m_previous = nullptr; } --m_size; delete oldHead; } // Template function member to remove an object from the tail of the list template void LinkedList::pop_back() { Node* oldTail{ m_tail }; if (oldTail == m_head) { m_head = m_tail = nullptr; } else { m_tail = oldTail->m_previous; m_tail->next = nullptr; } --m_size; delete oldTail; } // Template function members to get the object at the head of the list template T& LinkedList::front() { return m_head->value; } template const T& LinkedList::front() const { return m_head->value; } // Template function members to get the object at the tail of the list template T& LinkedList::back() { return m_tail->value; } template const T& LinkedList::back() const { return m_tail->value; } // Check whether list is empty or not template bool LinkedList::empty() const { return m_size == 0; // (or m_head == nullptr, or m_tail == nullptr) } // Template to get the size of a list template size_t LinkedList::size() const { return m_size; } // Template to clear a list template void LinkedList::clear() { // Use existing functions (avoid code duplication!) while (!empty()) pop_front(); } // Member function template to swap two lists template void LinkedList::swap(LinkedList& other) noexcept { std::swap(m_head, other.m_head); std::swap(m_tail, other.m_tail); std::swap(m_size, other.m_size); } // Definition of the nested Iterator class template class LinkedList::Iterator { public: explicit Iterator(Node* node) : m_current{ node } {} const T& value() const { return m_current->m_value; } bool hasValue() const { return m_current != nullptr; } operator bool() const { return m_current != nullptr; } void next() { m_current = m_current->m_next; } void previous() { m_current = m_current->m_previous; } private: Node* m_current; }; // Get an Iterator that starts at the head template typename LinkedList::Iterator LinkedList::front_iterator() const { return Iterator{ m_head }; } // Get an Iterator that starts at the tail template typename LinkedList::Iterator LinkedList::back_iterator() const { return Iterator{ m_tail }; } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_05/Soln17_05.cpp ================================================ // Exercise 17-5 Exercising the LinkedList template class // This program reverses the text that is entered import linked_list; import ; import ; import ; int main() { std::string text; // Stores input prose or poem std::cout << "\nEnter a poem or prose over one or more lines.\n" << "Terminate the input with #:\n"; getline(std::cin, text, '#'); LinkedList words; // List to store words // Extract words and store in the list std::string_view separators{ " ,.\"?!;:\n" }; // Separators between words size_t start {}; // Start of a word size_t end {}; // separator position after a word while (std::string::npos != (start = text.find_first_not_of(separators, start))) { end = text.find_first_of(separators, start+1); words.push_back(text.substr(start,end-start)); start = end; } // List the words 5 to a line std::cout << "\nThe words are:\n\n"; auto iterator{ words.front_iterator() }; size_t count {}; // Word counter const size_t perline {5}; // Worde per line while (iterator.hasValue()) { std::cout << iterator.value() << ' '; if (!(++count % perline)) std::cout << std::endl; iterator.next(); } std::cout << std::endl; // List the words in reverse order 5 to a line std::cout << "\nIn reverse order, the words are:\n\n"; iterator = words.back_iterator(); count = 0; while (iterator.hasValue()) { std::cout << iterator.value() << ' '; if(!(++count % perline)) std::cout << std::endl; iterator.previous(); } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_06/LinkedList.cppm ================================================ export module linked_list; /* This LinkedList template follows some (not all) conventions of Standard Library containers: some member function names are analogous, as is the choice not to work with exceptions in case you do something wrong (such as calling back() or pop_front() on an empty list). The latter has the implication that if you mis-use the container, your program will likely crash... */ import ; // for std::swap() export template class LinkedList { public: LinkedList() = default; // Default constructor (all pointers are initialised to nullptr) ~LinkedList(); LinkedList(const LinkedList& list); // Copy constructor LinkedList& operator=(LinkedList list); // Assignment operator void push_front(const T& value); // Add an object to the head void push_back(const T& value); // Add an object to the tail void pop_back(); // Removes the last element from the list (undefined behavior for empty lists) void pop_front(); // Removes the first element from the list (undefined behavior for empty lists) T& front(); // Get the object at the head (undefined behavior for empty lists) T& back(); // Get the object at the tail (undefined behavior for empty lists) const T& front() const; // Get the object at the head (undefined behavior for empty lists) const T& back() const; // Get the object at the tail (undefined behavior for empty lists) bool empty() const; // Checks whether the list is empty or not void clear(); // Function to remove all elements from the list size_t size() const; // Get the number of elements from the list class Iterator; // Nested Iterator class declaration (definition below) Iterator front_iterator() const; // Get an Iterator that starts at the head Iterator back_iterator() const; // Get an Iterator that starts at the tail void swap(LinkedList& other) noexcept; // Swap function private: class Node // Node class definition { public: Node(const T& theValue) : m_value{ theValue }, m_next{}, m_previous{} {} T m_value; Node* m_next; Node* m_previous; }; Node* m_head{}; // Pointer to first node Node* m_tail{}; // Pointer to last node size_t m_size{}; }; // Non-member swap function. // Convention dictates swap is present as non-member function. // For class types it is often easiest (and recommended) // to implement it using a member swap function. export template void swap(LinkedList& one, LinkedList& other) { one.swap(other); } // --------------------------------------------- // Member definitions // --------------------------------------------- // Destructor template template LinkedList::~LinkedList() { clear(); } // Copy constructor template template LinkedList::LinkedList(const LinkedList& list) { // Use existing members (iteration, push_back()) to implement the copying // This avoids duplicating any code that manipulates the list's pointers / size members. for (auto iterator{ list.front_iterator() }; iterator; iterator.next()) push_back(iterator.value()); } // Assignment operator template (uses copy-and-swap idiom) template LinkedList& LinkedList::operator=(LinkedList copy) { swap(*this, copy); return *this; } // Template for member functions that add an object to the head of the list template void LinkedList::push_front(const T& value) { // Note: this function is strongly exception-safe because // any and all change occur after the line annotated with (*), // which is the only line that may throw. Node* oldHead{ m_head }; m_head = new Node{ value }; // (*) ++m_size; if (oldHead) { oldHead->m_previous = m_head; m_head->m_next = oldHead; } else // list was empty before, and now has one element { m_tail = m_head; } } // Template for member functions that add an object to the tail of the list template void LinkedList::push_back(const T& value) { // Note: this function is strongly exception-safe because // any and all change occur after the line annotated with (*), // which is the only line that may throw. Node* oldTail{ m_tail }; m_tail = new Node{value}; // (*) ++m_size; if (oldTail) { oldTail->m_next = m_tail; m_tail->m_previous = oldTail; } else // list was empty before, and now has one element { m_head = m_tail; } } // Template for member functions that remove an object from the head of the list template void LinkedList::pop_front() { Node* oldHead{ m_head }; if (oldHead == m_tail) { m_head = m_tail = nullptr; } else { m_head = oldHead->m_next; m_head->m_previous = nullptr; } --m_size; delete oldHead; } // Template function member to remove an object from the tail of the list template void LinkedList::pop_back() { Node* oldTail{ m_tail }; if (oldTail == m_head) { m_head = m_tail = nullptr; } else { m_tail = oldTail->m_previous; m_tail->next = nullptr; } --m_size; delete oldTail; } // Template function members to get the object at the head of the list template T& LinkedList::front() { return m_head->value; } template const T& LinkedList::front() const { return m_head->value; } // Template function members to get the object at the tail of the list template T& LinkedList::back() { return m_tail->value; } template const T& LinkedList::back() const { return m_tail->value; } // Check whether list is empty or not template bool LinkedList::empty() const { return m_size == 0; // (or m_head == nullptr, or m_tail == nullptr) } // Template to get the size of a list template size_t LinkedList::size() const { return m_size; } // Template to clear a list template void LinkedList::clear() { // Use existing functions (avoid code duplication!) while (!empty()) pop_front(); } // Member function template to swap two lists template void LinkedList::swap(LinkedList& other) noexcept { std::swap(m_head, other.m_head); std::swap(m_tail, other.m_tail); std::swap(m_size, other.m_size); } // Definition of the nested Iterator class template class LinkedList::Iterator { public: explicit Iterator(Node* node) : m_current{ node } {} const T& value() const { return m_current->m_value; } bool hasValue() const { return m_current != nullptr; } operator bool() const { return m_current != nullptr; } void next() { m_current = m_current->m_next; } void previous() { m_current = m_current->m_previous; } private: Node* m_current; }; // Get an Iterator that starts at the head template typename LinkedList::Iterator LinkedList::front_iterator() const { return Iterator{ m_head }; } // Get an Iterator that starts at the tail template typename LinkedList::Iterator LinkedList::back_iterator() const { return Iterator{ m_tail }; } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_06/Pair.cppm ================================================ export module pair; import ; import ; export template class Pair { public: // Public members + no m_ prefix analogous to std::pair<> (see module) First first; Second second; Pair() = default; Pair(const First& f, const Second& s); // Note: not all compiler may fully support this generation yet (C++20)... // (replacing auto with std::strong_ordering may make Soln17_02.cpp compile then, // but in general auto is better because First and/or Second could be types // that are not strongly ordered, such as a floating-point types) auto operator<=>(const Pair& other) const = default; }; // Constructor template Pair::Pair(const First& f, const Second& s) : first{f}, second{s} {} export template std::ostream& operator<<(std::ostream& out, const Pair& pair) { return out << '(' << pair.first << ", " << pair.second << ')'; } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_06/Soln17_06.cpp ================================================ // Exercising the SparseArray class template in combination with the LinkedList class template import sparse_array; import linked_list; import ; import ; import ; import ; #include int main() { std::string text; // Stores input prose or poem std::cout << "Enter a poem or prose over one or more lines.\n" << "Terminate the input with #:\n"; getline(std::cin, text, '#'); SparseArray> lists; // Sparse array of linked lists const std::string_view letters {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"}; // Extract words and store in the appropriate list // A list in the SparseArray is selected by the index in letters of the first letter in a word. const std::string_view separators {" \n\t,.\"?!;:"}; // Separators between words size_t start {}; // Start of a word size_t end {}; // separator position after a word while (std::string::npos != (start = text.find_first_not_of(separators, start))) { end = text.find_first_of(separators, start+1); const auto word{ text.substr(start, end - start) }; const auto letter{ static_cast(std::toupper(word[0])) }; lists[letters.find(letter)].push_back(word); start = end; } // List the words in order 5 to a line const size_t perline {5}; for (size_t i {}; i < std::size(letters); ++i) { if (!lists.element_exists_at(i)) continue; size_t count {}; // Word counter for (auto iterator { lists[i].front_iterator() }; iterator; iterator.next()) { std::cout << iterator.value() << ' '; if (!(++count % perline)) std::cout << std::endl; } std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_06/SparseArray.cppm ================================================ // SparseArray class template definition // Note the use of the find() helper function and the const-and-back-again idiom to minimize code duplication export module sparse_array; import pair; import ; import ; // For std::to_string() import ; // For std::as_const() import ; export template class SparseArray { public: T& operator[](size_t index); // Subscript operator (creates default-constructed value if no value exists for the given index) T& at(size_t index); // Access function (throws std::out_of_range if no value exists for the given index) const T& at(size_t index) const; // const overload of at() bool element_exists_at(size_t index) const; // Return true iff an element exists at the given index private: T* find(size_t index); // Helper function (returns nullptr if no value exists for the given index) const T* find(size_t index) const; std::vector> m_values; }; template T& SparseArray::operator[](size_t index) { if (auto* found{ find(index) }; found) // Using C++17 initialization statements for if statement { // (see at() function for common, traditional equivalent) return *found; } else { m_values.push_back({ index, T{} }); // Or push_back(Pair{...}), or push_back(Pair{...}) return m_values.back().second; // Remember: std::vector<>::back() returns a reference to its last element } } template const T& SparseArray::at(size_t index) const { const auto* found{ find(index) }; if (found) { return *found; } else { throw std::out_of_range{"No value exists at index " + std::to_string(index)}; } } template T& SparseArray::at(size_t index) { return const_cast(std::as_const(*this).at(index)); } template bool SparseArray::element_exists_at(size_t index) const { return find(index) != nullptr; } template const T* SparseArray::find(size_t index) const { for (auto& pair : m_values) { if (pair.first == index) return &pair.second; } return nullptr; } template T* SparseArray::find(size_t index) { return const_cast(std::as_const(*this).find(index)); } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_07/Box.cppm ================================================ export module box; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_07/BoxFormatter.cppm ================================================ export module box.formatter; import box; import ; // Adding specific specializations to the std namespace is allowed export template <> class std::formatter : public std::formatter { public: /* This was no easy exercise. Fixes required compared to the outline in the exercise: - the member is called advance_to() and not advance() (we actually used advance() by mistake in the book, but hey, it did force you to practice consulting a Standard Library reference?) - you need to specify std::formatter to access the base class format() function - of course you still needed to add "Box(., ., .)" as well. One way to do this is repeatedly call std::format_to(), a function similar to std::format() except that it outputs not to a stream but an output iterator. You can also manipulate the iterator directly, as we show in for outputting the second ", " and the ")". (Iterator manipulation is explained in more detail in Chapter 20...) */ auto format(const Box& box, auto& context) { auto iter{ std::format_to(context.out(), "Box(") }; context.advance_to(iter); iter = std::formatter::format(box.getLength(), context); iter = std::format_to(iter, ", "); context.advance_to(iter); iter = std::formatter::format(box.getWidth(), context); *iter++ = ','; *iter++ = ' '; context.advance_to(iter); std::formatter::format(box.getHeight(), context); *iter++ = ')'; context.advance_to(iter); return iter; } }; ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_07/Soln17_07.cpp ================================================ // Implementing a custom std::formatter<> specialization to format Box objects import box; import box.formatter; import ; int main() { Box box{ 1, 2, 3 }; std::cout << std::format("My new box, {:.2}, is fabulous!", box); } ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_07A/Box.cppm ================================================ export module box; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_07A/BoxFormatter.cppm ================================================ export module box.formatter; import box; import ; import ; // Adding specific specializations to the std namespace is allowed export template <> class std::formatter { public: auto parse(auto& context) { // [context.begin(), context.end()) is a character range that contains a part of // the format string starting from the format specifications to be parsed, // e.g. in // // std::format("My new box, {:.2}, is fabulous!", box) // // the range will contain ".2}, is fabulous!". The formatter should // parse specifiers until '}' or the end of the range. // // Our goal for this same example is to store "Box({:.2}, {:.2}, {:.2})" in m_format. // We first find the range where for instance ".2" is present, // and then inject that three times into a format string of the correct form. auto iter{ context.begin() }; if (iter == context.end()) // May happen for empty {} format specifiers { m_format = "Box({}, {}, {})"; return iter; } // Search for the closing '}' while (iter != context.end() && *iter != '}') ++iter; if (*iter != '}') // If not found, fail { throw std::format_error{ "missing closing braces, }" }; } // Main trick in this format expression is that to get { or } // in the output you have to write {{ or }}. // Otherwise std::format() will see these characters as the begin or end of a replacement field. m_format = std::format("Box({{:{0}}}, {{:{0}}}, {{:{0}}})", std::string(context.begin(), iter)); return iter; } auto format(const Box& box, auto& context) { return std::format_to( context.out(), m_format, box.getLength(), box.getWidth(), box.getHeight() ); } private: std::string m_format; }; ================================================ FILE: Exercises/Modules/Chapter 17/Soln17_07A/Soln17_07A.cpp ================================================ // Implementing a custom std::formatter<> specialization to format Box objects // (Alternate solution) import box; import box.formatter; import ; int main() { Box box{ 1, 2, 3 }; std::cout << std::format("My new box, {:.2}, is fabulous!", box); } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_01/Box.cppm ================================================ export module box; import ; import ; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.m_length, box.m_width, box.m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_01/RandomBoxes.cppm ================================================ export module box.random; import box; import ; // For random number generation import ; // For std::bind() import ; // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } export Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } export auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_01/Soln18_01.cpp ================================================ // Exercise 18-1 Define move operators for the Truckload class import ; import ; import truckload; import box.random; /* Things to watch out for: - use of copy-and-swap / move-and-swap function - noexcept move and swap members - dislodge the linked list from the moved load in the move constructor */ int main() { const double dimLimit {99.0}; // Upper limit on Box dimensions Truckload load; const size_t boxCount {20}; // Number of Box object to be created // Create boxCount Box objects for (size_t i {}; i < boxCount; ++i) load.addBox(randomSharedBox()); std::cout << "The boxes in the Truckload are:\n"; std::cout << load << std::endl; Truckload moveConstructedLoad{ std::move(load) }; std::cout << "The boxes in the move constructed Truckload are:\n"; std::cout << moveConstructedLoad << std::endl; Truckload moveAssignedLoad; moveAssignedLoad = std::move(moveConstructedLoad); std::cout << "The boxes in the move assigned Truckload are:\n"; std::cout << moveAssignedLoad << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_01/Truckload.cpp ================================================ module truckload; import ; import ; // For standard exception type std::out_of_range import ; // For std::string and std::to_string() import ; // For std::swap() // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Copy assignment operator (updated to use copy-and-swap of course!) Truckload& Truckload::operator=(const Truckload& src) { auto copy{src}; swap(copy); return *this; } // Move constructor (noexcept!) Truckload::Truckload(Truckload&& src) noexcept : m_head{ src.m_head } , m_tail{ src.m_tail } { // Do not forget to dislodge the linked list from the moved src Truckload src.m_head = src.m_tail = nullptr; } // Move assignment operator (noexcept + move-and-swap of course!) Truckload& Truckload::operator=(Truckload&& src) noexcept { auto moved{std::move(src)}; swap(moved); return *this; } // Swap assignment operator (noexcept + move-and-swap of course!) void Truckload::swap(Truckload& other) noexcept { std::swap(m_head, other.m_head); std::swap(m_tail, other.m_tail); } // Optional yet conventional non-member function (forwards to member function) void swap(Truckload& one, Truckload& other) noexcept { one.swap(other); } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox& Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } throw std::out_of_range{ "Index too large: " + std::to_string(index) }; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_01/Truckload.cppm ================================================ export module truckload; import box; import ; import ; import ; export using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload ~Truckload(); // Destructor Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor Truckload& operator=(const Truckload& src); // Copy assignment operator Truckload(Truckload&& src) noexcept; // Move constructor Truckload& operator=(Truckload&& src) noexcept; // Move assignment operator void swap(Truckload& other) noexcept; class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in a module interface file) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; export std::ostream& operator<<(std::ostream& stream, const Truckload& load); // Optional yet conventional non-member function (forwards to member function) export void swap(Truckload& one, Truckload& other) noexcept; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_02/LinkedList.cppm ================================================ export module linked_list; /* The LinkedList template below follows some (not all) conventions of Standard Library containers: some member function names are analogous, as is the choice not to work with exceptions in case you do something wrong (such as calling back() or pop_front() on an empty list). The latter has the implication that if you mis-use the container, your program will likely crash... New in this version are the move constructor / assignment operators (mind the noexcepts, as always), and the fact that the push_back() and push_front() members have been updated to allow new T values to be moved in as well. Do not forget to add a move constructor for the nested Node class as well to prevent any unnecessary copies there! Note that if you are not defining a generic container template, you'd usually not add multiple overloads for copying and moving anymore. Even here, you could consider simply defining these two pass-by-value members: void push_front(T value); // Copy or move an object to the head void push_back(T value); // Copy or move an object to the tail And then apply the same simplification to the constructor of the nested Node class. The only downside then is that this is not optimal for objects without support for move semantics, bit given that move semantics has been around for almost a decade should be increasingly unlikely. We invite you to give it a try, and simplify the code along these lines. */ import ; // for std::swap() export template class LinkedList { public: LinkedList() = default; // Default constructor (all pointers are initialised to nullptr) ~LinkedList(); LinkedList(const LinkedList& list); // Copy constructor LinkedList& operator=(const LinkedList& list); // Copy assignment operator LinkedList(LinkedList&& list) noexcept; // Move constructor LinkedList& operator=(LinkedList&& list) noexcept; // Move assignment operator void push_front(const T& value); // Add an object to the head void push_back(const T& value); // Add an object to the tail void push_front(T&& value); // Move an object to the head void push_back(T&& value); // Move an object to the tail void pop_back(); // Removes the last element from the list (undefined behavior for empty lists) void pop_front(); // Removes the first element from the list (undefined behavior for empty lists) T& front(); // Get the object at the head (undefined behavior for empty lists) T& back(); // Get the object at the tail (undefined behavior for empty lists) const T& front() const; // Get the object at the head (undefined behavior for empty lists) const T& back() const; // Get the object at the tail (undefined behavior for empty lists) bool empty() const; // Checks whether the list is empty or not void clear(); // Function to remove all elements from the list size_t size() const; // Get the number of elements from the list class Iterator; // Nested Iterator class declaration (definition below) Iterator front_iterator() const; // Get an Iterator that starts at the head Iterator back_iterator() const; // Get an Iterator that starts at the tail void swap(LinkedList& other) noexcept; // Swap function private: class Node // Node class definition { public: Node(const T& value) : m_value{ value } {} Node(T&& value) : m_value{ std::move(value) } {} T m_value; Node* m_next{}; Node* m_previous{}; }; void push_front(Node* node) noexcept; void push_back(Node* node) noexcept; Node* m_head{}; // Pointer to first node Node* m_tail{}; // Pointer to last node size_t m_size{}; }; // Non-member swap function. // Convention dictates swap is present as non-member function. // For class types it is often easiest (and recommended) // to implement it using a member swap function. export template void swap(LinkedList& one, LinkedList& other) { one.swap(other); } // --------------------------------------------- // Member definitions // --------------------------------------------- // Destructor template template LinkedList::~LinkedList() { clear(); } // Copy constructor template template LinkedList::LinkedList(const LinkedList& list) { // Use existing members (iteration, push_back()) to implement the copying // This avoids duplicating any code that manipulates the list's pointers / size members. for (auto iterator{ list.front_iterator() }; iterator; iterator.next()) push_back(iterator.value()); } // Move constructor template template LinkedList::LinkedList(LinkedList&& list) noexcept : m_head{ list.m_head } , m_tail{ list.m_tail } , m_size{ list.m_size } { // Make sure list no longer deletes the nodes when destructed list.m_size = 0; list.m_head = list.m_tail = nullptr; // <-- optional } // Copy assignment operator template (uses copy-and-swap idiom) template LinkedList& LinkedList::operator=(const LinkedList& other) { auto copy{ other }; swap(copy); return *this; } // Move assignment operator template (uses move-and-swap idiom) template LinkedList& LinkedList::operator=(LinkedList&& other) noexcept { auto moved{ std::move(other) }; swap(moved); return *this; } // Template for member functions that adds a node to the head of the list template void LinkedList::push_front(Node* node) noexcept { Node* oldHead{ m_head }; m_head = node; ++m_size; if (oldHead) { oldHead->m_previous = m_head; m_head->m_next = oldHead; } else // list was empty before, and now has one element { m_tail = m_head; } } // Template for member functions that copy a value to the head of the list // Use push_front(Node*) to avoid any duplication! template void LinkedList::push_front(const T& value) { push_front(new Node{value}); } // Template for member functions that moves a value to the head of the list // Use push_front(Node*) to avoid any duplication! template void LinkedList::push_front(T&& value) { push_front(new Node{std::move(value)}); } // Template for member functions that add a node to the tail of the list template void LinkedList::push_back(Node* node) noexcept { Node* oldTail{ m_tail }; m_tail = node; ++m_size; if (oldTail) { oldTail->m_next = m_tail; m_tail->m_previous = oldTail; } else // list was empty before, and now has one element { m_head = m_tail; } } // Template for member functions that copy a value to the tail of the list // Use push_back(Node*) to avoid any duplication! template void LinkedList::push_back(const T& value) { push_back(new Node{value}); } // Template for member functions that moves a value to the tail of the list // Use push_back(Node*) to avoid any duplication! template void LinkedList::push_back(T&& value) { push_back(new Node{std::move(value)}); } // Template for member functions that remove an object from the head of the list template void LinkedList::pop_front() { Node* oldHead{ m_head }; if (oldHead == m_tail) { m_head = m_tail = nullptr; } else { m_head = oldHead->m_next; m_head->m_previous = nullptr; } --m_size; delete oldHead; } // Template function member to remove an object from the tail of the list template void LinkedList::pop_back() { Node* oldTail{ m_tail }; if (oldTail == m_head) { m_head = m_tail = nullptr; } else { m_tail = oldTail->m_previous; m_tail->next = nullptr; } --m_size; delete oldTail; } // Template function members to get the object at the head of the list template T& LinkedList::front() { return m_head->value; } template const T& LinkedList::front() const { return m_head->value; } // Template function members to get the object at the tail of the list template T& LinkedList::back() { return m_tail->value; } template const T& LinkedList::back() const { return m_tail->value; } // Check whether list is empty or not template bool LinkedList::empty() const { return m_size == 0; // (or m_head == nullptr, or m_tail == nullptr) } // Template to get the size of a list template size_t LinkedList::size() const { return m_size; } // Template to clear a list template void LinkedList::clear() { // Use existing functions (avoid code duplication!) while (!empty()) pop_front(); } // Member function template to swap two lists template void LinkedList::swap(LinkedList& other) noexcept { std::swap(m_head, other.m_head); std::swap(m_tail, other.m_tail); std::swap(m_size, other.m_size); } // Definition of the nested Iterator class template class LinkedList::Iterator { public: explicit Iterator(Node* node) : m_current{ node } {} const T& value() const { return m_current->m_value; } bool hasValue() const { return m_current != nullptr; } operator bool() const { return m_current != nullptr; } void next() { m_current = m_current->m_next; } void previous() { m_current = m_current->m_previous; } private: Node* m_current; }; // Get an Iterator that starts at the head template typename LinkedList::Iterator LinkedList::front_iterator() const { return Iterator{ m_head }; } // Get an Iterator that starts at the tail template typename LinkedList::Iterator LinkedList::back_iterator() const { return Iterator{ m_tail }; } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_02/Soln18_02.cpp ================================================ // Exercise 18-2 Exercising the LinkedList's move capabilities // We do so by creating a linked list of std::unique_ptr<> elements: // a perfect example of an element type that cannot be copied! import linked_list; import ; // for std::unique_ptr<> import ; import ; void printList(std::string_view message, const LinkedList>& list) { std::cout << message << ": "; for (auto iterator{ list.front_iterator() }; iterator; iterator.next()) { std::cout << *iterator.value() << ' '; } std::cout << std::endl; } int main() { // In real life, you'd rarely use std::unique_ptr. // A more realistic use would be std::unique_ptr, // where ClassType is a (potentially polymorphic) class type. // For our example here, std::unique_ptr will do just fine: // the idea is simply to use an uncopyable type as template type argument for LinkedList. LinkedList> number_pointers; auto one{ std::make_unique(1) }; number_pointers.push_back(std::move(one)); number_pointers.push_back(std::make_unique(2)); printList("Elements in the original list", number_pointers); LinkedList> move_constructed{std::move(number_pointers)}; printList("Elements in the move constructed list", move_constructed); LinkedList> move_assigned; move_assigned = std::move(move_constructed); printList("Elements in the move assigned list", move_assigned); } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_03/Customer.cpp ================================================ // A simple C++ customer class module customer; Customer::Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city) : m_surname{ surname } , m_name{ name } , m_street{ street } , m_streetNumber{ streetNumber } , m_city{ city } {} std::string Customer::toString() const { std::string result; result += m_surname; result += ' '; result += m_name; result += ", "; result += m_street; result += ' '; result += std::to_string(m_streetNumber); result += ", "; result += m_city; return result; } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_03/Customer.cppm ================================================ // A simple C++ customer class export module customer; import ; import ; export class Customer { public: Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city ); std::string toString() const; private: std::string m_surname; std::string m_name; std::string m_street; int m_streetNumber; std::string m_city; }; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_03/DB.cpp ================================================ // A mocked database #include "DB.h" #include // For std::strcmp() import ; import ; namespace { struct QueryResult { std::vector> data; size_t index {}; }; class Database { public: Database() = default; bool hasConnection() const { return m_connected; } void connect() { m_connected = true; } void disconnect() { m_connected = false; } QueryResult* query(const char* query); private: bool m_connected{}; }; } DB_CONNECTION* db_connect() { // We only have one single database, which allows only one single connection: static Database theDatabase; if (theDatabase.hasConnection()) { return nullptr; } else { theDatabase.connect(); return &theDatabase; } } void db_disconnect(DB_CONNECTION* connection) { // reinterpret_cast<> is used to cast between pointers/references of // unrelated types (such as void* and Database*) reinterpret_cast(connection)->disconnect(); } DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query) { return reinterpret_cast(connection)->query(query); } QueryResult* Database::query(const char* query) { if (!hasConnection()) { return nullptr; } // Our database only understands one single query! if (std::strcmp(query, "SELECT * FROM CUSTOMER_TABEL") != 0) { return nullptr; } auto result{ std::make_unique() }; result->data = std::vector>{ { "Sherlock", "Holmes", "Baker Street", "221", "London" }, { "Joe", "Biden", "Pennsylvania Avenue", "1600", "Washington DC" }, { "Donald", "Duck", "Webfoot Walk", "1313", "Duckville" }, { "Sirius", "Black", "Grimmauld Place", "12", "London" }, { "Nemo", "Clownfish", "Wallaby Way", "42", "Sydney" }, { "Sam", "Malone", "Beacon Street", "112", "Boston" } }; return result.release(); } int db_num_fields(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->data.empty()) { return -1; } else { return static_cast(theResult->data.front().size()); } } DB_ROW db_fetch_row(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->index >= theResult->data.size()) { return nullptr; } else { return theResult->data[theResult->index++].data(); } } void db_free_result(DB_QUERY_RESULT* result) { delete reinterpret_cast(result); } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_03/DB.h ================================================ // A C-style interface to a mock database // (a simplified subset of the MySQL C interface) #ifndef DB_INCLUDES #define DB_INCLUDES using DB_CONNECTION = void; using DB_QUERY_RESULT = void; using DB_ROW = const char**; /*! Make a connection to the database * Do not forget to close the database connection (db_disconnect()) once done with it. * \return a DB_CONNECTION handle; nullptr upon failure */ DB_CONNECTION* db_connect(); /*! Query the database * \param[in] connection A database connection handle returned by db_connect() * \param[in] query A SQL query * \return A handle to the result of the query (memory to be freed using db_free_result()!) */ DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query); /*! Retrieve the number of fields per row stored by the given query result * \param[in] result A handle returned by db_query * \return The number of fields per row stored by the result; -1 upon failure */ int db_num_fields(DB_QUERY_RESULT* result); /*! Fetch a single row from the result. * \param[in] result A handle returned by db_query * \return An array of strings. Each field is represented as a string (zero-terminated char array). * Let row be the result, then the first field is accessed using result[0]. */ DB_ROW db_fetch_row(DB_QUERY_RESULT* result); /*! Release the memory allocated for the result. * \param[in] result A handle returned by db_query() */ void db_free_result(DB_QUERY_RESULT* result); /*! Disconnect from the database * \param[in] connection A connection handle returned by db_connect() */ void db_disconnect(DB_CONNECTION* connection); #endif ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_03/DBException.cppm ================================================ // A simple C++ exception type export module db.exception; import ; export class DatabaseException : public std::runtime_error { public: using std::runtime_error::runtime_error; // Inherit constructor }; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_03/DB_RAII.cppm ================================================ module; #include "DB.h" export module db.raii; /* In this solution we have the RAII classes accept the resource handle in their constructor. Alternatively, you could also acquire the resources inside the constructors of the RAII class. For instance: you could call db_connect() from within the DBConnectionRAII() contructor. When creating RAII classes, it is typically crucial they cannot be copied (otherwise multiple objects would be releasing the same resource, which is typically not allowed). To accomplish this, you delete both copy members. */ /** * RAII object that ensures that a given database connection is closed * once the RAII object goes out of scope. */ export class DBConnectionRAII { public: DBConnectionRAII(DB_CONNECTION* connection) noexcept : m_connection(connection) { } ~DBConnectionRAII() // implicitly noexcept { if (m_connection) { db_disconnect(m_connection); } } // Prevent copying by deleting both copy members DBConnectionRAII(const DBConnectionRAII& other) = delete; DBConnectionRAII& operator=(const DBConnectionRAII& other) = delete; // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_CONNECTION*() const noexcept { return m_connection; } private: DB_CONNECTION* m_connection; }; /* RAII object that takes a DB_QUERY_RESULT and ensures it is freed */ export class DBQueryResultRAII { public: DBQueryResultRAII(DB_QUERY_RESULT* result) noexcept : m_result(result) { } ~DBQueryResultRAII() // implicitly noexcept { if (m_result) { db_free_result(m_result); } } // Prevent copying by deleting both copy members DBQueryResultRAII(const DBQueryResultRAII& other) = delete; DBQueryResultRAII& operator=(const DBQueryResultRAII& other) = delete; // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_QUERY_RESULT* () const noexcept { return m_result; } private: DB_QUERY_RESULT* m_result; }; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_03/Soln18_03.cpp ================================================ // Exercise 18-3 /* Below is the same test program as used in Exercise 16-6, with some additional commented lines to show that the RAII objects cannot be copied. */ import ; import ; #include "DB.h" import db.exception; import db.raii; import customer; void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream int main() { DBConnectionRAII connection{ db_connect() }; /* DBConnectionRAII copy{ connection }; // Will not compile (copy constructor is deleted) DBConnectionRAII otherConnection{ db_connect() }; otherConnection = connection; // Will not compile (copy assignment operator is deleted) */ try { DBQueryResultRAII result{ db_query(connection, "SELECT * FROM CUSTOMER_TABEL") }; if (!result) { throw DatabaseException{"Query failed"}; } /* DBQueryResultRAII copy{ result }; // Will not compile (copy constructor is deleted) DBQueryResultRAII otherResult{ db_query(connection, "SELECT * FROM CUSTOMER_TABEL") }; otherResult = result; // Will not compile (copy assignment operator is deleted) */ std::vector customers{ readCustomers(result) }; if (customers.empty()) { std::cerr << "No customers found?" << std::endl; return 2; } for (auto& customer : customers) { print(std::cout, customer); } } catch (std::exception& caught) { std::cerr << caught.what() << std::endl; return 1; } } std::vector readCustomers(DB_QUERY_RESULT* result) { // Sanity check // (if the number of fields does not match 5, // the code below would crash!) verifyCustomerFields(result); std::vector customers; auto row{ db_fetch_row(result) }; while (row) { customers.push_back(Customer{ row[0], // Surname row[1], // Name row[2], // Street std::stoi(row[3]), // Street number row[4] // City }); row = db_fetch_row(result); } return customers; } void verifyCustomerFields(DB_QUERY_RESULT* result) { int numFields{ db_num_fields(result) }; if (numFields < 0) { throw DatabaseException{"db_num_fields() failed"}; } if (numFields != 5) { throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; } } void print(std::ostream& stream, const Customer& customer) { stream << customer.toString() << std::endl; if (std::cout.fail()) { std::cout.clear(); throw std::runtime_error("Failed to output customer"); } } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_04/Customer.cpp ================================================ // A simple C++ customer class module customer; Customer::Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city) : m_surname{ surname } , m_name{ name } , m_street{ street } , m_streetNumber{ streetNumber } , m_city{ city } {} std::string Customer::toString() const { std::string result; result += m_surname; result += ' '; result += m_name; result += ", "; result += m_street; result += ' '; result += std::to_string(m_streetNumber); result += ", "; result += m_city; return result; } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_04/Customer.cppm ================================================ // A simple C++ customer class export module customer; import ; import ; export class Customer { public: Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city ); std::string toString() const; private: std::string m_surname; std::string m_name; std::string m_street; int m_streetNumber; std::string m_city; }; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_04/DB.cpp ================================================ // A mocked database #include "DB.h" #include // For std::strcmp() import ; import ; namespace { struct QueryResult { std::vector> data; size_t index {}; }; class Database { public: Database() = default; bool hasConnection() const { return m_connected; } void connect() { m_connected = true; } void disconnect() { m_connected = false; } QueryResult* query(const char* query); private: bool m_connected{}; }; } DB_CONNECTION* db_connect() { // We only have one single database, which allows only one single connection: static Database theDatabase; if (theDatabase.hasConnection()) { return nullptr; } else { theDatabase.connect(); return &theDatabase; } } void db_disconnect(DB_CONNECTION* connection) { // reinterpret_cast<> is used to cast between pointers/references of // unrelated types (such as void* and Database*) reinterpret_cast(connection)->disconnect(); } DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query) { return reinterpret_cast(connection)->query(query); } QueryResult* Database::query(const char* query) { if (!hasConnection()) { return nullptr; } // Our database only understands one single query! if (std::strcmp(query, "SELECT * FROM CUSTOMER_TABEL") != 0) { return nullptr; } auto result{ std::make_unique() }; result->data = std::vector>{ { "Sherlock", "Holmes", "Baker Street", "221", "London" }, { "Donald", "Trump", "Pennsylvania Avenue", "1600", "Washington DC" }, { "Donald", "Duck", "Webfoot Walk", "1313", "Duckville" }, { "Sirius", "Black", "Grimmauld Place", "12", "London" }, { "Nemo", "Clownfish", "Wallaby Way", "42", "Sydney" }, { "Sam", "Malone", "Beacon Street", "112", "Boston" } }; return result.release(); } int db_num_fields(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->data.empty()) { return -1; } else { return static_cast(theResult->data.front().size()); } } DB_ROW db_fetch_row(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->index >= theResult->data.size()) { return nullptr; } else { return theResult->data[theResult->index++].data(); } } void db_free_result(DB_QUERY_RESULT* result) { delete reinterpret_cast(result); } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_04/DB.h ================================================ // A C-style interface to a mock database // (a simplified subset of the MySQL C interface) #ifndef DB_INCLUDES #define DB_INCLUDES using DB_CONNECTION = void; using DB_QUERY_RESULT = void; using DB_ROW = const char**; /*! Make a connection to the database * Do not forget to close the database connection (db_disconnect()) once done with it. * \return a DB_CONNECTION handle; nullptr upon failure */ DB_CONNECTION* db_connect(); /*! Query the database * \param[in] connection A database connection handle returned by db_connect() * \param[in] query A SQL query * \return A handle to the result of the query (memory to be freed using db_free_result()!) */ DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query); /*! Retrieve the number of fields per row stored by the given query result * \param[in] result A handle returned by db_query * \return The number of fields per row stored by the result; -1 upon failure */ int db_num_fields(DB_QUERY_RESULT* result); /*! Fetch a single row from the result. * \param[in] result A handle returned by db_query * \return An array of strings. Each field is represented as a string (zero-terminated char array). * Let row be the result, then the first field is accessed using result[0]. */ DB_ROW db_fetch_row(DB_QUERY_RESULT* result); /*! Release the memory allocated for the result. * \param[in] result A handle returned by db_query() */ void db_free_result(DB_QUERY_RESULT* result); /*! Disconnect from the database * \param[in] connection A connection handle returned by db_connect() */ void db_disconnect(DB_CONNECTION* connection); #endif ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_04/DBException.cppm ================================================ // A simple C++ exception type export module db.exception; import ; export class DatabaseException : public std::runtime_error { public: using std::runtime_error::runtime_error; // Inherit constructor }; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_04/DB_RAII.cppm ================================================ // RAII classes for handles returned by DB.h C interface functions /* In this solution we have the RAII classes accept the resource handle in their constructor. Alternatively, you could also acquire the resources inside the constructors of the RAII class. For instance: you could call db_connect() from within the DBConnectionRAII() contructor. When creating RAII classes, it is typically crucial they cannot be copied (otherwise multiple objects would be releasing the same resource, which is typically not allowed). To accomplish this, you delete both copy members. Moving RAII objects, on the other hand, is usually possible. */ module; #include "DB.h" export module db.raii; /** * RAII object that ensures that a given database connection is closed * once the RAII object goes out of scope. */ export class DBConnectionRAII { public: DBConnectionRAII(DB_CONNECTION* connection = nullptr) noexcept : m_connection(connection) { } ~DBConnectionRAII() // implicitly noexcept { if (m_connection) { db_disconnect(m_connection); } } // Prevent copying by deleting both copy members DBConnectionRAII(const DBConnectionRAII& other) = delete; DBConnectionRAII& operator=(const DBConnectionRAII& other) = delete; // Allow moving by adding the appropriate members DBConnectionRAII(DBConnectionRAII&& other) noexcept : m_connection{ other.m_connection } { other.m_connection = nullptr; // Make sure other no longer closes the connection } DBConnectionRAII& operator=(DBConnectionRAII&& other) noexcept { // You could consider move-and-swap here, but it's so trivial we decided against it here. m_connection = other.m_connection; other.m_connection = nullptr; return *this; } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_CONNECTION*() const noexcept { return m_connection; } private: DB_CONNECTION* m_connection; }; /* RAII object that takes a DB_QUERY_RESULT and ensures it is freed */ export class DBQueryResultRAII { public: DBQueryResultRAII(DB_QUERY_RESULT* result = nullptr) noexcept : m_result{ result } { } ~DBQueryResultRAII() // implicitly noexcept { if (m_result) { db_free_result(m_result); } } // Prevent copying by deleting both copy members DBQueryResultRAII(const DBQueryResultRAII& other) = delete; DBQueryResultRAII& operator=(const DBQueryResultRAII& other) = delete; // Allow moving by adding the appropriate members DBQueryResultRAII(DBQueryResultRAII&& other) noexcept : m_result{ other.m_result } { other.m_result = nullptr; // Make sure other no longer closes the connection } DBQueryResultRAII& operator=(DBQueryResultRAII&& other) noexcept { // You could consider move-and-swap here, but it's so trivial we decided against it here. m_result = other.m_result; other.m_result = nullptr; return *this; } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_QUERY_RESULT* () const noexcept { return m_result; } private: DB_QUERY_RESULT* m_result; }; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_04/Soln18_04.cpp ================================================ // Exercise 18-4 /* Below is the same test program as used in Exercise 16-6, with some additional lines to show that the RAII objects can be moved. */ import ; import ; #include "DB.h" import db.exception; import db.raii; import customer; void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream int main() { DBConnectionRAII connection{ db_connect() }; DBConnectionRAII moved_connection{ std::move(connection) }; try { DBQueryResultRAII result{ db_query(moved_connection, "SELECT * FROM CUSTOMER_TABEL") }; if (!result) { throw DatabaseException{"Query failed"}; } DBQueryResultRAII moved_result; moved_result = std::move(result); std::vector customers{ readCustomers(moved_result) }; if (customers.empty()) { std::cerr << "No customers found?" << std::endl; return 2; } for (auto& customer : customers) { print(std::cout, customer); } } catch (std::exception& caught) { std::cerr << caught.what() << std::endl; return 1; } } std::vector readCustomers(DB_QUERY_RESULT* result) { // Sanity check // (if the number of fields does not match 5, // the code below would crash!) verifyCustomerFields(result); std::vector customers; auto row{ db_fetch_row(result) }; while (row) { customers.push_back(Customer{ row[0], // Surname row[1], // Name row[2], // Street std::stoi(row[3]), // Street number row[4] // City }); row = db_fetch_row(result); } return customers; } void verifyCustomerFields(DB_QUERY_RESULT* result) { int numFields{ db_num_fields(result) }; if (numFields < 0) { throw DatabaseException{"db_num_fields() failed"}; } if (numFields != 5) { throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; } } void print(std::ostream& stream, const Customer& customer) { stream << customer.toString() << std::endl; if (std::cout.fail()) { std::cout.clear(); throw std::runtime_error("Failed to output customer"); } } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_05/Customer.cpp ================================================ // A simple C++ customer class module customer; Customer::Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city) : m_surname{ surname } , m_name{ name } , m_street{ street } , m_streetNumber{ streetNumber } , m_city{ city } {} std::string Customer::toString() const { std::string result; result += m_surname; result += ' '; result += m_name; result += ", "; result += m_street; result += ' '; result += std::to_string(m_streetNumber); result += ", "; result += m_city; return result; } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_05/Customer.cppm ================================================ // A simple C++ customer class export module customer; import ; import ; export class Customer { public: Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city ); std::string toString() const; private: std::string m_surname; std::string m_name; std::string m_street; int m_streetNumber; std::string m_city; }; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_05/DB.cpp ================================================ // A mocked database #include "DB.h" #include // For std::strcmp() #include #include namespace { struct QueryResult { std::vector> data; size_t index {}; }; class Database { public: Database() = default; bool hasConnection() const { return m_connected; } void connect() { m_connected = true; } void disconnect() { m_connected = false; } QueryResult* query(const char* query); private: bool m_connected{}; }; } DB_CONNECTION* db_connect() { // We only have one single database, which allows only one single connection: static Database theDatabase; if (theDatabase.hasConnection()) { return nullptr; } else { theDatabase.connect(); return &theDatabase; } } void db_disconnect(DB_CONNECTION* connection) { // reinterpret_cast<> is used to cast between pointers/references of // unrelated types (such as void* and Database*) reinterpret_cast(connection)->disconnect(); } DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query) { return reinterpret_cast(connection)->query(query); } QueryResult* Database::query(const char* query) { if (!hasConnection()) { return nullptr; } // Our database only understands one single query! if (std::strcmp(query, "SELECT * FROM CUSTOMER_TABEL") != 0) { return nullptr; } auto result{ std::make_unique() }; result->data = std::vector>{ { "Sherlock", "Holmes", "Baker Street", "221", "London" }, { "Joe", "Biden", "Pennsylvania Avenue", "1600", "Washington DC" }, { "Donald", "Duck", "Webfoot Walk", "1313", "Duckville" }, { "Sirius", "Black", "Grimmauld Place", "12", "London" }, { "Nemo", "Clownfish", "Wallaby Way", "42", "Sydney" }, { "Sam", "Malone", "Beacon Street", "112", "Boston" } }; return result.release(); } int db_num_fields(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->data.empty()) { return -1; } else { return static_cast(theResult->data.front().size()); } } DB_ROW db_fetch_row(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->index >= theResult->data.size()) { return nullptr; } else { return theResult->data[theResult->index++].data(); } } void db_free_result(DB_QUERY_RESULT* result) { delete reinterpret_cast(result); } ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_05/DB.h ================================================ // A C-style interface to a mock database // (a simplified subset of the MySQL C interface) #ifndef DB_INCLUDES #define DB_INCLUDES using DB_CONNECTION = void; using DB_QUERY_RESULT = void; using DB_ROW = const char**; /*! Make a connection to the database * Do not forget to close the database connection (db_disconnect()) once done with it. * \return a DB_CONNECTION handle; nullptr upon failure */ DB_CONNECTION* db_connect(); /*! Query the database * \param[in] connection A database connection handle returned by db_connect() * \param[in] query A SQL query * \return A handle to the result of the query (memory to be freed using db_free_result()!) */ DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query); /*! Retrieve the number of fields per row stored by the given query result * \param[in] result A handle returned by db_query * \return The number of fields per row stored by the result; -1 upon failure */ int db_num_fields(DB_QUERY_RESULT* result); /*! Fetch a single row from the result. * \param[in] result A handle returned by db_query * \return An array of strings. Each field is represented as a string (zero-terminated char array). * Let row be the result, then the first field is accessed using result[0]. */ DB_ROW db_fetch_row(DB_QUERY_RESULT* result); /*! Release the memory allocated for the result. * \param[in] result A handle returned by db_query() */ void db_free_result(DB_QUERY_RESULT* result); /*! Disconnect from the database * \param[in] connection A connection handle returned by db_connect() */ void db_disconnect(DB_CONNECTION* connection); #endif ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_05/DBException.cppm ================================================ // A simple C++ exception type export module db.exception; import ; export class DatabaseException : public std::runtime_error { public: using std::runtime_error::runtime_error; // Inherit constructor }; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_05/DB_RAII.cppm ================================================ // RAII classes for handles returned by DB.h C interface functions /* In this solution we have the RAII classes accept the resource handle in their constructor. Alternatively, you could also acquire the resources inside the constructors of the RAII class. For instance: you could call db_connect() from within the DBConnectionRAII() contructor. When creating RAII classes, it is typically crucial they cannot be copied (otherwise multiple objects would be releasing the same resource, which is typically not allowed). To accomplish this, you delete both copy members. Moving RAII objects, on the other hand, is usually possible. */ module; #include "DB.h" export module db.raii; import ; // For std::exchange<>() /** * RAII object that ensures that a given database connection is closed * once the RAII object goes out of scope. */ export class DBConnectionRAII { public: DBConnectionRAII(DB_CONNECTION* connection = nullptr) noexcept : m_connection(connection) { } ~DBConnectionRAII() // implicitly noexcept { if (m_connection) { db_disconnect(m_connection); } } // Prevent copying by deleting both copy members DBConnectionRAII(const DBConnectionRAII& other) = delete; DBConnectionRAII& operator=(const DBConnectionRAII& other) = delete; // Allow moving by adding the appropriate members DBConnectionRAII(DBConnectionRAII&& other) noexcept : m_connection{ std::exchange(other.m_connection, nullptr) } { } DBConnectionRAII& operator=(DBConnectionRAII&& other) noexcept { // You could consider move-and-swap here, but it's so trivial (especially now) // that we decided against it here. m_connection = std::exchange(other.m_connection, nullptr); return *this; } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_CONNECTION*() const noexcept { return m_connection; } private: DB_CONNECTION* m_connection; }; /* RAII object that takes a DB_QUERY_RESULT and ensures it is freed */ export class DBQueryResultRAII { public: DBQueryResultRAII(DB_QUERY_RESULT* result = nullptr) noexcept : m_result{ result } { } ~DBQueryResultRAII() // implicitly noexcept { if (m_result) { db_free_result(m_result); } } // Prevent copying by deleting both copy members DBQueryResultRAII(const DBQueryResultRAII& other) = delete; DBQueryResultRAII& operator=(const DBQueryResultRAII& other) = delete; // Allow moving by adding the appropriate members DBQueryResultRAII(DBQueryResultRAII&& other) noexcept : m_result{ std::exchange(other.m_result, nullptr) } { } DBQueryResultRAII& operator=(DBQueryResultRAII&& other) noexcept { // You could consider move-and-swap here, but it's so trivial (especially now) // that we decided against it here. m_result = std::exchange(other.m_result, nullptr); return *this; } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_QUERY_RESULT* () const noexcept { return m_result; } private: DB_QUERY_RESULT* m_result; }; ================================================ FILE: Exercises/Modules/Chapter 18/Soln18_05/Soln18_05.cpp ================================================ // Exercise 18-5 // Only difference is the use of std::exchange() in the RAII classes import ; import ; #include "DB.h" import db.exception; import db.raii; import customer; void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream int main() { DBConnectionRAII connection{ db_connect() }; DBConnectionRAII moved_connection{ std::move(connection) }; try { DBQueryResultRAII result{ db_query(moved_connection, "SELECT * FROM CUSTOMER_TABEL") }; if (!result) { throw DatabaseException{"Query failed"}; } DBQueryResultRAII moved_result; moved_result = std::move(result); std::vector customers{ readCustomers(moved_result) }; if (customers.empty()) { std::cerr << "No customers found?" << std::endl; return 2; } for (auto& customer : customers) { print(std::cout, customer); } } catch (std::exception& caught) { std::cerr << caught.what() << std::endl; return 1; } } std::vector readCustomers(DB_QUERY_RESULT* result) { // Sanity check // (if the number of fields does not match 5, // the code below would crash!) verifyCustomerFields(result); std::vector customers; auto row{ db_fetch_row(result) }; while (row) { customers.push_back(Customer{ row[0], // Surname row[1], // Name row[2], // Street std::stoi(row[3]), // Street number row[4] // City }); row = db_fetch_row(result); } return customers; } void verifyCustomerFields(DB_QUERY_RESULT* result) { int numFields{ db_num_fields(result) }; if (numFields < 0) { throw DatabaseException{"db_num_fields() failed"}; } if (numFields != 5) { throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; } } void print(std::ostream& stream, const Customer& customer) { stream << customer.toString() << std::endl; if (std::cout.fail()) { std::cout.clear(); throw std::runtime_error("Failed to output customer"); } } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_01/Soln19_01.cpp ================================================ // Exercise 19-1. // A lambda expression returning the number of vector elements that begin with a given letter. import ; import ; import ; import ; int main() { std::vector words{"apple", "pear", "plum", "orange", "peach", "grape", "greengage"}; std::cout << "Words are:\n"; for (const auto& word : words) std::cout << std::format("{:10}", word); std::cout << std::endl; const auto count { [&words](char letter) { size_t n {}; for (auto& word : words) if (letter == word[0]) ++n; return n; } }; char ch {'p'}; std::cout << std::format("There are {} words that begin with {}.\n", count(ch), ch); ch = 'g'; std::cout << std::format("There are {} words that begin with {}.\n", count(ch), ch); } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_02/Soln19_02.cpp ================================================ // Exercise 19-2. // Sorting in various ways using higher-order functions, functors, and lambda expressions. import ; import ; import ; import ; import sort; #include // For std::tolower() #include // For std::abs() // Output vector elements template void list(const std::vector& values, size_t width = 5) { for (auto value : values) std::cout << std::format("{:{}}", value, width); std::cout << std::endl; } int main() { std::vector numbers{ -2, 4, -5, 6, 10, -40, 56, 4, 67, 45 }; std::cout << "\nIntegers to be sorted:\n"; list(numbers); sort(numbers, std::greater<>{}); std::cout << "\nSorted integers:\n"; list(numbers); std::cout << "\nCharacters to be sorted:\n"; std::vector letters{ 'C', 'd', 'a', 'z', 't', 'S', 'p', 'm', 'D', 'f' }; list(letters, 2); sort(letters, [](char x, char y) { return std::tolower(x) < std::tolower(y); }); std::cout << "\nSorted characters:\n"; list(letters, 2); std::cout << "\nFloating-point values to be sorted:\n"; std::vector values{ -2.5, 1.4, -2.55, 6.3, 10.1, -40.5, 56.0, 4.7, 67.3, 45.0 }; list(values, 10); sort(values, [](double x, double y) { return std::abs(x) < std::abs(y); }); std::cout << "\nSorted floaating-point values:\n"; list(values, 10); } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_02/Sort.cppm ================================================ export module sort; import ; // for std::swap() import ; // The primary, exported sort() template with two function parameters calls // this internal sort() template with four parameters template void sort(std::vector& data, Compare compare, size_t start, size_t end); // Sort all vector elements export template void sort(std::vector& data, Compare compare) { if (!data.empty()) sort(data, compare, 0, data.size() - 1); } // Swap two vector elements template void swap(std::vector& data, size_t first, size_t second) { std::swap(data[first], data[second]); } // Sort a range of vector elements template void sort(std::vector& data, Compare compare, size_t start, size_t end) { // Start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle value to partition set swap(data, start, (start + end) / 2); // Swap middle value with start // Check data against chosen value size_t current{ start }; // The index of the last element less than the chosen element (after partitioning) for (size_t i{ start + 1 }; i <= end; ++i) { if (compare(data[i], data[start])) // Is value less than chosen word? swap(data, ++current, i); // Yes, so swap to the left } swap(data, start, current); // Swap the chosen value with last in if (current > start) sort(data, compare, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(data, compare, current + 1, end); // Sort right subset if exists } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_03/BubbleSort.cppm ================================================ export module sort.bubble; import ; // for std::swap() import ; // The sorting algorithm exported by this module export template void bubbleSort(std::vector& data, Compare compare); // Utility to swap two vector elements template void swap(std::vector& data, size_t first, size_t second) { std::swap(data[first], data[second]); } template void bubbleSort(std::vector& data, Compare compare) { if (data.empty()) return; while (true) { bool swapped{ false }; // Becomes true when not all values are in order for (size_t i {}; i < data.size() - 1; ++i) { if (compare(data[i+1], data[i])) // Out of order so swap them { swap(data, i, i+1); swapped = true; } } if (!swapped) // If there were no swaps break; // ...they are in order... } // ...otherwise, go round again. } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_03/Quicksort.cppm ================================================ export module sort.quick; import ; // for std::swap() import ; // The sorting algorithm exported by this module export template void quicksort(std::vector& values, Compare compare); // Utility to swap two vector elements template void swap(std::vector& data, size_t first, size_t second) { std::swap(data[first], data[second]); } // The primary, exported quicksort() template with two parameter // calls the quicksort() template with four parameters // Sort a range of vector elements template void quicksort(std::vector& data, Compare compare, size_t start, size_t end) { // Start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle value to partition set swap(data, start, (start + end) / 2); // Swap middle value with start // Check data against chosen value size_t current{ start }; // The index of the last element less than the chosen element (after partitioning) for (size_t i{ start + 1 }; i <= end; ++i) { if (compare(data[i], data[start])) // Is value less than chosen word? swap(data, ++current, i); // Yes, so swap to the left } swap(data, start, current); // Swap the chosen value with last in if (current > start) quicksort(data, compare, start, current - 1); // Sort left subset if exists if (end > current + 1) quicksort(data, compare, current + 1, end); // Sort right subset if exists } // Sort all vector elements using quicksort template void quicksort(std::vector& data, Compare compare) { if (!data.empty()) quicksort(data, compare, 0, data.size() - 1); } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_03/Soln19_03.cpp ================================================ // Exercise 19-3. Comparing sorting algorithms import ; import ; import ; // For random number generation import ; // For std::bind() and import ; // For std::ranges::sort() (bonus algorithm) import ; import sort; #include // For std::log2() /* If you look carefully, you will notice that our implementation of quicksort deviates from the theoretical expectations. The larger the input size, the more it deviates (as shown by the ratios in our output). Better implementations of quicksort are no doubt possible: on random data an optimal quicksort algorithm should perform, on average, at a ratio 1.39 of the log-linear best case performance. But better algorithms exist as well. As a reference, we also compared with the Standard Library's sort() algorithm you'll learn about in Chapter 20. This algorithm should perform very close to the theoretical optimum. */ // Function to generate a random integer 1 to 100 (both inclusive) // Caution: unlike uniform_real_distribution, uniform_int_distribution // generates numbers in a closed interval [min, max]. auto createUniformPseudoRandomNumberGenerator() { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ 1u, 100u }; // Generate in [0, 100] interval return std::bind(distribution, generator); //... and in the darkness bind them! } auto generateRandomNumbers(unsigned number) { static auto random{ createUniformPseudoRandomNumberGenerator() }; std::vector random_numbers; for (unsigned i{}; i < number; ++i) random_numbers.push_back(random()); return random_numbers; } int main() { unsigned count {}; const auto counting_less{ [&count](int x, int y) { ++count; return x < y; } }; for (auto size : { 500u, 1'000u, 2'000u, 4'000u }) { const auto numbers{ generateRandomNumbers(size) }; count = 0; // Reset the count auto copy{ numbers }; // Ensure both sorrting algorithms work on exact same random sequence quicksort(copy, counting_less); const auto quicksort_count{ count }; count = 0; // Repeat for bubble sort algorithm copy = numbers; bubbleSort(copy, counting_less); const auto bubble_sort_count{ count }; // Not requested, but it is interesting (see earlier) // to also compare with the sorting algorithm of the C++ Standard Library count = 0; // Repeat once more for std::ranges::sort() (see Chapter 20) copy = numbers; std::ranges::sort(copy, counting_less); const auto std_sort_count{ count }; const auto quick_sort_theory = static_cast(size * std::log2(size)); const auto bubble_sort_theory = size * size; const auto std_sort_theory = static_cast(size * std::log2(size)); std::cout << std::format( "Number of comparisons for {} elements:\n" " - quicksort: {} (n*log(n): {}; ratio: {:.2})\n" " - bubble sort: {} (n*n: {}; ratio: {:.2})\n" " - Standard Library sort: {} (n*log(n): {}; ratio: {:.2})\n", size, quicksort_count, quick_sort_theory, static_cast(quick_sort_theory) / quicksort_count, bubble_sort_count, bubble_sort_theory, static_cast(bubble_sort_theory) / bubble_sort_count, std_sort_count, std_sort_theory, static_cast(std_sort_theory) / std_sort_count ) << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_03/Sort.cppm ================================================ export module sort; // The two algorithms that we are comparing: export import sort.quick; export import sort.bubble; ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_04/Collect.cppm ================================================ export module collect; import ; export template std::vector collect(const std::vector& values, Predicate predicate) { std::vector result; for (auto& value : values) if (predicate(value)) result.push_back(value); return result; } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_04/Soln19_04.cpp ================================================ // Exercise 19-4. // Collecting values using higher-order functions, functors, and lambda expressions. /* There are several ways to check for palindromes. One of the more elegant solutions probably though is the recursive one shown here. While recursive lambdas are possible, more or less, recursion is far easier with a regular function. Note that to collect upper case letters, there's no need for a lambda either. Just use a pointer to the std::isupper() function as the callback! */ import ; import ; import ; import ; import ; import collect; #include ; // For std::isupper() // Output vector elements template void list(const std::vector& values, size_t width = 5) { for (auto value : values) std::cout << std::format("{:{}}", value, width); std::cout << std::endl; } bool is_palindrome(std::string_view s) { return s.length() == 0 || (s.front() == s.back() && is_palindrome(s.substr(1, s.length() - 2))); } int main() { const std::vector numbers{ -2, 4, -5, 6, 10, -40, 56, 4, 67, 45 }; std::cout << "\nAll numbers:\n"; list(numbers); int threshold {}; std::cout << "\nPlease enter a threshold: "; std::cin >> threshold; const auto greater{ collect(numbers, [threshold](int i) { return i > threshold; }) }; std::cout << "Numbers greater than " << threshold << ":\n"; list(greater); const std::vector letters{ 'C', 'd', 'a', 'z', 't', 'S', 'p', 'm', 'D', 'f' }; std::cout << "\nAll characters:\n"; list(letters, 2); //const auto capitals{ collect(letters, std::isupper) }; // <-- This should but does not work with all compilers const auto capitals{ collect(letters, isupper) }; std::cout << "\nCapital letters:\n"; list(capitals, 2); const std::vector strings{ "palindrome", "racecar", "rubarb", "noon", "kayak", "ananas", "madam", "backwards" }; std::cout << "\nAll strings:\n"; list(strings, 12); const auto palindromes{ collect(strings, is_palindrome) }; std::cout << "\nPalindromes:\n"; list(palindromes, 10); } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_05/Box.cppm ================================================ export module box; import ; import ; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.m_length, box.m_width, box.m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_05/DeliveryTruck.cpp ================================================ module delivery_truck; DeliveryTruck::DeliveryTruck(Truckload aTruckload) : m_truckload{ std::move(aTruckload) } // Do not copy! {} void DeliveryTruck::deliverBox(SharedBox box) { m_truckload.removeBox(box); // Notify all interested parties (aka "observers") that the Box was delivered for (auto& callback : m_callbacks) callback(box); } void DeliveryTruck::registerOnDelivered(Callback callback) { m_callbacks.push_back(std::move(callback)); // Do not copy! } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_05/DeliveryTruck.cppm ================================================ export module delivery_truck; import truckload; import ; // For std::function<> import ; // For std::vector<> export class DeliveryTruck { public: using Callback = std::function; // Type alias for the type of the delivery callback functions DeliveryTruck(Truckload truckload); // Create a delivery truck (pass-by-value to allow a Truckload to be either copied or moved!) void deliverBox(SharedBox box); void registerOnDelivered(Callback callback); private: Truckload m_truckload; std::vector m_callbacks; }; ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_05/RandomBoxes.cppm ================================================ export module box.random; import box; import ; // For random number generation import ; // For std::bind() import ; // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } export Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } export auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_05/Soln19_05.cpp ================================================ // Exercise 19-5. // Using callback functions to implement the so-called observer pattern, // where a callback function is called whenever a certain event occurs // (in this case: when a Box is delivered). import ; import truckload; import delivery_truck; import box.random; void logDelivary(SharedBox box) { std::cout << "The box " << *box << " was delivered. On time, as always!" << std::endl; } int main() { const size_t boxCount {20}; // Number of Box object to be created // Create boxCount Box objects Truckload load; for (size_t i {}; i < boxCount; ++i) load.addBox(randomSharedBox()); DeliveryTruck truck{ load }; // Copy the load, because we still need it below. // Note that all Boxes are shared, so the they themselves are not copied. // Register two callback functions: truck.registerOnDelivered(logDelivary); unsigned count {}; truck.registerOnDelivered([&count](SharedBox) { ++count; }); // Deliver some boxes: for (size_t i : { 5u, 8u, 11u }) truck.deliverBox(load[i]); std::cout << count << " boxes were delivered. On time, as always!" << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_05/Truckload.cpp ================================================ module truckload; import ; import ; // For standard exception type std::out_of_range import ; // For std::string and std::to_string() // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } Truckload& Truckload::operator=(const Truckload& other) { if (&other != this) // Do not forget: avoid issues with self-assignment! { delete m_head; // Delete all current packages m_head = m_tail = nullptr; // Reset both pointers // Same as copy constructor // (see Chapter 17 for the copy-and-swap idiom that allows you to avoid // duplicating logic like this...) for (Package* package{ other.m_head }; package; package = package->m_next) { addBox(package->m_box); } } return *this; } // Destructor: clean up the list Truckload::~Truckload() { delete m_head; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox& Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } throw std::out_of_range{ "Index too large: " + std::to_string(index) }; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Exercises/Modules/Chapter 19/Soln19_05/Truckload.cppm ================================================ export module truckload; import box; import ; import ; import ; export using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor Truckload& operator=(const Truckload& other); // Copy assignment operator ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in a module interface file) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; export std::ostream& operator<<(std::ostream& stream, const Truckload& load); ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_01/Box.cppm ================================================ export module box; import ; import ; export class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.m_length, box.m_width, box.m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_01/RandomBoxes.cppm ================================================ export module box.random; import box; import ; // For random number generation import ; // For std::bind() import ; // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } export Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } export auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_01/Soln20_01.cpp ================================================ // Exercise 20-1 Rework the Truckload class using std::vector<> import ; import ; import truckload; import box.random; /* In this solution we preserved the interface of the nested Iterator class. An alternative, of course, would be to define Iterator as an alias for std::vector::iterator, and introduce begin() and end() members. But then all code using the iterators would have to be updated as well. */ int main() { const double dimLimit{ 99.0 }; // Upper limit on Box dimensions Truckload load; const size_t boxCount{ 20 }; // Number of Box object to be created // Create boxCount Box objects for (size_t i{}; i < boxCount; ++i) load.addBox(randomSharedBox()); std::cout << "The boxes in the Truckload are:\n"; std::cout << load << std::endl; Truckload moveConstructedLoad{ std::move(load) }; std::cout << "The boxes in the move constructed Truckload are:\n"; std::cout << moveConstructedLoad << std::endl; Truckload moveAssignedLoad; moveAssignedLoad = std::move(moveConstructedLoad); std::cout << "The boxes in the move assigned Truckload are:\n"; std::cout << moveAssignedLoad << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_01/Truckload.cpp ================================================ module truckload; import ; import ; // Constructor - one Box Truckload::Truckload(SharedBox box) : m_boxes{ 1, box } { } // Constructor - vector of Boxes Truckload::Truckload(std::vector boxes) : m_boxes{ std::move(boxes) } { } // Swap assignment operator (noexcept) void Truckload::swap(Truckload& other) noexcept { m_boxes.swap(other.m_boxes); } // Optional yet conventional non-member function (forwards to member function) void swap(Truckload& one, Truckload& other) noexcept { one.swap(other); } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_boxes }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_boxes->begin(); return m_current != m_boxes->end() ? *m_current : SharedBox{}; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (m_current == m_boxes->end()) // If there's no current... return getFirstBox(); // ...return the 1st Box ++m_current; // Move to the next package return m_current != m_boxes->end() ? *m_current : SharedBox{}; } void Truckload::addBox(SharedBox box) { m_boxes.push_back(std::move(box)); } bool Truckload::removeBox(SharedBox boxToRemove) { // Unlike the original implementation, // std::erase() removes all occurrences of boxToRemove. // If that is not acceptable, you can use find() as shown below... return std::erase(m_boxes, boxToRemove) > 0; // Remove only the first occurrence of boxToRemove /* if (auto found{ std::ranges::find(m_boxes, boxToRemove) }; found != end(m_boxes)) { m_boxes.erase(found); return true; } else { return false; } */ } SharedBox& Truckload::operator[](size_t index) { // Original implementation performed bounds checking, so use at() instead of [] return m_boxes.at(index); } SharedBox Truckload::operator[](size_t index) const { // Original implementation performed bounds checking, so use at() instead of [] return m_boxes.at(index); } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_01/Truckload.cppm ================================================ export module truckload; import box; import ; import ; import ; export using SharedBox = std::shared_ptr; export class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(std::vector boxes); // Constructor - vector of Boxes (accept by value!) void swap(Truckload& other) noexcept; class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index); // Overloaded subscript operator (can no longer be const!) SharedBox operator[](size_t index) const; // Overloaded subscript operator (new const version returns by value) private: std::vector m_boxes; }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in a module interface file) // Note that this is effectively a const iterator... class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: const std::vector* m_boxes; // Pointer to the underlying vector std::vector::const_iterator m_current; // Iterator pointing to the Box that was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(const std::vector& boxes) : m_boxes{ &boxes }, m_current{ boxes.end() } {} }; export std::ostream& operator<<(std::ostream& stream, const Truckload& load); // Optional yet conventional non-member function (forwards to member function) export void swap(Truckload& one, Truckload& other) noexcept; ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_02/Soln20_02.cpp ================================================ // Exercise 20-2 // Replace a custom stack container by the standard stack container adapter import ; import ; import ; /* Two challenges: - std::stack<>::pop() is a void function. To access the top of the stack, you use std::stack<>::top(). - std::stack is not allowed */ int main() { std::string words[]{ "The", "quick", "brown", "fox", "jumps" }; std::stack wordStack; // A stack of strings for (const auto& word : words) wordStack.push(word); std::stack newStack{ wordStack }; // Create a copy of the stack // Display the words in reverse order while (!newStack.empty()) { std::cout << newStack.top() << ' '; newStack.pop(); } std::cout << std::endl; // Reverse wordStack onto newStack while (!wordStack.empty()) { newStack.push(wordStack.top()); wordStack.pop(); } // Display the words in original order while (!newStack.empty()) { std::cout << newStack.top() << ' '; newStack.pop(); } std::cout << std::endl; std::cout << std::endl << "Enter a line of text:" << std::endl; std::string text; std::getline(std::cin, text); // Read a line into the string object std::stack characters; // A stack for characters for (size_t i{}; i < text.length(); ++i) characters.push(text[i]); // Push the string characters onto the stack std::cout << std::endl; while (!characters.empty()) { std::cout << characters.top(); // Pop the characters off the stack characters.pop(); } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_03/Soln20_03.cpp ================================================ // Exercise 20-3 Replacing custom container types with standard ones /* The following replacements were made compared to Soln17_06.cpp: - LinkedList --> std::vector (not std::list<>, because vector<> should be your go-to container; there's rarely a good reason to use linked lists) - SparseArray --> std::map (no need to use a size_t as the key!) Notice how much more elegant inserting and retrieving values from the map is. All loops at the end of the solution can now also be replaced with elegant range-based for loops. At the bottom, there's two alternative loops: one close to the original one, and one even more elegant one that takes advantage of the fact that the keys in the map are already sorted. Soln20_03A contains alternative solutions based on std::multimap<> */ import ; import ; import ; import ; import ; #include int main() { std::string text; // Stores input prose or poem std::cout << "Enter a poem or prose over one or more lines.\n" << "Terminate the input with #:\n"; getline(std::cin, text, '#'); std::map> lists; // Extract words and store in the appropriate list const std::string_view separators {" \n\t,.\"?!;:"}; // Separators between words size_t start {}; // Start of a word size_t end {}; // separator position after a word while (std::string::npos != (start = text.find_first_not_of(separators, start))) { end = text.find_first_of(separators, start+1); const auto word{ text.substr(start, end - start) }; const auto letter{ static_cast(std::toupper(word[0])) }; lists[letter].push_back(word); start = end; } // List the words in order 5 to a line const size_t perline {5}; /* Option 1: use a loop similar to the original one */ const std::string_view letters{ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" }; for (char letter : letters) { if (!lists.contains(letter)) continue; size_t count {}; // Word counter for (const auto& word : lists[letter]) { if (count++ % perline == 0 && count != 1) std::cout << std::endl; std::cout << word << ' '; } std::cout << std::endl; } std::cout << std::endl; /* Option 2: take advantage of the fact that the keys are already sorted in the map */ for (const auto& [letter, list] : lists) { size_t count{}; // Word counter for (const auto& word : list) { if (count++ % perline == 0 && count != 1) std::cout << std::endl; std::cout << word << ' '; } std::cout << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_03A/Soln20_03A.cpp ================================================ // Exercise 20-3 Replacing custom container types with standard ones /* The following replacements were made compared to Soln17_06.cpp: - SparseArray> --> std::multimap This means that compared to Soln20_03, we replaced std::map> with the more compact (and efficient) std::multimap. This data structure was less discussed in the main text (as it is less used in practice), but is actually more appropriate here. At the bottom, there's two alternative loops: one close to the original one, and one that takes advantage of the fact that the keys in the multimap are sorted. */ import ; import ; import ; import ; import ; #include int main() { std::string text; // Stores input prose or poem std::cout << "Enter a poem or prose over one or more lines.\n" << "Terminate the input with #:\n"; getline(std::cin, text, '#'); std::multimap words; const std::string_view letters{ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" }; // Extract words and store in the appropriate list // A list in the SparseArray is selected by the index in letters of the first letter in a word. const std::string_view separators{ " \n\t,.\"?!;:" }; // Separators between words size_t start{}; // Start of a word size_t end{}; // separator position after a word while (std::string::npos != (start = text.find_first_not_of(separators, start))) { end = text.find_first_of(separators, start + 1); const auto word{ text.substr(start, end - start) }; const auto letter{ static_cast(std::toupper(word[0])) }; words.insert({ letter, word }); start = end; } // List the words in order 5 to a line const size_t perline{ 5 }; /* Option 1: use a loop similar to the original one */ for (char letter : letters) { if (!words.contains(letter)) continue; size_t count{}; // Word counter // Retrieve the range of all words that begin with letter const auto [begin, end] { words.equal_range(letter) }; for (auto iter{ begin }; iter != end; ++iter) { if (count++ % perline == 0 && count != 1) std::cout << std::endl; std::cout << iter->second << ' '; } std::cout << std::endl; } std::cout << std::endl; /* Option 2: take advantage of the fact that the keys are already sorted in the multimap */ size_t count{}; // Word counter char previous_letter{}; for (const auto& [letter, word] : words) { // Add extra enter after each new letter (but not the first time) if (count && letter != previous_letter) { std::cout << std::endl; count = 0; } if (count++ % perline == 0 && count != 1) std::cout << std::endl; std::cout << word << ' '; previous_letter = letter; } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_04/Soln20_04.cpp ================================================ // Removing all elements that satisfy a certain condition using std::partition() / stable_partition(). /* Notes: - Normally one would use std::remove() (as this is slightly faster and more to the point), though std::partition() can be used for this as well. - You may notice that std::partition() reorders the elements that it keeps. It is implementation-defined whether this partition() preserves the original order or not. If you want to make sure the original order of the numbers is preserved, you need to use std::stable_partition() */ import ; import ; import ; import ; std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); void removeEvenNumbers(std::vector& numbers) { const auto first_even_number { std::stable_partition(begin(numbers), end(numbers), [](auto num) { return num % 2 == 1; }) }; /* ^^^^^^^^^^^^^^^^ if you use partition(), the order of the odd elements may become scrambled */ numbers.erase(first_even_number, end(numbers)); } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers; for (int i{ 1 }; i <= N; ++i) numbers.push_back(i); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_05/Soln20_05.cpp ================================================ // Exercise 20-5 Create a generic average() algorithm using std::accumulate() import ; import ; import ; // for std::pair<> (only required for Solution 2 below) import ; import ; // Solution 1: simply use accumulate to sum, and determine the count using std::distance() // (the latter is more general than using iterator arithmetic, end - begin, // which only works for random-access iterators) template std::optional average(IterType begin, IterType end) { const auto count{ std::distance(begin, end) }; const auto sum{ std::accumulate(begin, end, T{}) }; return count ? std::optional{ sum / count } : std::nullopt; } // Solution 2: accumulate a pair<> that counts both the number of elements and the sum //template //std::optional average(IterType begin, IterType end) //{ // const auto accumulated { // std::accumulate(begin, end, std::make_pair(0u, T{}), [](const auto& accumulated, const auto& element) // { // return std::make_pair(accumulated.first + 1, accumulated.second + element); // }) // }; // // return accumulated.first ? std::optional{ accumulated.second / accumulated.first } : std::nullopt; //} int main() { std::vector numbers{ 1, 2, 4, 8, 16, 32, 64, 128, 256 }; std::cout << *average(begin(numbers), end(numbers)) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_06/Soln20_06.cpp ================================================ // Removing all elements that satisfy a certain condition // while iterating over a container import ; import ; import ; import ; // for std::remove_if() import ; // for std::iota() /* Note: std::ranges::iota() does not exist. No doubt because the module defines a std::ranges::views::iota range factory. Maybe you can alter this solution to use that view instead to fill the vector? (Note: in the next exercise you are asked to not use a vector at all, but operate directly with views...) */ std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); template void removeEvenNumbers(Auto& numbers) /* Using more elegant std::erase_if() */ { std::erase_if(numbers, [](auto number) { return number % 2 == 0; }); } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers(N); std::iota(begin(numbers), end(numbers), 1); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_07/Soln20_07.cpp ================================================ // Removing all elements that satisfy a certain condition // while iterating over a container import ; import ; import ; import ; using namespace std::ranges::views; template void printVector(std::string_view message, Auto& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } int main() { const size_t num_numbers{ 20 }; auto numbers{ iota(1, 20) }; printVector("The original set of numbers", numbers); auto odd_numbers{ numbers | filter([](int i) { return i % 2 != 0; }) }; printVector("The numbers that were kept", odd_numbers); } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_08/Soln20_08.cpp ================================================ // Exercise 20-8 Apply remove-erase idiom to remove duplicate elements import ; import ; // For random number generation import ; // For std::bind() and std::ref() import ; import ; // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_numbers{ 25'000 }; std::vector numbers(num_numbers); // Why not use an algorithm as well to generate the random numbers!! // (Note: technically generate() is allowed to copy a function object // as often as it wants. std::ref() ensures that the algorithm // at all times operates on a reference to our random-number generator) auto generator{ createUniformPseudoRandomNumberGenerator(0, 10'000) }; std::ranges::generate(numbers, std::ref(generator)); // Algorithm number two std::ranges::sort(numbers); // And number three; this time combined with the remove-erase idiom (or unique-erase, if you will) /* Option 1: iterator-based */ numbers.erase(std::unique(begin(numbers), end(numbers)), end(numbers)); /* Option 2: range-based (sadly requires two statements...) */ //const auto [to_erase_begin, to_erase_end] { std::ranges::unique(numbers) }; //numbers.erase(to_erase_begin, to_erase_end); std::cout << "Number of unique numbers: " << numbers.size() << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_09/Soln20_09.cpp ================================================ // Exercise 20-9 Parallel version of 20-8 import ; import ; // For random number generation import ; // For std::bind() and std::ref() import ; import ; import ; // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_numbers{ 25'000 }; std::vector numbers(num_numbers); /* Caution: a pseudo-random number generator cannot safely be accessed concurrently! */ auto generator{ createUniformPseudoRandomNumberGenerator(0, 10'000) }; std::ranges::generate(numbers, std::ref(generator)); // Algorithm number two std::sort(std::execution::par, begin(numbers), end(numbers)); // And number three; this time combined with the remove-erase idiom (or unique-erase, if you will) numbers.erase(std::unique(std::execution::par, begin(numbers), end(numbers)), end(numbers)); std::cout << "Number of unique numbers: " << numbers.size() << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_10/Soln20_10.cpp ================================================ // Exercise 20-10: exercising projection with range-based algorithms import ; import ; import ; import ; int main() { std::vector names{"Frodo Baggins", "Gandalf the Gray", "Aragon", "Samwise Gamgee", "Peregrin Took", "Meriadoc Brandybuck", "Gimli", "Legolas Greenleaf", "Boromir"}; // Sort the names lexicographically std::ranges::sort(names); std::cout << "Names sorted lexicographically:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl << std::endl; // Sort the names by length /* Option 1: projection using member function pointer */ std::ranges::sort(names, std::less<>{}, &std::string::length); /* Option 2: projection using lambda expression */ // std::ranges::sort(names, std::less<>{}, [](const auto& name) { return name.length(); }); std::cout << "Names sorted by length:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_11/Soln20_11.cpp ================================================ // Exercise 20-11: exercising range factories and range adaptors import ; import ; import ; #include using namespace std::ranges::views; bool isPrime(unsigned number) { // Of course we use an algorithm and a range factory here as well! // Caution: mind the corner cases where number is 0, 1, or 2! return number >= 2 && std::ranges::none_of( iota(2u, static_cast(std::sqrt(number) + 1)), [number](unsigned divisor) { return number % divisor == 0; } ); } int main() { const unsigned num_primes{ 100 }; const unsigned per_line{ 10 }; unsigned count{}; for (auto prime : iota(2) | filter(&isPrime) | take(num_primes) | reverse) { std::cout << prime << ' '; if (++count % per_line == 0) std::cout << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_12/Soln20_12.cpp ================================================ // Exercise 20-12: exercising new range adaptors import ; import ; import ; #include using namespace std::ranges::views; bool isPrime(unsigned number) { // Of course we use an algorithm and a range factory here as well! // Caution: mind the corner cases where number is 0, 1, or 2! return number >= 2 && std::ranges::none_of( iota(2u, static_cast(std::sqrt(number) + 1)), [number](unsigned divisor) { return number % divisor == 0; } ); } int main() { const unsigned max{ 1'000 }; const unsigned per_line{ 10 }; // As you no doubt deduced, drop_while() is not really applicable here, // but take_while() fits nicely! unsigned count{}; for (auto prime : iota(2u) | filter(&isPrime) | take_while([max](unsigned prime) { return prime < max; }) | reverse) { std::cout << prime << ' '; if (++count % per_line == 0) std::cout << std::endl; } } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_13/Soln20_13.cpp ================================================ // Exercise 20-13: more fun with algorithms and ranges import ; import ; import ; import ; import ; import ; import ; import ; using namespace std::ranges::views; // Type aliases using Words = std::vector; using WordCounts = std::map; // Function prototypes Words extractWords(std::string_view text, std::string_view separators = " ,.!?\"\n"); WordCounts countWords(const Words& words); void showWordCounts(const WordCounts& wordCounts); /* Below we list a number of possible solutions. We're sure there are plenty more variations possible... */ size_t maxWordLength(const WordCounts& wordCounts) { // Filter out words that occur only once + transform to word lengths using lambda expression auto frequentWordLengths { wordCounts | filter([](const auto wordCount) { return wordCount.second >= 2; }) | transform([](const auto wordCount) { return wordCount.first.length(); }) }; return std::ranges::empty(frequentWordLengths) ? 0 : *std::ranges::max_element(frequentWordLengths); } size_t maxWordLength_1(const WordCounts& wordCounts) { // Filter out words that occur only once, transform twice in a row with member pointers. auto frequentWordLengths { wordCounts | filter([](const auto wordCount) { return wordCount.second >= 2; }) | transform(&WordCounts::value_type::first) // Or, without alias: transform(&std::pair::first) | transform(&std::string_view::length) }; return std::ranges::empty(frequentWordLengths) ? 0 : *std::ranges::max_element(frequentWordLengths); } size_t maxWordLength_2(const WordCounts& wordCounts) { // Filter out words that occur only once, transform to words, project to lengths. auto frequentWords { wordCounts | filter([](const auto wordCount) { return wordCount.second >= 2; }) | transform([](const auto wordCount) { return wordCount.first; }) }; return std::ranges::empty(frequentWords) ? 0 : (*std::ranges::max_element(frequentWords, std::less<>{}, &std::string_view::length)).length(); } size_t maxWordLength_3(const WordCounts& wordCounts) { // Filter out words that occur only once auto frequentWordCounts { wordCounts | filter([](const auto wordCount) { return wordCount.second >= 2; }) }; // Project to obtain the words (the first element of the pairs in the map), // and compare their lengths using a lambda expression return std::ranges::empty(frequentWordCounts) ? 0 : std::ranges::max_element( frequentWordCounts, [](auto word1, auto word2) { return word1.length() < word2.length(); }, &WordCounts::value_type::first // Or, without alias: std::pair::first )->first.size(); } int main() { std::string text; // The string to count words in // Read a string from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); const Words words{ extractWords(text) }; if (words.empty()) { std::cout << "No words in text." << std::endl; return 0; } const WordCounts wordCounts{ countWords(words) }; showWordCounts(wordCounts); } Words extractWords(std::string_view text, std::string_view separators) { Words words; size_t start{ text.find_first_not_of(separators) }; // Start 1st word size_t end{}; // Index for the end of a word while (start != std::string_view::npos) { end = text.find_first_of(separators, start + 1); // Find end separator if (end == std::string_view::npos) // End of text? end = text.length(); // Yes, so set to last+1 words.push_back(text.substr(start, end - start)); start = text.find_first_not_of(separators, end + 1); // Find next word } return words; } WordCounts countWords(const Words& words) { WordCounts result; for (auto& word : words) ++result[word]; return result; } void showWordCounts(const WordCounts& wordCounts) { const size_t field_width{ maxWordLength(wordCounts) + 1}; const size_t words_per_line{5}; size_t words_in_line{}; // Number of words in the current line char previous_initial{}; for (const auto& [word, count] : wordCounts) { if (count < 2) continue; // Skip words that appear only once // Output newline when initial letter changes or after 5 per line if ( (previous_initial && word[0] != previous_initial) || words_in_line++ == words_per_line) { words_in_line = 0; std::cout << std::endl; } // Output "word (count)", where word has a dynamic field width std::cout << std::format("{:>{}} ({:2})", word, field_width, count); previous_initial = word[0]; } std::cout << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 20/Soln20_14/Soln20_14.cpp ================================================ // Exercise 20-14: the hard nut import ; import ; import ; import ; #include using namespace std::ranges::views; bool isPrime(unsigned number) { return number >= 2 && std::ranges::none_of( iota(2u, static_cast(std::sqrt(number) + 1)), [number](unsigned divisor) { return number % divisor == 0; } ); } int main() { // This first version is one that compiled (with some help) on at least one compiler. // It uses one thing you have not encountered yet: std::ranges::views::common. // This range adaptor transforms a given range into a view where begin() and end() // return values of the same type, something which in general is not true for ranges. // Legacy algorithms, such as the iterator-pair-based std::copy() algorithm, // often expect iterator pairs to have the same type, though. auto view { std::ranges::istream_view(std::cin) | take_while([](int i) { return i >= 0; }) | transform([](int i) { return static_cast(i); }) | filter(isPrime) | transform([](unsigned i) { return "Yes, that is a prime!"; }) | common }; std::copy(view.begin(), view.end(), std::ostream_iterator(std::cout, "\n")); /* // Same as the previous one, but then without the legacy iterator-pair-based copy() // (and thus without the common range adaptor). // At the time we tried this, this did not compile yet in any compiler. auto view { std::ranges::istream_view(std::cin) | take_while([](int i) { return i >= 0; }) | transform([](int i) { return static_cast(i); }) | filter(isPrime) | transform([](unsigned i) { return "Yes, that is a prime!"; }) }; std::ranges::copy(view, std::ostream_iterator(std::cout, "\n")); */ /* // A variation that uses std::ranges::for_each(). // But, like the exercise says, this is cheating though, // as for_each() is just a poorly disguised range-based for loop, // and loops were not allowed! std::ranges::for_each( std::ranges::istream_view(std::cin) | take_while([](int i) { return i >= 0; }) | transform([](int i) { return static_cast(i); }) | filter(isPrime) | transform([](unsigned i) { return "Yes, that is a prime!"; }) , [](const auto& s) { std::cout << s << std::endl; } ); */ /* // A "clever" version where we print during a filter step auto view { std::ranges::istream_view(std::cin) | take_while([](int i) { return i >= 0; }) | transform([](int i) { return static_cast(i); }) | filter(isPrime) | filter([](unsigned i) { std::cout << "Yes, that is a prime!\n"; return false; }) }; view.begin(); // Try to find the begin of the view // Since the last step filters out all elements, though, // this will never find a begin, and eventually return view.end()... */ } ================================================ FILE: Exercises/Modules/Chapter 21/Soln21_01/Soln21_01.cpp ================================================ // Exercise 21-1 Understanding compound and simple requirements import ; // For the std::same_as<> and std::convertible_to<> concepts import ; // For std::ranges::range<> concept import ; // For the std::remove_cv<> type trait import ; import ; import ; template concept BidirectionalIterator = true; // Feel free to further work out all requirements... // A compound requirement "{ expr } -> concept;" is equivalent to // the simple requirement "expr;", // combined with the nested requirement "requires concept;" template concept RandomAccessIterator = BidirectionalIterator && requires(const Iter i, const Iter j, Iter k, const int n) { { i - n }; { i + n }; { n + i }; requires std::same_as; requires std::same_as; requires std::same_as; { k += n }; { k -= n }; requires std::same_as; requires std::same_as; i[n]; requires std::same_as; i < j; i > j; i <= j; i >= j; requires std::convertible_to && std::convertible_to j), bool> && std::convertible_to && std::convertible_to= j), bool>; }; // To rewrite a compound requirement with noexcept you should know // that noexcept(expr) is a valid constant expression as well... template concept NoExceptDestructible = requires (T& value) { value.~T(); noexcept(value.~T()); }; template concept Character = std::same_as, char> || std::same_as, char8_t> || std::same_as, char16_t> || std::same_as, char32_t> || std::same_as, wchar_t>; // More of the same... template concept String = std::ranges::range && requires(S & s, const S & cs) { typename S::value_type; requires Character; cs.length(); requires std::integral; s[0]; cs[0]; requires std::same_as && std::convertible_to; s.data(); cs.data(); requires std::same_as && std::same_as; // ... }; static_assert(NoExceptDestructible); static_assert(NoExceptDestructible); static_assert(String); static_assert(!String>); static_assert(Character); static_assert(Character); static_assert(RandomAccessIterator::iterator>); static_assert(!RandomAccessIterator::iterator>); static_assert(RandomAccessIterator); int main() { } ================================================ FILE: Exercises/Modules/Chapter 21/Soln21_02/Soln21_02.cpp ================================================ // Exercise 21-2 Writing a proper, modern algorithm is hard... // It involves adding support for projection, member pointers, // and iterator pairs where the end iterator (or, sentinel) // is of a different type than the begin iterator, // all with proper type constraints. import ; import ; // Standard concepts import ; // Range factories and adaptors import ; // For the std::identity and std::ranges::less functor classes, and std::invoke() import ; // Iterator-based concepts, std::sentinel_for, and std::projected import ; using namespace std::ranges::views; // The original function template which requires begin and end to be of the same type // Similar to the original iterator-pair based algorithms of , // only with type constraints. template Comparison> Iterator original_find_optimum(Iterator begin, Iterator end, Comparison compare) { if (begin == end) return end; Iterator optimum{ begin }; for (auto iter{ ++begin }; iter != end; ++iter) { if (compare(*iter, *optimum)) optimum = iter; } return optimum; } // Our improved function template supporting projection and differently-typed sentinels. // Appropriate default values were added as well. template Sentinel, typename Projection = std::identity, std::indirect_strict_weak_order> Comparison = std::ranges::less> Iterator find_optimum(Iterator begin, Sentinel end, Comparison compare = {}, Projection projection = {}) { if (begin == end) return begin; // Do not return end as before... Iterator optimum{ begin }; for (auto iter{ ++begin }; iter != end; ++iter) { // Note 1: std::invoke() is required to support comparison and projection through member pointers // Note 2: this re-invokes projection(*optimum) for each comparison, same as std::max_element() if (std::invoke(compare, std::invoke(projection, *iter), std::invoke(projection, *optimum))) optimum = iter; } return optimum; } /* A simple Box class to exercise our member-based comparison and projection facilities. It lacks an operator<(), so with classical algorithms you'd need lambdas to compare these Boxes... */ class Box { public: Box() : Box{ 1, 1, 1 } {}; Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } {} double volume() const { return m_length * m_width * m_height; } double isSmallerThan(const Box& other) const { return volume() < other.volume(); } private: double m_length, m_width, m_height; }; int main() { std::vector boxes{ {2.0, 2.0, 3.0}, {1.0, 3.0, 2.0}, {1.0, 2.0, 1.0}, {2.0, 3.0, 3.0} }; // Step 1: create a view where begin() and end() are different. // The result of the take_while() range adapter has this property // (or, at least, it should have as per its specification in the Standard). auto first_boxes{ boxes | take_while([](const Box& box) { return box.volume() < 15; }) }; /* This therefore generally does not compile... */ //std::cout << "Volume of smallest box: " // << original_find_optimum(first_boxes.begin(), first_boxes.end(), // [](const Box& box1, const Box& box2) { return box1.isSmallerThan(box2); })->volume() // << std::endl; // Side-note: you can use the std::ranges::views::common to turn a range // where begin and end have a different type into a view where they do. auto common_boxes{ first_boxes | common }; std::cout << "Volume of smallest box: " << original_find_optimum(common_boxes.begin(), common_boxes.end(), [](const Box& box1, const Box& box2) { return box1.isSmallerThan(box2); })->volume() << std::endl; // Step 2: Show off our shiny new, modernised version of find_optimum() // First off: no need for the common adapter! std::cout << "Volume of largest box: " << find_optimum(first_boxes.begin(), first_boxes.end(), [](const Box& box1, const Box& box2) { return !box1.isSmallerThan(box2); })->volume() << std::endl; // Other cool features of our modern find_optimum() version include projection // and the support for member function pointers. So no more lambdas required here! // Use a member-function pointer for the comparison function std::cout << "Volume of smallest box: " << find_optimum(first_boxes.begin(), first_boxes.end(), &Box::isSmallerThan)->volume() << std::endl; // Use a member-function pointer for the new projection functionality std::cout << "Volume of largest box: " << find_optimum(first_boxes.begin(), first_boxes.end(), std::greater<>{}, &Box::volume)->volume() << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 21/Soln21_03/Soln21_03.cpp ================================================ // Exercise 21-3 import ; import ; import ; import ; import ; /* Not requested, but just for fun, here is a concept that prescribes a complete set of operations one would require to take an average */ template concept Averagable = requires (const T x, const T y, T z, const int i) { { x + y } -> std::same_as; { x - y } -> std::same_as; { z += y } -> std::same_as; { z -= y } -> std::same_as; { x / i } -> std::same_as; { x * i } -> std::same_as; { z /= i } -> std::same_as; { z *= i } -> std::same_as; }; // By default, take the N / 2'th element // (Note: this template covers both the case if T would not be Averageble, // and the case where N % 2 == 1). template auto& medianOfSorted(std::span span) { static_assert(N != 0, "The median of an empty span is not defined"); return span[N / 2]; } template requires (N % 2 == 0) auto medianOfSorted(std::span span) { static_assert(N != 0, "The median of an empty span is not defined"); return (span[N / 2 - 1] + span[N / 2]) / 2; } int main() { std::array values_odd{ 1, 2, 3, 4, 5, 6, 7 }; std::cout << medianOfSorted(std::span{ values_odd }) << std::endl; std::array values_even{ 1., 2., 3., 4., 5., 6. }; std::cout << medianOfSorted(std::span{ values_even }) << std::endl; std::string strings_odd[] { "1", "2", "3", "4", "5" }; std::cout << medianOfSorted(std::span{ strings_odd }) << std::endl; std::string strings_even[] { "1", "2", "3", "4", "5", "6" }; std::cout << medianOfSorted(std::span{ strings_even }) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 21/Soln21_04/Soln21_04.cpp ================================================ // Exercise 21-4 Generalising medianOfSorted() import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; /* Apologies. When we wrote the exercise, we misread what a std::sized_range() was. std::sized_range() is no concept for statically fixed-size ranges, but for ranges for which std::ranges::size() may be invoked *at runtime*. So this solution presents the next best thing: a medianOfSorted() that works for any range (not just fixed-size ranges), with appropriate type constraints. Key point is that underlying, range-based algorithms still very much work with iterators. Most requirements use Standard Library concepts in the std::ranges namespace, and the function implementations use Standard Library functions in that same namespace. */ /* Not requested, but just for fun, here is a concept that prescribes a complete set of operations one would require to take an average */ template concept Averagable = requires (const T x, const T y, T z, const int i) { { x + y } -> std::same_as; { x - y } -> std::same_as; { z += y } -> std::same_as; { z -= y } -> std::same_as; { x / i } -> std::same_as; { x * i } -> std::same_as; { z /= i } -> std::same_as; { z *= i } -> std::same_as; }; // By default, take the N/2'th element template // Requires being able to determine the range's size using std::ranges::size() requires std::ranges::input_range // Requires being able to dereference an iterator over the range. auto& medianOfSorted(Range&& range) { auto iter{ std::ranges::begin(range) }; // Supported for every range std::ranges::advance(iter, std::ranges::size(range) / 2); // Advances in constant time for random-access ranges, linearly otherwise. return *iter; // Only possible with input iterators... } // When we can take an average, check the size // (dynamically, not statically as was the original intent) // to decide whether we need to do so... template requires std::ranges::forward_range // More strict than before: we need the multi-pass guarantee && Averagable> auto medianOfSorted(Range&& range) { const auto N{ std::ranges::size(range) }; auto iter{ std::ranges::begin(range) }; if (N % 2 == 0) { std::ranges::advance(iter, N / 2 - 1); const auto& value1{ *iter }; // Reference remains valid because iter is a forward iterator! std::ranges::advance(iter, 1); return (value1 + *iter) / 2; } else { std::ranges::advance(iter, N / 2); return *iter; } } int main() { std::array values_odd{ 1, 2, 3, 4, 5, 6, 7 }; std::cout << medianOfSorted(values_odd) << std::endl; std::array values_even{ 1., 2., 3., 4., 5., 6. }; std::cout << medianOfSorted(values_even) << std::endl; std::string strings_odd[] { "1", "2", "3", "4", "5" }; std::cout << medianOfSorted(strings_odd) << std::endl; std::string strings_even[] { "1", "2", "3", "4", "5", "6" }; std::cout << medianOfSorted(std::span{ strings_even }) << std::endl; std::vector dynamically_sized{ 1.f, 2.f, 3.f, 4.f }; std::cout << medianOfSorted(dynamically_sized) << std::endl; std::list non_random_access{ 4.f, 3.f, 2.f, 1.f, 0.f }; std::cout << medianOfSorted(non_random_access) << std::endl; std::forward_list non_sized_ranged{ 123, 456, 789 }; //std::cout << medianOfSorted(non_sized_ranged) << std::endl; /* Error: not sized! */ } ================================================ FILE: Exercises/Modules/Chapter 21/Soln21_05/Soln21_05.cpp ================================================ // Exercise 21-5 Recreating std::advance() using constraint-based specialisation import ; import ; import ; import ; import ; template auto my_advance(Iter iter, std::iter_difference_t n) { return iter + n; } template auto my_advance(Iter iter, std::iter_difference_t n) { while (n > 0) { ++iter; --n; } while (n < 0) { --iter; ++n; } return iter; } template auto my_advance(Iter iter, std::iter_difference_t n) { while (n > 0) { ++iter; --n; } return iter; } int main() { // Random-access iterators: std::vector v{ 1, 2, 3, 4, 5 }; std::cout << *my_advance(v.begin(), 3) << std::endl; // Bidirectional iterators: std::list l{ 1, 2, 3, 4, 5 }; std::cout << *my_advance(l.end(), -3) << std::endl; // Forward iterators: std::forward_list f{ 1, 2, 3, 4, 5 }; std::cout << *my_advance(f.begin(), 3) << std::endl; } ================================================ FILE: Exercises/Modules/Chapter 21/Soln21_06/Array.cppm ================================================ export module array; import ; // For standard exception types import ; // For std::to_string() import ; // For std::as_const() import ; /* // Original template based on type traits and some mild template meta-programming template std::conditional_t, T&&, const T&> move_assign_if_noexcept(T& x) noexcept { return std::move(x); } */ // A straightforward concept with a single compound requirement template concept NoThrowMoveAssignable = requires (T left, T right) { { left = std::move(right) } noexcept -> std::same_as; }; // By default: return lvalue reference (will result in x being copied) // (Note: this default move_assign_if_noexcept() template in itself is noexcept, // the copy assignment that generally follows is not...) auto& move_assign_if_noexcept(auto& x) noexcept { return x; } // Specialization that moves if possible without throwing // Uses abbreviated template syntax with a type constraint on auto) auto&& move_assign_if_noexcept(NoThrowMoveAssignable auto& x) noexcept { return std::move(x); } export template class Array { public: Array(); // Default constructor explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array) noexcept; // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs) noexcept; // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size void push_back(T element); // Copy or move element to the back of the array private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) noexcept : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) noexcept { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) export template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template void Array::push_back(T element) // Pass by value (copy of lvalue, or moved rvalue!) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i {}; i < m_size; ++i) // Move existing elements (copy if not noexcept)... newArray[i] = move_assign_if_noexcept(m_elements[i]); newArray[m_size] = move_assign_if_noexcept(element); // Move (or copy) the new one... swap(newArray); // ... and swap! } ================================================ FILE: Exercises/Modules/Chapter 21/Soln21_06/Soln21_06.cpp ================================================ // Exercise 21-6 Simplifying move_assign_if_noexcept using concepts (see Array.h) import array; import ; // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array> array_of_arrays; Array array{ buildStringArray(1'000) }; array_of_arrays.push_back(array); // Push an lvalue array.push_back("One more for good measure"); std::cout << std::endl; array_of_arrays.push_back(std::move(array)); // Push an rvalue } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_01/ASSERT.h ================================================ #ifndef ASSERT_H // Do not forget the #includ guard! #define ASSERT_H #include #include /* Only real complication is the need for () around condition to avoid expanding to expressions such as !x < y. Best do the same for message as well to avoid similar operator precedence issues there. This confirms, yet again, that using function-like macros often leads to needlessly fragile code. */ #define ASSERT(condition, message) \ if (!(condition)) \ { \ std::cerr << (message) << std::endl; \ std::abort(); \ } \ #endif // End of the #include guard ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_01/SolnA_01.cpp ================================================ // Exercise A-1 Defining a function-like macro #include #include #include "ASSERT.h" int main() { int y{ 5 }; for (int x{}; x < 20; ++x) { std::cout << std::format("x = {}\ty = {}", x, y) << std::endl; ASSERT(x < y, "Whoopsidaisies, too far!"); } } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_02/ASSERT.h ================================================ #ifndef ASSERT_H #define ASSERT_H #include #include #ifndef NDEBUG #define ASSERT(condition, message) \ if (!(condition)) \ { \ std::cerr << (message) << std::endl; \ std::abort(); \ } #else #define ASSERT(condition, message) #endif #endif // End of the #include guard ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_02/SolnA_02.cpp ================================================ // Exercise A-2 Conditional compilation #include #include // Define (before including ASSERT.h) to disable the assertion #define NDEBUG #include "ASSERT.h" int main() { int y{ 5 }; for (int x{}; x < 20; ++x) { std::cout << std::format("x = {}\ty = {}", x, y) << std::endl; ASSERT(x < y, "Whoopsidaisies, too far!"); } } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_03/SmartException.cpp ================================================ #include "SmartException.h" SmartException::SmartException(const std::string& message, std::source_location location) : std::logic_error{ message } , m_location{ std::move(location) } { } void SmartException::throwFromHere(std::source_location location) { m_location = std::move(location); throw *this; } const std::source_location& SmartException::where() const { return m_location; } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_03/SmartException.h ================================================ #ifndef SMART_EXCEPTION_H #define SMART_EXCEPTION_H #include #include class SmartException : public std::logic_error { public: SmartException(const std::string& message, std::source_location location = std::source_location::current()); /* Throws the exception at this location * For cases where the exception is not created at the same line where it is thrown: * SmartException exception{ "BOOM!", {} }; * ... // more code * exception.throwFromHere(); * Note: in the example above, {} is optional * (it avoids calling std::source_location::current() during construction) */ void throwFromHere(std::source_location location = std::source_location::current()); const std::source_location& where() const; private: std::source_location m_location; }; #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_03/SolnA_03.cpp ================================================ // Exercise A-3 Writing an exception class that records where // the exception object was created/thrown. #include #include "SmartException.h" int main() { try { throw SmartException{ "Throwing directly..." }; } catch (const SmartException& exception) { std::cerr << "Something went wrong at line " << exception.where().line() << std::endl; } // Advanced use. This was no requirement of the exercise, // but it seems like something a smart exception could use... try { SmartException exception{ "Throwing later...", {} }; // Optional: {} to avoid calling std::source_location::current() // ... exception.throwFromHere(); } catch (const SmartException& exception) { std::cerr << "Something went wrong at line " << exception.where().line() << std::endl; } } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_04/SmartException.h ================================================ #ifndef SMART_EXCEPTION_H #define SMART_EXCEPTION_H #include #include class SmartException : public std::logic_error { public: SmartException(const std::string& message, std::source_location location = std::source_location::current()) : std::logic_error{ message } , m_location{ std::move(location) } { } /* Throws the exception at this location * For cases where the exception is not created at the same line where it is thrown: * SmartException exception{ "BOOM!", {} }; * ... // more code * exception.throwFromHere(); * Note: in the example above, {} is optional * (it avoids calling std::source_location::current() during construction) */ void throwFromHere(std::source_location location = std::source_location::current()); const std::source_location& where() const; private: std::source_location m_location; }; // Note: do not repeat the default argument value! inline void SmartException::throwFromHere(std::source_location location) { m_location = std::move(location); throw *this; } inline const std::source_location& SmartException::where() const { return m_location; } #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_04/SolnA_04.cpp ================================================ // Exercise A-4 Defining class member functions in the header #include #include "SmartException.h" int main() { try { throw SmartException{ "Throwing directly..." }; } catch (const SmartException& exception) { std::cerr << "Something went wrong at line " << exception.where().line() << std::endl; } // Advanced use. This was no requirement of the exercise, // but it seems like something a smart exception could use... try { SmartException exception{ "Throwing later...", {} }; // Optional: {} to avoid calling std::source_location::current() // ... exception.throwFromHere(); } catch (const SmartException& exception) { std::cerr << "Something went wrong at line " << exception.where().line() << std::endl; } } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_05/Print.cpp ================================================ #include #include void print(std::string_view string) { std::cout << string << std::endl; } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_05/PrintThat.cpp ================================================ #include "PrintThat.h" // Note: extern keyword is optional, // but does make clear that external linkage is used here. extern void print(std::string_view string); void print_that(std::string_view string) { print(string); } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_05/PrintThat.h ================================================ // Note: #include guards are strictly speaking optional for headers // that contain only function prototypes, // but convention dictates you add them regardless. #ifndef PRINT_THAT_H #define PRINT_THAT_H #include void print_that(std::string_view string); #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_05/PrintThis.cpp ================================================ #include "PrintThis.h" // Note: extern keyword is optional, // but does make clear that external linkage is used here. extern void print(std::string_view string); void print_this(std::string_view string) { print(string); } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_05/PrintThis.h ================================================ // Note: #include guards are strictly speaking optional for headers // that contain only function prototypes, // but convention dictates you add them regardless. #ifndef PRINT_THIS_H #define PRINT_THIS_H #include void print_this(std::string_view string); #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_05/SolnA_05.cpp ================================================ // Exercise A-5 External functions and header files... #include #include "PrintThis.h" #include "PrintThat.h" int main() { print_this("Happiness can be found even in the darkest of times, "); print_that("if one only remembers to turn on the light."); // -- Albus Dumbledore } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_06/Print.h ================================================ #ifndef PRINT_H #define PRINT_H #include #include inline void print(std::string_view string) { std::cout << string << std::endl; } #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_06/PrintThat.cpp ================================================ #include "PrintThat.h" #include "Print.h" void print_that(std::string_view string) { print(string); } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_06/PrintThat.h ================================================ // Note: #include guards are strictly speaking optional for headers // that contain only function prototypes, // but convention dictates you add them regardless. #ifndef PRINT_THAT_H #define PRINT_THAT_H #include void print_that(std::string_view string); #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_06/PrintThis.cpp ================================================ #include "PrintThis.h" #include "Print.h" void print_this(std::string_view string) { print(string); } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_06/PrintThis.h ================================================ // Note: #include guards are strictly speaking optional for headers // that contain only function prototypes, // but convention dictates you add them regardless. #ifndef PRINT_THIS_H #define PRINT_THIS_H #include void print_this(std::string_view string); #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_06/SolnA_06.cpp ================================================ // Exercise A-6 Inline function definitions #include #include "PrintThis.h" #include "PrintThat.h" int main() { print_this("I have found that it is the small everyday deed of ordinary folks "); print_that("that keep the darkness at bay. Small acts of kindness and love."); // -- Gandalf the Grey } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07A/Print.cpp ================================================ #include #include unsigned count; void print(std::string_view string) { std::cout << string << std::endl; ++count; } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07A/PrintThat.cpp ================================================ #include "PrintThat.h" // Note: extern keyword is optional, // but does make clear that external linkage is used here. extern void print(std::string_view string); void print_that(std::string_view string) { print(string); } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07A/PrintThat.h ================================================ // Note: #include guards are strictly speaking optional for headers // that contain only function prototypes, // but convention dictates you add them regardless. #ifndef PRINT_THAT_H #define PRINT_THAT_H #include void print_that(std::string_view string); #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07A/PrintThis.cpp ================================================ #include "PrintThis.h" // Note: extern keyword is optional, // but does make clear that external linkage is used here. extern void print(std::string_view string); void print_this(std::string_view string) { print(string); } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07A/PrintThis.h ================================================ // Note: #include guards are strictly speaking optional for headers // that contain only function prototypes, // but convention dictates you add them regardless. #ifndef PRINT_THIS_H #define PRINT_THIS_H #include void print_this(std::string_view string); #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07A/SolnA_07A.cpp ================================================ // Exercise A-7 External variables (based on Exercise A-5) #include #include "PrintThis.h" #include "PrintThat.h" extern unsigned count; int main() { print_this("It is our choices that show what we truly are, "); print_that("far more than our abilities."); // -- Albus Dumbledore std::cout << "\n(print() was called " << count << " times)"; } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07B/Print.h ================================================ #ifndef PRINT_H #define PRINT_H #include #include inline int count {}; inline void print(std::string_view string) { std::cout << string << std::endl; ++count; } #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07B/PrintThat.cpp ================================================ #include "PrintThat.h" #include "Print.h" void print_that(std::string_view string) { print(string); } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07B/PrintThat.h ================================================ // Note: #include guards are strictly speaking optional for headers // that contain only function prototypes, // but convention dictates you add them regardless. #ifndef PRINT_THAT_H #define PRINT_THAT_H #include void print_that(std::string_view string); #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07B/PrintThis.cpp ================================================ #include "PrintThis.h" #include "Print.h" void print_this(std::string_view string) { print(string); } ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07B/PrintThis.h ================================================ // Note: #include guards are strictly speaking optional for headers // that contain only function prototypes, // but convention dictates you add them regardless. #ifndef PRINT_THIS_H #define PRINT_THIS_H #include void print_this(std::string_view string); #endif ================================================ FILE: Exercises/NoModules/Appendix A/SolnA_07B/SolnA_07B.cpp ================================================ // Exercise A-7 External variables (based on Exercise A-6) #include #include "PrintThis.h" #include "PrintThat.h" #include "Print.h" int main() { print_this("All we have to decide is what to do "); print_that("with the time that is given to us."); // -- Gandalf the Grey std::cout << "\n(print() was called " << count << " times)"; } ================================================ FILE: Exercises/NoModules/Chapter 01/Exer1_03.cpp ================================================ // Exercise 1-3. Spot the errors. // The correct version should closely resemble the answer to Exercise 1.1. include // # character missing before include Int main() // Should be int, not Int { std:cout << "Hola Mundo!" << std:endl // A semicolon is missing from the end of this line // cout and endl must be prefixed with std::, not std: ) ================================================ FILE: Exercises/NoModules/Chapter 01/Soln1_01.cpp ================================================ // Exercise 1-1 Writing the line "Hello World" to the screen. #include int main() { std::cout << "Hello World" << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 01/Soln1_02.cpp ================================================ // Exercise 1-2. Write your name and age on successive lines. // There are several possibilities. You can do it in one statement for example. // You could also output '\n' to go to a new line. #include int main() { std::cout << "Phil McCavity" << std::endl; // Name std::cout << "Age: 88" << std::endl; // and age } ================================================ FILE: Exercises/NoModules/Chapter 02/Soln2_01A.cpp ================================================ // Exercise 2-1. Convert inches to feet and inches // The number of inches per foot is constant, // and should not be changed within the program, // so we recognize this by declaring it as a const. // // This solution uses only std::cout. // See alternate solution for a version that uses std::format(). // // Note: we always output "feet" and "inches", // even if it concerns only 1 foot or 1 inch. // In a later chapter you will learn about the conditional // statements and expressions that allow you to refine this. #include int main() { const int inches_per_foot{ 12 }; // Initialize constant variable std::cout << "This program will convert inches to feet and inches." << std::endl; int inches{}; std::cout << "Please enter a number of inches: "; std::cin >> inches; const int feet{ inches / inches_per_foot }; const int remaining_inches{ inches % inches_per_foot }; std::cout << inches << " inches equals " << feet << " feet and " << remaining_inches << " inches." << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 02/Soln2_01B.cpp ================================================ // Exercise 2-1. Convert inches to feet and inches // The number of inches per foot is constant, // and should not be changed within the program, // so we recognize this by declaring it as a const. // // This solution uses std::format(). // // Note: we always output "feet" and "inches", // even if it concerns only 1 foot or 1 inch. // In a later chapter you will learn about the conditional // statements and expressions that allow you to refine this. #include #include int main() { const int inches_per_foot { 12 }; // Initialize constant variable std::cout << "This program will convert inches to feet and inches." << std::endl; int inches {}; std::cout << "Please enter a number of inches: "; std::cin >> inches; const int feet = inches / inches_per_foot; const int remaining_inches = inches % inches_per_foot; std::cout << std::format("{} inches equals {} feet and {} inches.", inches, feet, remaining_inches) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 02/Soln2_02A.cpp ================================================ // Exercise 2-2 Compute the area of a circle // This solution uses only std::cout. // See alternate solution for a version that uses std::format(). #include #include int main() { std::cout << "This program will compute the area of a circle." << std::endl; double radius {}; std::cout << "Please enter the radius of the circle: "; std::cin >> radius; const auto area{ std::numbers::pi * radius * radius }; std::cout << "The area of the circle is " << area << " square units." << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 02/Soln2_02B.cpp ================================================ // Exercise 2-2 Compute the area of a circle // This solution uses std::format() // to control the precision used when outputting the result // (we opted for 2 decimals after the decimal points). #include #include #include int main() { std::cout << "This program will compute the area of a circle." << std::endl; double radius {}; std::cout << "Please enter the radius of the circle: "; std::cin >> radius; const auto area{ std::numbers::pi * radius * radius }; std::cout << std::format("The area of the circle is {:.2f} square units.", area) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 02/Soln2_03.cpp ================================================ // Exercise 2-3. Calculating the height of a tree #include #include #include int main() { const double inches_per_foot {12.0}; const double pi_degrees {180.0}; double feet {}; double inches {}; std::cout << "Enter the distance from the tree in feet and inches: "; std::cin >> feet >> inches; const double distance {feet + inches / inches_per_foot}; double angle {}; std::cout << "Enter the angle of the top of the tree in degrees: "; std::cin >> angle; // First convert angle to radians // (the trigoniometric functions of operate with radians) angle *= std::numbers::pi / pi_degrees; double eye_height {}; std::cout << "Enter your eye height from the ground in inches: "; std::cin >> eye_height; eye_height /= inches_per_foot; // Convert to feet const double height {eye_height + distance * std::tan(angle)}; // Tree height in feet const unsigned height_feet {static_cast(height)}; const unsigned height_inches {static_cast(std::round(inches_per_foot * (height - height_feet)))}; std::cout << "\nThe height of the tree is " << height_feet << " feet " << height_inches << " inches.\n" << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 02/Soln2_04.cpp ================================================ // Exercise 2-4. Calculating the Body Mass Index. // In the expression for bmi, both h_feet and h_inches will be implicitly converted to double // because the other operand of the sub-expression is double. #include int main() { const double lbs_per_kg {2.2}; const double inches_per_foot {12.0}; const double meters_per_foot {0.3048}; double w_lbs {}; unsigned int h_feet {}; unsigned int h_inches {}; std::cout << "Enter your weight in pounds: "; std::cin >> w_lbs; std::cout << "Enter you height in feet and inches: "; std::cin >> h_feet >> h_inches; const double w_kg {w_lbs / lbs_per_kg}; const double h_meters { meters_per_foot * (h_feet + h_inches / inches_per_foot) }; const double bmi {w_kg / (h_meters * h_meters)}; std::cout << "Your BMI is " << bmi << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 02/Soln2_05.cpp ================================================ // Exercise 2-5. Output your BMI with one decimal after the decimal point. #include #include int main() { const double lbs_per_kg{ 2.2 }; const double inches_per_foot{ 12.0 }; const double meters_per_foot{ 0.3048 }; double w_lbs{}; unsigned int h_feet{}; unsigned int h_inches{}; std::cout << "Enter your weight in pounds: "; std::cin >> w_lbs; std::cout << "Enter you height in feet and inches: "; std::cin >> h_feet >> h_inches; const double w_kg{ w_lbs / lbs_per_kg }; const double h_meters{ meters_per_foot * h_feet + h_inches / inches_per_foot }; const double bmi{ w_kg / (h_meters * h_meters) }; std::cout << std::format("Your BMI is {:.1f}\n", bmi); } ================================================ FILE: Exercises/NoModules/Chapter 02/Soln2_06.cpp ================================================ // Exercise 2-6. Format a table. #include #include #include int main() { // Define the format strings for the various rows of the table first const auto format_header { "{:20} {:35} {}\n" }; const auto format_precision5 { "{:20} {:35} {:.5f}...\n" }; const auto format_precision3 { "{:20} {:35} {:.3f}...\n" }; std::cout << std::format(format_header, "Constant", "Description", "Approximation"); std::cout << std::format(format_precision5, "std::numbers::e", "The base of the natural logarithm", std::numbers::e); std::cout << std::format(format_precision5, "std::numbers::pi", "pi", std::numbers::pi); std::cout << std::format(format_precision5, "std::numbers::sqrt2", "Square root of 2", std::numbers::sqrt2); std::cout << std::format(format_precision3, "std::numbers::phi", "The golden ration constant", std::numbers::phi); } ================================================ FILE: Exercises/NoModules/Chapter 02/Soln2_07.cpp ================================================ // Exercise 2-7. Fun with formatting. // You could always experiment further with various formatting options here! #include #include #include #include int main() { // Define the format strings for the various rows of the table first const auto format_header { "{:20} {:35} {}\n" }; const auto format_precision5{ "{:20} {:35} {:.5f}...\n" }; const auto format_precision3{ "{:20} {:35} {:.3f}...\n" }; const auto format_extra_row { "{:20} {:35} {:.5E}...\n" }; std::cout << std::format(format_header, "Constant", "Description", "Approximation"); std::cout << std::format(format_precision5, "std::numbers::e", "The base of the natural logarithm", std::numbers::e); std::cout << std::format(format_precision5, "std::numbers::pi", "pi", std::numbers::pi); std::cout << std::format(format_precision5, "std::numbers::sqrt2", "Square root of 2", std::numbers::sqrt2); std::cout << std::format(format_precision3, "std::numbers::phi", "The golden ration constant", std::numbers::phi); std::cout << std::format(format_extra_row, "sin(pi/4)", "... in exponent notation", std::sin(std::numbers::pi / 4)); } ================================================ FILE: Exercises/NoModules/Chapter 02/Soln2_08.cpp ================================================ // Exercise 2-8. Finding the largest of two integers without comparing them. #include int main() { unsigned a {}; unsigned b {}; std::cout << "Enter a positive integer: "; std::cin >> a; std::cout << "Enter another positive integer: "; std::cin >> b; // The trick is that, for integer values x and y, // x / y equals zero if x < y. // So unless a and b are equal, either a/b or b/a is zero, // meaning in (x * (a/b) + y * (b/a)) one of both operands of + equals 0. // Once you have that, it's just a matter of working out the details. const unsigned larger {(a * (a / b) + b * (b / a)) / (a / b + b / a)}; const unsigned smaller {(b * (a / b) + a * (b / a)) / (a / b + b / a)}; std::cout << "The larger integer is " << larger << ".\n" << "The smaller integer is " << smaller << '.' << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 03/Soln3_01.cpp ================================================ // Exercise 3-1. Output an integer and its complements in binary and decimal. // This tests how well you remember string formatting using std::format() // (see Chapter 2 if you forgot some of the formatting options), // as well as two's complement binary encoding and bitwise ~. #include #include // See Appendix A (available online) for static_assert(). // You should adjust the field widths of the table if ints have a different size. static_assert(sizeof(int) == 4, "This program assumes 32 bit ints"); int main() { int value {}; std::cout << "Enter any integer: "; std::cin >> value; const auto inverted{ static_cast(~value) }; // Output column headings (0b and 32 bits make for a width of 34) std::cout << std::format(" {:^34} {:^34} {:^34}\n", "value", "~value", "~value + 1"); // Output binary values (cast to unsigned integers first for more realistic output) std::cout << std::format(" {:#034b} {:#034b} {:#034b}\n", value, inverted, inverted + 1); // Output decimal values std::cout << std::format(" {:^34} {:^34} {:^34}\n", value, ~value, ~value + 1); } ================================================ FILE: Exercises/NoModules/Chapter 03/Soln3_02.cpp ================================================ // Exercise 3-2. Calculating the number of boxes that can be stored on a shelf, without overhang. // We have to calculate how many boxes we can get into a row, // and how many rows we can have, and then multiply these numbers together. // The 'no overhang' problem is easily handled: casting from double to long // (using static_cast<>()) ensures that the fractional part of the double value // is omitted, and only whole boxes are counted. // By including static_cast<>() in the code, we are effectively telling the // compiler that we know what information will be lost in the cast. #include int main() { const int inches_per_foot {12}; double shelf_length {}; double shelf_depth {}; int box_size {}; // Prompt the user for both the shelf and box dimensions std::cout << "Enter shelf length (feet): "; std::cin >> shelf_length; std::cout << "Enter shelf depth (feet): "; std::cin >> shelf_depth; std::cout << "Enter length ofthe side of a box (inches): "; std::cin >> box_size; // Calculating the number of whole boxes needed to fill the shelf. long boxes {static_cast((shelf_length * inches_per_foot) / box_size) * static_cast((shelf_depth * inches_per_foot) / box_size)}; // Displaying the number of boxes std::cout << "The number of boxes that can be contained in a single layer is " << boxes << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 03/Soln3_03.cpp ================================================ /********************************************************************************* Exercise 3-3. The output from the code is 2. Here's the important statement again: auto j {(k >> 4) & ~(~0u << 3)}; This question is an exercise in bit manipulation on k. First, (k >> 4) shifts the bits in k 4 places to the right; the bitwise representation of 430 is 110101110, so a 4-bit shift leaves 11010. Next, ~0 is composed of all 1s; shifting that three places to the left and complementing the result will leave 111. Finally, doing a bitwise AND on 11010 and 111 leaves 10 (in binary) or 2 (in decimal) as the result. *********************************************************************************/ #include int main() { /* Note: try to figure out the solution to this exercise without actually running it */ auto k{ 430u }; auto j{ (k >> 4) & ~(~0u << 3) }; std::cout << j << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 03/Soln3_04.cpp ================================================ // Exercise 3-4. Packing and unpacking characters. // Strictly speaking, whether or not this program works is compiler-dependent. // Although it's extremely unlikely that you'll notice a problem. // Concretely: it'll only work if a single byte is 8 bit (virtually always the case), // and the fundamental type int counts at least 4 bytes (true for most modern systems). #include #include int main() { unsigned int packed {}; unsigned char ch {}; std::cout << std::format("{:26}", "Enter a character: "); std::cin >> ch; packed |= ch; std::cout << std::format("{:26}", "Enter a second character: "); std::cin >> ch; packed <<= 8; // Shift left 1 byte packed |= ch; std::cout << std::format("{:26}", "Enter a third character: "); std::cin >> ch; packed <<= 8; // Shift left 1 byte packed |= ch; std::cout << std::format("{:26}", "Enter a fourth character: "); std::cin >> ch; packed <<= 8; // Shift left 1 byte packed |= ch; std::cout << std::format("The word containing 4 packed characters is {:#0x}", packed) << std::endl; std::cout << "Unpacking the characters again gives (low-order byte first): "; // Unpacking packed... // (without the static_cast() conversions the bytes would be written as numbers, // which would be fine as well...) unsigned int mask {0x00000FF}; // Keep low order byte // (could just use 0xFF as well, or, of course, 255) ch = packed & mask; // Low order byte std::cout << std::format("{:4}", static_cast(ch)); ch = (packed >> 8) & mask; // 2nd byte std::cout << std::format("{:4}", static_cast(ch)); ch = (packed >> 16) & mask; // 3rd byte std::cout << std::format("{:4}", static_cast(ch)); ch = (packed >> 24) & mask; // 4th byte std::cout << std::format("{:4}", static_cast(ch)) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 03/Soln3_05.cpp ================================================ // Exercise 3-5. Using an enumeration type for colors. // Of course, you have to research the RGB components for the colors. // Biggest complication is that you cannot apply any binary or other arithmetic operations // on values of a scoped enumaration type. For this, you have to first cast them to an integer. #include #include int main() { enum class Color : unsigned { Red = 0xFF0000u, Green = 0x00FF00u, Blue = 0x0000FFu, Yellow = 0xFFFF00u, Purple = 0xFF00FFu, Black = 0x000000u, White = 0xFFFFFFu }; const auto format_string { "The components of {:^6} are: red: {:3}, green: {:3}, blue: {:3}\n" }; const Color yellow{ Color::Yellow }; const Color purple{ Color::Purple }; const Color green { Color::Green }; std::cout << std::format(format_string, "yellow", (static_cast(yellow) & static_cast(Color::Red)) >> 16, (static_cast(yellow) & static_cast(Color::Green)) >> 8, (static_cast(yellow) & static_cast(Color::Blue)) ); std::cout << std::format(format_string, "purple", (static_cast(purple) & static_cast(Color::Red)) >> 16, (static_cast(purple) & static_cast(Color::Green)) >> 8, (static_cast(purple) & static_cast(Color::Blue)) ); std::cout << std::format(format_string, "green", (static_cast(green) & static_cast(Color::Red)) >> 16, (static_cast(green) & static_cast(Color::Green)) >> 8, (static_cast(green) & static_cast(Color::Blue)) ); } ================================================ FILE: Exercises/NoModules/Chapter 03/Soln3_06.cpp ================================================ // Exercise 3-6. Swapping integers. #include int main() { int first {}, second {}; std::cout << "Enter two integers separated by a space: "; std::cin >> first >> second; first ^= second; second ^= first; first ^= second; std::cout << "In reverse order they are " << first << " and " << second << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 04/Soln4_01.cpp ================================================ // Exercise 4-1 Testing whether two integer values are equal. #include int main() { int value1 {}; int value2 {}; std::cout << "Please input two integers, separated by a space: "; std::cin >> value1 >> value2; std::cout << std::endl; if (value1 == value2) std::cout << "The values you entered are the same (two times " << value1 << ")." << std::endl; else std::cout << "The values you entered are not the same (" << value1 << " != " << value2 << ")." << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 04/Soln4_02.cpp ================================================ // Exercise 4-2 Testing for exact division of one integer by another. // We can use an if statement to check that the input is valid // and we can use another to arrange the input as we need. // Then we use an if-else to generate the appropriate output. #include int main() { int value1 {}; int value2 {}; std::cout << "Please input two positive integers, separated by a space: "; std::cin >> value1 >> value2; std::cout << std::endl; if (value1 <= 0 || value2 <= 0) // Valid input? { std::cout << "Sorry - positive integers only." << std::endl; return 1; } // Ensure that value1 is not smaller than value2 if (value1 < value2) { const auto temp{ value1 }; // swap if necessary value1 = value2; value2 = temp; } if (value1 % value2 == 0) std::cout << value2 << " divides into " << value1 << " exactly. " << std::endl; else std::cout << value1 << " is not exactly divisible by " << value2 << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 04/Soln4_03.cpp ================================================ // Exercise 4-3 Using nested ifs and a logical && to check the value of a number. #include int main() { double value {}; std::cout << "Please enter a number between 1 and 100: "; std::cin >> value; std::cout << std::endl; if (value >= 1 && value <= 100) { std::cout << "The number you entered is "; if (value > 50) std::cout << "greater than 50"; else if (value < 50) std::cout << "less than 50" << std::endl; else std::cout << "50" << std::endl; std::cout << '.' << std::endl; } else { std::cout << "The number is not between 1 and 100." << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 04/Soln4_04.cpp ================================================ // Exercise 4-4. // As promised, you're to go look for a person "who is over 21, under 35, female, // has a bachelors or masters degree, is unmarried, and who speaks Hindi or Urdu" #include #include // For std::tolower() / std::toupper() enum class AcademicDegree { none, associate, bachelor, professional, master, doctor }; int main() { unsigned int age {}; // Initialized to 0 char gender {}; // Initialized to '\0' (see Chapter 5) AcademicDegree degree {}; // Initialized to AcademicDegree::none bool married {}; // Initialized to false bool speaksHindi {}; bool speaksUrdu {}; std::cout << "What is your age, if I may ask? "; std::cin >> age; if (age > 120) { std::cout << "Sure it is, joker. Sadly, commedians don't qualify..." << std::endl; return 1; } std::cout << "What is your gender ([m]ale, [f]emale, or [o]ther)? "; std::cin >> gender; gender = std::tolower(gender); if (gender != 'm' && gender != 'f' && gender != 'o') { std::cout << "That was not one of the options... " "The square brackets were not clear, perhaps? We were worried about that..."; return 1; } std::cout << "What is your highest academic degree?\n" << "Possible values are:\n" << "\tn: no academic degree\n" << "\ta: associate's degree\n" << "\tb: bachelor's dehree\n" << "\tp: professional degree\n" << "\tm: master's degree\n" << "\td: doctorate\n"; char degreeChar {}; std::cin >> degreeChar; switch (std::tolower(degreeChar)) { case 'n': degree = AcademicDegree::none; break; case 'a': degree = AcademicDegree::associate; break; case 'b': degree = AcademicDegree::bachelor; break; case 'p': degree = AcademicDegree::professional; break; case 'm': degree = AcademicDegree::master; break; case 'd': degree = AcademicDegree::doctor; break; default: std::cout << "Given that you cannot correctly enter your degree, shall I just note down 'none'?\n"; std::cout << "On second thought: no, I do not believe you qualify. Goodbye." << std::endl; return 1; } // Now we ask a few yes/no questions, and use some variations on how to decide // whether or not the user's input is valid or not. In real code, one should // probably be consistent rather than using a different style each time... char yes_no {}; std::cout << "Are you married (y or n)? "; std::cin >> yes_no; if (yes_no == 'y' || yes_no == 'Y') married = true; else if (yes_no == 'n' || yes_no == 'N') married = false; else { std::cout << "Incapable of entering your marital status. Surely still single then...?" << std::endl; return 1; } std::cout << "Do you speak Hindi (y or n)? "; std::cin >> yes_no; yes_no = std::toupper(yes_no); if (yes_no == 'Y') speaksHindi = true; else if (yes_no == 'N') speaksHindi = false; else { std::cout << "I'm sorry? I didn't catch that. Please answer in English next time..." << std::endl; return 1; } std::cout << "Do you speak Urdu (y or n)? "; std::cin >> yes_no; switch (std::tolower(yes_no)) { case 'y': speaksUrdu = true; break; case 'n': speaksUrdu = false; break; default: std::cout << "I'm sorry? I didn't catch that. Please answer in English next time..." << std::endl; return 1; } // Determine whether the user is someone "who is over 21, under 35, female, has a bachelors or masters degree, // is unmarried, and who speaks Hindi or Urdu" if ((age > 21 && age < 35) && gender == 'f' && (degree == AcademicDegree::bachelor || degree == AcademicDegree::master) && !married && (speaksHindi || speaksUrdu)) { std::cout << "Congratulations: you are precisely the person we were looking for! Are you willing to work for minimum wage?" << std::endl; } else { std::cout << "Sorry. You don't seem to meet our requirements to the letter. Don't call us, we'll call you...?" << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 04/Soln4_05.cpp ================================================ // Exercise 4-05 // Using the conditional operator to select output. #include #include int main() { int mice {}; // Count of all mice int brown {}; // Count of brown mice int white {}; // Count of white mice std::cout << "How many brown mice do you have? "; std::cin >> brown; std::cout << "How many white mice do you have? "; std::cin >> white; if (brown < 0 || white < 0) { std::cout << "One cannot have a negative amount of mice..." << std::endl; return 1; } mice = brown + white; std::cout << std::format("You have {} {} in total.\n", mice, mice == 1 ? "mouse" : "mice"); if (mice == 1) { // Mind the parentheses around the conditional expression! std::cout << "It is a " << (brown? "brown" : "white") << " mouse." << std::endl; } else { // No need for parenthese around the conditional expressions here std::cout << std::format("Of these mice, {} {} brown {}.", brown, brown == 1 ? "is a" : "are", brown == 1 ? "mouse" : "mice" ) << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 04/Soln4_06.cpp ================================================ // Exercise 4-6 Finding the range for an integer. // This is just a question of bolting sufficient conditional operators together // in an expression. #include using std::cin; using std::cout; using std::endl; int main() { int n {}; std::cout << "Enter an integer: "; std::cin >> n; std::cout << "The value is " << (n <= 20 ? "not greater than 20" : n <= 30 ? "greater than 20 and not greater than 30" : n <= 100? "greater than 30 and not exceeding 100" : "greater than 100") << '.' << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 04/Soln4_07.cpp ================================================ // Exercise 4-7 Outputting the binary code for a letter. /* * Most of the program is fairly simple. * The cctype functions make determining upper or lower case easy. * Finding out if it's a vowel is also easy with a switch. * Only getting the binary code needs a little thought, though. * Each of the masks selects a different bit of the ch variable. * If the bit is '1', the expression will be non-zero, which is converted to Boolean true. * If it's '0', the whole expression will be zero, or Boolean false. * Ones and zeros are therefore output as appropriate. */ #include #include int main() { char entered_letter {}; std::cout << "Enter a letter: "; std::cin >> entered_letter; if (!std::isalpha(entered_letter)) { std::cout << "That's not a letter!" << std::endl; return 1; } // We'll need the lower case letter... const auto lower_case_letter{ static_cast(std::tolower(entered_letter)) }; // Determine upper or lower case. std::cout << "'" << entered_letter << "' is " << (std::islower(entered_letter) ? "lowercase" : "uppercase") << '.' << std::endl; // Determine whether it is a vowel or a consonant. std::cout << "'" << entered_letter << "' is a "; switch (lower_case_letter) { case 'a': case 'e': case 'i': case 'o': case 'u': std::cout << "vowel"; break; default: std::cout << "consonant"; break; } std::cout << '.' << std::endl; // Output the character code as binary std::cout << "The binary code for '" << lower_case_letter << "' is " << ((lower_case_letter & 0b10000000)? 1 : 0) << ((lower_case_letter & 0b01000000)? 1 : 0) << ((lower_case_letter & 0b00100000)? 1 : 0) << ((lower_case_letter & 0b00010000)? 1 : 0) << ((lower_case_letter & 0b00001000)? 1 : 0) << ((lower_case_letter & 0b00000100)? 1 : 0) << ((lower_case_letter & 0b00000010)? 1 : 0) << ((lower_case_letter & 0b00000001)? 1 : 0) << std::endl; return 0; } ================================================ FILE: Exercises/NoModules/Chapter 04/Soln4_08.cpp ================================================ // Exercise 4-8 // Dividing a cash amount into quarters, nickels, dimes and cents. #include int main() { // Declare the constants (amounts of cents) const unsigned quarter {25}; const unsigned dime {10}; const unsigned nickel {5}; double amountInDollars {0.0}; std::cout << std::endl << "Please enter a cash amount between 0 and 10 dollars: $"; std::cin >> amountInDollars; if (amountInDollars >= 0.0 && amountInDollars <= 10.0) { // Multiply dollar amount by 100 ($1 = 100 cents) // We add 0.5 to compensate for errors in binary floating-point representation auto amountInCents {static_cast(amountInDollars * 100.0 + 0.5)}; // Find the number of quarters const auto quarters {amountInCents / quarter}; amountInCents %= quarter; // Get the remainder // Find the number of dimes const auto dimes {amountInCents / dime}; amountInCents %= dime; // Get the remainder // Find the number of nickels const auto nickels {amountInCents / nickel}; amountInCents %= nickel; // Get the remainder // Find the number of pennies const auto pennies {amountInCents}; // The remainder is already in pennies std::cout << std::endl << "The dollar value $" << amountInDollars << " can be broken down into:" << std::endl << quarters << " quarter" << (quarters == 1? "" : "s") << ',' << std::endl << dimes << " dime" << (dimes == 1? "" : "s") << ',' << std::endl << nickels << " nickel" << (nickels == 1? "" : "s") << ',' << std::endl << pennies << " penn" << (pennies == 1? "y" : "ies") << '.' << std::endl; } else { std::cout << std::endl << "You did not enter a dollar amount between 0 and 10." << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 05/Soln5_01.cpp ================================================ // Exercise 5-1 Squaring odd numbers #include #include int main() { int limit {}; std::cout << "Enter the upper limit for squared odd numbers: "; std::cin >> limit; for (int i {1}; i <= limit; i += 2) { std::cout << std::format("{:4} squared is {:8}\n", i, i * i); } } ================================================ FILE: Exercises/NoModules/Chapter 05/Soln5_02.cpp ================================================ // Exercise 5-2 Summing integers and calculating the average #include #include #include int main() { unsigned int count {}; long long total {}; while (true) { std::cout << "Enter an integer: "; int n; std::cin >> n; total += n; ++count; char yesno {}; std::cout << "Do you want to enter another (y/n)?"; std::cin >> yesno; if (std::tolower(yesno) == 'n') break; } std::cout << std::format("The total is {}. The average is {:.2f}.", total, static_cast(total) / count) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 05/Soln5_03.cpp ================================================ // Exercise 5-3 Using a do-while loop to count characters #include int main() { unsigned count {}; char ch {}; std::cout << "Please enter a sequence of characters terminated by '#':" << std::endl; // We have to read at least one character so do-while is best do { std::cin >> ch; ++count; } while (ch != '#'); // We do not count '#' as a character, so count must be adjusted --count; std::cout << "You entered " << count << " characters (not counting spaces and the terminal #)." << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 05/Soln5_04.cpp ================================================ // Exercise 5-4 Print out characters entered by the user in reverse order #include int main() { const size_t max_num_characters {1'000}; char string[max_num_characters]; std::cout << "Please enter a string: "; std::cin.getline(string, max_num_characters); // Count the number of characters size_t count {}; for (; count < max_num_characters && string[count] != '\0'; ++count) {} /* Take care: never write the following: for (size_t i = count - 1; i >= 0; --i) ... Because size_t is unsigned, the loop continuation condition i >= 0 shall always and forever be true. That is: every size_t value is always greater or equal to zero, by definition. Subtracting one from zero wraps around to std::numeric_limits::max(), a huge number. Other solutions besides the one we use below include: // Cast to a signed integer (works even for count == 0!) for (int i{ static_cast(i) - 1 }; i != -1; --i) ... // Subtract in second for expression (less readable, fails if count == 0) for (size_t i{ count }; i-- > 0; ) ... // Use a break statement to end the loop (fails if count == 0) for (size_t i{ count }; ; i--) { ... if (i == 0) break; } */ // Print out the characters in reverse order for (size_t i{ 1 }; i <= count; ++i) { std::cout << string[count - i]; } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 05/Soln5_05.cpp ================================================ // Exercise 5-5 Print out characters entered by the user *after* reversing them #include int main() { const size_t max_num_characters {1'000}; char string[max_num_characters]; std::cout << "Please enter a string: "; std::cin.getline(string, max_num_characters); // Count the number of characters size_t count {}; while (count < max_num_characters && string[count] != '\0') ++count; // Reverse the characters of the string entered by the user for (size_t i{ 0 }; i < count / 2; ++i) { char temp{ string[i] }; string[i] = string[count - i - 1]; string[count - i - 1] = temp; } // Print out all characters, one by one for (size_t i{ 0 }; i < count; ++i) { std::cout << string[i]; } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 05/Soln5_06.cpp ================================================ // Exercise 5-6. Working with a vector container #include #include #include int main() { std::cout << "What is the largest number I should check? "; unsigned bound {}; std::cin >> bound; std::vector values; // Add element values 1 to bound for (unsigned i {1}; i <= bound; ++i) values.push_back(i); size_t count {}; // Number of output values size_t perline {10}; // Number output perline for (auto value : values) { if (value % 7 == 0 || value % 13 == 0) continue; std::cout << std::format("{:5}", value); if (++count % perline == 0) std::cout << "\n"; } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 05/Soln5_07.cpp ================================================ // Exercise 5-7. Outputting product records & cost // Getting the alignment right is tricky. // You have to adjust the field widths until it looks OK. #include #include #include #include int main() { std::vector product_id; std::vector quantity; std::vector unit_cost; // Read the records while (true) { std::cout << "Enter a record - product number, quantity, unit cost separated by spaces: "; size_t id {}; size_t n {}; double cost {}; std::cin >> id >> n >> cost; product_id.push_back(id); quantity.push_back(n); unit_cost.push_back(cost); std::cout << "Do you want to enter another record (Y or N): "; char answer {}; std::cin >> answer; if (std::toupper(answer) == 'N') break; } // Column headings std::cout << std::format("{:10} {:10} {:10} {:10}\n", "Product", "Quantity", "Unit Price", "Cost"); double total_cost {}; for (size_t i {}; i < product_id.size(); ++i) { const auto cost{ quantity[i] * unit_cost[i] }; std::cout << std::format("{:<10} {:<10} ${:<9.2f} ${:<9.2f}\n", product_id[i], quantity[i], unit_cost[i], cost); total_cost += cost; } // Note the little trick to add empty space... std::cout << std::format("{:33}${:<9.2f}\n", "", total_cost); } ================================================ FILE: Exercises/NoModules/Chapter 05/Soln5_08.cpp ================================================ // Exercise 5-08. Generate 93 Fibonacci numbers stored in an array. // Of course 93 was not an arbitrary choice for the number of Fibonacci numbers. // Fibonacci number grow fairly rapidly. // 93 is the most that are possible with type unsigned long long on most platforms. #include #include // See Appendix A (available online) for static_assert() static_assert(sizeof(unsigned long long) >= 8, "This program assumes the depth of unsigned long long is (at least) 64 bit."); int main() { const size_t n {93}; std::array fib; fib[0] = fib[1] = 1UL; for (size_t i {2}; i < n; ++i) fib[i] = fib[i - 1] + fib[i - 2]; std::cout << "The first " << n << " Fibonacci numbers are:\n"; for (auto number : fib) { std::cout << number << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 06/Soln6_01.cpp ================================================ // Exercise 6-1. Storing odd numbers in an array and accessing them using pointer notation /* Note that the use of pointer notation is just for the sake of the exercise, * * to help you understand the intimate relation between pointers and array names. * * In real code, you'd normally just use array notation, because it is that much easier. */ #include #include int main() { const size_t n {50}; size_t odds[n]; for (size_t i {}; i < n; ++i) odds[i] = i * 2 + 1; const size_t perline {10}; std::cout << "The " << n << " first odd numbers are:\n"; for (size_t i {}; i < n; ++i) { std::cout << std::format("{:5}", *(odds + i)); if ((i + 1) % perline == 0) // Uses the loop counter to decide when a newline is required std::cout << std::endl; } std::cout << "\nIn reverse order these numbers are:\n"; for (int i {n - 1}; i >= 0; --i) // This won't work with size_t for the loop counter { // because size_t cannot be negative std::cout << std::format("{:5}", *(odds + i)); if (i % perline == 0) std::cout << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 06/Soln6_02.cpp ================================================ // Exercise 6-2. Traversing arrays using pointer arithmetics // An exercise to further deepen your understanding of the relation // between pointers, pointer arithmetic, and arrays. #include #include int main() { const size_t n {50}; size_t odds[n]; for (size_t i {}; i < n; ++i) odds[i] = i * 2 + 1; const size_t perline {10}; std::cout << "The " << n << " first odd numbers are:\n"; size_t* traversal_pointer{ odds }; for (size_t i {}; i < n; ++i) { std::cout << std::format("{:5}", *traversal_pointer++); if ((i + 1) % perline == 0) // Uses the loop counter to decide when a newline is required std::cout << std::endl; } std::cout << "\nIn reverse order these numbers are:\n"; for (size_t i {}; i < n; ++i) // No need to reverse the manipulation of the loop counter now { std::cout << std::format("{:5}", *(--traversal_pointer)); // Use the pre-decrement operator to make sure the pointer is decremented if ((i + 1) % perline == 0) // before it is dereferenced (at the start of this loop, std::cout << std::endl; // the pointer points one passed the last element of the odds array) } } ================================================ FILE: Exercises/NoModules/Chapter 06/Soln6_03.cpp ================================================ // Exercise 6-3. Storing numbers in a dynamic array // Btw: notice anything about the result? // Try increasing numbers of array elements... #include #include #include int main() { size_t n {}; std::cout << "Enter the number of array elements: "; std::cin >> n; auto* values{ new double[n] }; for (size_t i {}; i < n; ++i) *(values+i) = 1.0 / ((i + 1)*(i + 1)); double sum {}; for (size_t i {}; i < n; ++i) sum += values[i]; std::cout << std::format("The result is {}", std::sqrt(6.0 * sum)) << std::endl; delete[] values; // Don't forget to free the memory! } ================================================ FILE: Exercises/NoModules/Chapter 06/Soln6_04.cpp ================================================ // Exercise 6-4. Storing numbers in a vector /* * The result is an approximate value for pi. * The more values you use, the closer it becomes to pi. * In technical speak, this approximation does not converge to pi very fast though, * which basically means that you'll need quite some values * if you want to approximate pi beyond its first few digits. * * We must dereference values to use it with the subscript operator * because it is not a vector but a pointer to a vector. * * Note that allocating a vector<> like this in the free store is not often done. * Dynamic allocation is mostly reserved for objects of other class types, * in particular polymorphic ones. You'll learn all about this in later chapters! */ #include #include #include #include int main() { size_t n {}; std::cout << "Enter the number of vector elements: "; std::cin >> n; auto* values{ new std::vector(n) }; for (size_t i {}; i < n; ++i) (*values)[i] = 1.0 / ((i + 1)*(i + 1)); double sum {}; for (auto value : *values) sum += value; std::cout << std::format("Result is {}", std::sqrt(6.0*sum)) << std::endl; delete values; // It's not an array this time! } ================================================ FILE: Exercises/NoModules/Chapter 06/Soln6_05.cpp ================================================ // Exercise 6-5. Managing a dynamic array using a smart pointer // You can no longer use pointer notation now! // (You could, technically, call get() first and then use pointer notation, // though why make the syntax even more convoluted: just use array notation!) #include #include #include #include int main() { size_t n {}; std::cout << "Enter the number of array elements: "; std::cin >> n; auto values{ std::make_unique(n) }; for (size_t i {}; i < n; ++i) values[i] = 1.0 / ((i + 1)*(i + 1)); double sum {}; for (size_t i {}; i < n; ++i) sum += values[i]; std::cout << std::format("The result is {}", std::sqrt(6.0 * sum)) << std::endl; // No need to deallocate the memory yourself anymore: the smart pointer takes care of that for you! } ================================================ FILE: Exercises/NoModules/Chapter 06/Soln6_06.cpp ================================================ // Exercise 6-6. Storing a dynamically allocated vector in a smart pointer #include #include #include #include #include int main() { size_t n {}; std::cout << "Enter the number of vector elements: "; std::cin >> n; auto values{ std::make_unique>(n) }; for (size_t i {}; i < n; ++i) (*values)[i] = 1.0 / ((i + 1)*(i + 1)); double sum {}; for (auto value : *values) sum += value; std::cout << std::format("Result is {}", std::sqrt(6.0*sum)) << std::endl; // No need to deallocate the memory yourself anymore: the smart pointer takes care of that for you! } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_01.cpp ================================================ // Exercise 7-1 Storing student names and grades. // This uses a vector of string objects to store the names. #include #include #include #include #include int main() { std::vector names; std::vector grades; size_t max_length {}; // Longest name length double average_grade {}; // First accumulates the sum of the grades, // to be divided by the number of grades later // Data entry loop. // This loop reads the name and grade for each student. while (true) { std::cout << "Enter a student's name: "; std::string name; // Stores a student name std::getline(std::cin, name); names.push_back(name); if (max_length < name.length()) max_length = name.length(); std::cout << "Enter " << name << "\'s grade: "; double grade {}; // Stores a student grade std::cin >> grade; grades.push_back(grade); average_grade += grade; std::cout << "Do you wish to enter another student's details (y/n): "; char answer {}; std::cin >> answer; // Ignore the line break that is still on the input stream after reading the y/n character // Otherwise the next std::getline() always returns an empty line... // (Note: we'll try to remember to add this annoyance as a hint in the next edition...) std::cin.ignore(); if (std::toupper(answer) == 'N') break; } // Calculating the class average. average_grade /= grades.size(); // Displaying the class average. std::cout << std::format("\nThe class average for {} students is {:.2f}\n", names.size(), average_grade); // Displaying the students' names and grades. const size_t perline {3}; for (size_t i {}; i < names.size(); ++i) { std::cout << std::format("{:<{}} {:>4}\t", names[i], max_length, grades[i]); if ((i + 1) % perline) continue; std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_02.cpp ================================================ // Exercise 7-2 Frequency of words in text. #include #include #include #include int main() { std::string text; // The text to be searched std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); const std::string separators {" ,;:.\"!?'\n"}; // Word delimiters std::vector words; // Words found std::vector counts; // Words counts (same order as words) size_t start {text.find_first_not_of(separators)}; // First word start index while (start != std::string::npos) // Find the words { size_t end {text.find_first_of(separators, start + 1)}; // Find end of word if (end == std::string::npos) // Found a separator? end = text.length(); // No, so set to last + 1 std::string word{ text.substr(start, end - start) }; // Record the word // Check for word already in vector bool is_in {false}; // true when word has been found before for (int i {}; i < words.size(); ++i) { if (words[i] == word) { ++counts[i]; is_in = true; break; } } if (!is_in) // If it's a new word... { words.push_back(word); // ...store the word... counts.push_back(1); // ...and record the count } start = text.find_first_not_of(separators, end + 1); // Find 1st character of next word } // Find maximum word length size_t max_length {}; for (auto& word : words) if (max_length < word.length()) max_length = word.length(); std::cout << "Your string contains the following " << words.size() << " words and counts:\n"; size_t count {}; // Numbers of words output const size_t perline {3}; // Number per line for (size_t i {}; i < words.size(); ++i) { std::cout << std::format("{:<{}}{:>4} ", words[i], max_length, counts[i]); if (!(++count % perline)) std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_03.cpp ================================================ // Exercise 7-3 Replacing a word in text by asterisks. // Because we are looking for the word regardless of case, // the best way is to scan the text character by character. #include #include #include int main() { std::string text; // The text to be searched std::string word; // Stores the word to be replaced std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); std::cout << "\nEnter the word to be replaced: "; std::cin >> word; std::string lower_word; // Convert word to lower case for (char ch : word) // (we do so once, rather than with every iteration of the loop) lower_word += std::tolower(ch); const std::string separators {" ,;:.\"!?'\n"}; // Word delimiters size_t start {text.find_first_not_of(separators)}; // First word start index while (start != std::string::npos) // Find the words { auto end{ text.find_first_of(separators, start + 1) }; // Find end of word if (end == std::string::npos) // Found a separator? end = text.length(); // No, so set to last + 1 // Compare the word found with lower_word if (end - start == word.length()) { bool is_same_word{ true }; // Assume it is the word we need to replace for (size_t i {start}; i < end; ++i) { if (lower_word[i - start] != std::tolower(text[i])) // If a character differs... { is_same_word = false; // ...it is not the word break; } } if (is_same_word) // If it is the word... { for (size_t i {start}; i < end; ++i) // ... replace by asterisks text[i] = '*'; } } start = text.find_first_not_of(separators, end + 1); // Find 1st character of next word } std::cout << std::endl << text << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_04.cpp ================================================ // Exercise 7-4 Check for anagrams. // There's more than one way to do this. // We chose to delete characters in one word that are common to both. // If the length of the word that has characters deleted is zero, they are anagrams. #include #include #include int main() { std::string word1, word2; std::cout << "Enter the first word: "; std::cin >> word1; std::cout << "Enter the second word: "; std::cin >> word2; // Test the pathological case of the strings being different lengths if (word1.length() != word2.length()) { std::cout << word1 << " and " << word2 << " are different lengths so they can't be anagrams!" << std::endl; return 0; } std::string word2_copy {word2}; // Copy word2 - because we will delete characters // Loop over all the characters in word1 for (char c : word1) { // Loop over all the characters in word2 (we need the index now, so a range-based for loop won't do) for (size_t i {}; i < word2_copy.length(); ++i) { if (std::tolower(word2_copy[i]) == std::tolower(c)) { word2_copy.erase(i, 1); // Character found so erase from word2 break; // Caution: do not use erase(i), because that erases the entire substring starting at index i! } } } std::cout << word1 << " and " << word2 << " are " << (word2_copy.empty() ? "" : "not ") << "anagrams of one another." << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_05.cpp ================================================ // Exercise 7-5 Check for anagrams, ignoring spaces. // We chose to ignore all non-alphanumeric characters, not just spaces. #include #include #include int main() { std::string word1, word2; std::cout << "Enter the first word or phrase: "; std::getline(std::cin, word1); std::cout << "Enter the second word or phrase: "; std::getline(std::cin, word2); std::string word2_copy {word2}; // Copy word2 - because we will delete characters bool anagrams{true}; // Assume anagrams: becomes false if found otherwise // Loop over all the characters in word1 for (char c : word1) { if (!std::isalnum(c)) continue; // Ignore characters that aren't alphanumeric. bool found {false}; // Becomes true if c is found // Loop over all the characters in word2 (we need the index now, so a range-based for loop won't do) for (size_t i {}; i < word2_copy.length(); ++i) { if (std::tolower(word2_copy[i]) == std::tolower(c)) { word2_copy.erase(i, 1); // Character found so erase from word2 found = true; break; // Caution: do not use erase(i), because that erases the entire substring starting at index i! } } if (!found) { anagrams = false; break; } } // No alphanumeric characters are allowed to be left in word2_copy anymore at this point for (char c : word2_copy) { if (std::isalnum(c)) { anagrams = false; break; } } std::cout << word1 << " and " << word2 << " are " << (anagrams? "" : "not ") << "anagrams of one another." << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_06.cpp ================================================ // Exercise 7-6 Finding words that begin with a given letter. // If you wanted the words ordered by the first letter, you could sort the contents of letter first. // You could also retain all the sets of words for each letter in a separate vector for each set; // for this you could in other words use a std::vector>. // Here, we opted to gather the words for one letter and print out the results // during one iteration in the same loop. #include #include #include #include #include int main() { std::string text; // The text to be searched std::string letters; std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); std::cout << "\nEnter the starting letters for the words you want to find: "; std::cin >> letters; const std::string separators {" ,;:.\"!?'\n"}; // Word delimiters std::vector words; // Words found const size_t perline {5}; // Words output per line size_t count {}; // Number of words found for (auto ch : letters) { size_t start {text.find_first_not_of(separators)}; // First word start index size_t max_length {}; // Maximum word length while (start != std::string::npos) // Find the words { auto end{ text.find_first_of(separators, start + 1) }; // Find end of word if (end == std::string::npos) // Found a separator? end = text.length(); // No, so set to last + 1 auto word{ text.substr(start, end - start) }; // Record the word if (std::toupper(word[0]) == std::toupper(ch)) // If it begins with the current letter... { words.push_back(word); // ...save the word if (max_length < word.length()) max_length = word.length(); } start = text.find_first_not_of(separators, end + 1); // Find 1st character of next word } // List words for current letter std::cout << "\nWords beginning with '" << ch << "' are:\n"; for (auto& word : words) { std::cout << std::format("{:<{}}", word, max_length + 2); if (++count % perline) continue; std::cout << std::endl; } std::cout << std::endl; words.clear(); count = 0; } } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_07.cpp ================================================ // Exercise 7-7 // Practice string concatenation, looping over strings, and stoi()-like functions #include #include int main() { std::cout << "Enter a sequence of numbers terminated by #:\n"; // Read a long string containing any number of integers std::string numbers; std::getline(std::cin, numbers, '#'); long sum {}; size_t index {}; while (true) // An indefinite loop, stopped using a break statement. { // Note: this solution does not verify that in between the numbers there's only whitespace. // In real programs, such input validation may be appropriate... // Search for the start of the next (signed!) number const size_t start{ numbers.find_first_of("-0123456789", index) }; if (start == std::string::npos) break; // Stop once there's no numbers left // Search for the end of the number size_t end{ numbers.find_first_not_of("0123456789", start + 1) }; if (end == std::string::npos) end = numbers.length(); const size_t length{ end - start }; // To access substrings, we need the length of the string const auto substring{ numbers.substr(start, length) }; // Just in case: skip minus signs not followed by a number (would otherwise crash...) if (substring != "-") { const int number{ std::stoi(substring) }; sum += number; } index = end; // Advance to the next input (if any) } std::cout << "The sum of the numbers you entered is: " << sum << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_08A.cpp ================================================ // Exercise 7-8. Testing whether something is a tautogram. // // The loop based on find_first_of/find_first_not_of we use here is only one of many possible solutions. // See Soln7_08A for an alternate solution that first splits the text into a vector of words. // // Our solution in this file more or less assumes you enter only characters // that are either a letter or whitespace, // although it does cope with commas or dots that come directly after a word. // The alternate solution is more robust against non-letter characters. #include #include #include int main() { std::string text; // The text to be checked std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); const auto whitespace{ " \t\n\r\f\v" }; // Can be solved using std::isspace() as well... const size_t first_letter_index{ text.find_first_not_of(whitespace) }; if (first_letter_index == std::string::npos) { // Is an empty string a tautogram? Let's not go there. return 0; } const char start_letter{ static_cast(std::toupper(text[first_letter_index])) }; bool tautogram{ true }; for (size_t start_current_word{ first_letter_index };;) // Use an indefinite loop (see the break; statements) { const size_t next_space_index{ text.find_first_of(whitespace, start_current_word) }; if (next_space_index == std::string::npos) { break; } const size_t next_letter_index{ text.find_first_not_of(whitespace, next_space_index) }; if (next_letter_index == std::string::npos) { break; } if (std::toupper(text[next_letter_index]) != start_letter) { tautogram = false; break; } start_current_word = next_letter_index; } std::cout << "The text that you entered is " << (tautogram ? "" : "not ") << "a tautogram.\n"; if (tautogram) { std::cout << "All words start with the letter " << start_letter << '.'; } } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_08B.cpp ================================================ // Exercise 7-8. Testing whether something is a tautogram. // This version uses a totally different technique (first splits the text into words), // and as a bonus ignores small words, and only requires 70% of the words to start with the same letter. // With this looser definition of tautogram "The only verdict is vengeance; a vendetta, held as a votive not in vain.", // not entirely coincidentally, passes as a "near tautogram". #include #include #include #include int main() { // Ignore small words const size_t small_word_bound{ 3 }; // Set to 0 to not ignore small words at all const double near_tautology_ratio_bound{ 0.7 }; // Set to 1 to always require 100% of the words to begin with the same letter std::string text; // The text to be checked std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); std::vector words; for (size_t i {}; i < text.length(); ) { // Skip spaces until we find the start of a word... while (i < text.length() && !std::isalpha(text[i])) ++i; const size_t start{ i }; // Search for the end of the word... while (i < text.length() && std::isalpha(text[i])) ++i; if (i > start) { words.push_back(text.substr(start, i - start)); } } // Only search for tautograms where words start with Latin letters A - Z size_t counts[26] {}; // All counts are initialized to 0 size_t long_word_count{}; for (auto& word : words) { if (word.length() > small_word_bound) { ++long_word_count; const auto start_letter{ std::toupper(word[0]) }; if (start_letter >= 'A' && start_letter <= 'Z') { ++counts[start_letter - 'A']; } } } // Look for the most common start letter size_t most_common_index{ 0 }; for (size_t i{ 1 }; i < std::size(counts); ++i) { if (counts[i] > counts[most_common_index]) { most_common_index = i; } } const auto most_common_start_letter{ static_cast('A' + most_common_index) }; if (counts[most_common_index] == words.size()) { std::cout << "The text that you entered is a true tautogram.\n"; std::cout << "All words start with the letter " << most_common_start_letter << '.'; } else if (static_cast(counts[most_common_index]) / long_word_count >= near_tautology_ratio_bound) { std::cout << "It is a \"near tautogram\", though, as most longer words do start with the letter " << most_common_start_letter << '.'; } else { std::cout << "The text that you entered is no tautogram. Not even close.\n"; } } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_09A.cpp ================================================ // Exercise 7-9. Remove the begin letter of the tautogram (the easy way) #include #include #include int main() { std::string text; // The text to be checked std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); const auto whitespace{ " \t\n\r\f\v" }; // Can be solved using std::isspace() as well... const size_t first_letter_index{ text.find_first_not_of(whitespace) }; if (first_letter_index == std::string::npos) { // Is an empty string a tautogram? Let's not go there. return 0; } const char start_letter{ static_cast(std::toupper(text[first_letter_index])) }; bool tautogram{ true }; for (size_t start_current_word{ first_letter_index };;) // Use an indefinite loop (see the break; statements) { const size_t next_space_index{ text.find_first_of(whitespace, start_current_word) }; if (next_space_index == std::string::npos) { break; } const size_t next_letter_index{ text.find_first_not_of(whitespace, next_space_index) }; if (next_letter_index == std::string::npos) { break; } if (std::toupper(text[next_letter_index]) != start_letter) { tautogram = false; break; } start_current_word = next_letter_index; } std::cout << "The text that you entered is " << (tautogram ? "" : "not ") << "a tautogram.\n"; if (tautogram) { std::cout << "All words start with the letter " << start_letter << ".\n"; std::erase(text, start_letter); std::erase(text, std::tolower(start_letter)); std::cout << "After removing this letter, the text becomes as follows:\n" << text << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 07/Soln7_09B.cpp ================================================ // Exercise 7-9. Remove the begin letter of the tautogram (the hard way) #include #include #include int main() { std::string text; // The text to be checked std::cout << "Enter some text terminated by *:\n"; std::getline(std::cin, text, '*'); const auto whitespace{ " \t\n\r\f\v" }; // Can be solved using std::isspace() as well... const size_t first_letter_index{ text.find_first_not_of(whitespace) }; if (first_letter_index == std::string::npos) { // Is an empty string a tautogram? Let's not go there. return 0; } const char start_letter{ static_cast(std::toupper(text[first_letter_index])) }; bool tautogram{ true }; for (size_t start_current_word{ first_letter_index };;) // Use an indefinite loop (see the break; statements) { const size_t next_space_index{ text.find_first_of(whitespace, start_current_word) }; if (next_space_index == std::string::npos) { break; } const size_t next_letter_index{ text.find_first_not_of(whitespace, next_space_index) }; if (next_letter_index == std::string::npos) { break; } if (std::toupper(text[next_letter_index]) != start_letter) { tautogram = false; break; } start_current_word = next_letter_index; } std::cout << "The text that you entered is " << (tautogram ? "" : "not ") << "a tautogram.\n"; if (tautogram) { std::cout << "All words start with the letter " << start_letter << ".\n"; std::string text_without_start_letter; for (char c : text) { if (std::toupper(c) != start_letter) { text_without_start_letter.push_back(c); } } std::cout << "After removing this letter, the text becomes as follows:\n" << text_without_start_letter << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 08/Soln8_01.cpp ================================================ // Exercise 8-1 Reading and validating a date of birth. // As always, there are many ways of doing this! #include #include #include int validate_input(int lower, int upper, const std::string& description); int year(); int month(); int date(int month_value, int year_value); std::string ending(int date_day); int main() { std::cout << "Enter your date of birth." << std::endl; int date_year {year()}; int date_month {month()}; int date_day {date(date_month, date_year)}; std::string months[] {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; std::cout << std::endl; std::cout << std::format("You were born on the {} of {}, {}.", std::to_string(date_day) + ending(date_day), months[date_month - 1], date_year ) << std::endl; } // Reads an integer that is between lower and upper inclusive int validate_input(int lower, int upper, const std::string& description) { int data {}; std::cout << std::format("Please enter {} from {} to {}: ", description, lower, upper); std::cin >> data; while (data < lower || data > upper) { std::cout << "Invalid entry; please re-enter " << description << ": "; std::cin >> data; } return data; } // Reads the year int year() { const int low_year {1870}; // Program only works for folks under 150 years old const int high_year {2020}; // and those already born... return validate_input(low_year, high_year, "a year"); } // Reads the month int month() { const int low_month {1}; const int high_month {12}; return validate_input(low_month, high_month, "a month number"); } // Reads in the date in the given month and year int date(int month_number, int year) { const int date_min {1}; const int feb {2}; // Days in month: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec static const int date_max[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // With the above array declared as static, it will only be created the first // time the function is called. Of course, this doesn't save anything in this // example as we only call it once... // Feb has 29 days in a leap year. A leap year is a year that is divible by 4 // except years that are divisible by 100 but not divisible by 400 if (month_number == feb && year % 4 == 0 && !(year % 100 == 0 && year % 400 != 0)) return validate_input(date_min, 29, "a date"); else return validate_input(date_min, date_max[month_number - 1], "a date"); } // Select the ending of the ordinal day number std::string ending(int date_day) { if (date_day == 1 || date_day == 21 || date_day == 31) return "st"; else if (date_day == 2 || date_day == 22) return "nd"; else if (date_day == 3 || date_day == 23) return "rd"; else return "th"; } ================================================ FILE: Exercises/NoModules/Chapter 08/Soln8_02.cpp ================================================ // Exercise 8-2 Reversing the order of a string of characters. /****************************************************************** The reverse() function works with an argument of type string, or a C-style string terminated with '\0'. *******************************************************************/ #include #include std::string reverse(std::string str); int main() { std::string sentence; std::cout << "Enter a sequence of characters, then press 'Enter': " << std::endl; getline(std::cin, sentence); std::cout << "\nYour sequence in reverse order is:\n"; std::cout << reverse(sentence) << std::endl; std::cout << "\nHere is a demonstration of reverse() working with a C-style string:\n"; char stuff[] {"abcdefg"}; // C-style string std::cout << "\nThe original string is: \"" << stuff << "\"" << "\nReversed it becomes: \"" << reverse(stuff) << "\"" << std::endl; } // Reverse a string in place // The code here is working with a copy of the argument // so the original is not affected. std::string reverse(std::string str) { const size_t length {str.length()}; for (size_t i {}; i < length / 2; ++i) { char temp = str[i]; str[i] = str[length - i - 1]; str[length - i - 1] = temp; } return str; } ================================================ FILE: Exercises/NoModules/Chapter 08/Soln8_03.cpp ================================================ // Exercise 8_3 Checking the number of arguments entered at the command line. #include int main(int numArguments, char* arguments[]) { switch (numArguments - 1) { case 2: case 3: case 4: for (size_t i {1}; i < numArguments; ++i) std::cout << "Argument " << i << " is " << arguments[i] << std::endl; break; default: std::cout << "You entered the incorrect number of arguments.\n" << "Please enter 2, 3 or 4 arguments. " << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 08/Soln8_04.cpp ================================================ // Exercise 8_4 An overloaded plus() function. #include #include int plus(int a, int b); double plus(double x, double y); std::string plus(const std::string& s1, const std::string& s2); int main() { const int n {plus(3, 4)}; std::cout << "plus(3, 4) returns " << n << std::endl; const double d {plus(3.2, 4.2)}; std::cout << "plus(3.2, 4.2) returns " << d << std::endl; const std::string s {plus("he", "llo")}; std::cout << "plus(\"he\", \"llo\") returns " << s << std::endl; const std::string s1 {"aaa"}; const std::string s2 {"bbb"}; const std::string s3 {plus(s1, s2)}; std::cout << "With s1 as " << s1 << " and s2 as " << s2 << std::endl; std::cout << "plus(s1, s2) returns " << s3 << std::endl; /* const auto d {plus(3, 4.2)}; This won't compile because there is more than one overloaded plus() function for the arguments. The compiler will not choose so there must be a unique match with a function signature. */ } // Adding integer values int plus(int a, int b) { return a + b; } // Adding floating-point values double plus(double x, double y) { return x + y; } // Adding strings std::string plus(const std::string& s1, const std::string& s2) { return s1 + s2; } ================================================ FILE: Exercises/NoModules/Chapter 08/Soln8_05.cpp ================================================ // Exercise 8_5 Listing prime numbers #include #include #include #include // for std::sqrt() bool isPrime(unsigned number); // Option 1: return vector<> // Remember: starting with C++11, returning a vector is efficient. // In modern C++, this is therefore the recommended approach. std::vector generateNumbers(unsigned to, unsigned from = 1); std::vector filterPrimeNumbers(const std::vector& numbers); /* // Option 2: vector<> output variables (not implemented) // This is the traditional approach. // In modern C++, returning using return value is recommended. void generateNumbers(std::vector& output, unsigned to, unsigned from = 1); void filterPrimeNumbers(std::vector& output, const std::vector& numbers); */ int main() { unsigned user_number{}; std::cout << "Would you be so kind as to enter a number? " << std::endl; std::cin >> user_number; const auto numbers{ generateNumbers(user_number) }; const auto primes{ filterPrimeNumbers(numbers) }; unsigned count{}; for (auto& prime : primes) { std::cout << std::format("{:6}", prime); if (++count % 15 == 0) std::cout << '\n'; } std::cout << std::endl; } /* The sqrt() in the for loop below is not required. The following loop would be equally correct, just a bit slower: for (unsigned i = 2; i < number; ++i) { ... } It is a quite common optimisation though to stop testing at the square root of the number. Think about why this is correct! */ bool isPrime(unsigned number) { // a prime number is a natural number strictly greater than 1... if (number <= 1) return false; // ...and with no positive divisors other than 1 and itself for (unsigned i{ 2 }; i < std::sqrt(number); ++i) { if (number % i == 0) { return false; } } return true; } std::vector generateNumbers(unsigned to, unsigned from) { std::vector result; result.reserve(to - from + 1); for (unsigned i{ from }; i <= to; ++i) result.push_back(i); return result; } std::vector filterPrimeNumbers(const std::vector& numbers) { std::vector result; for (auto number : numbers) { if (isPrime(number)) result.push_back(number); } return result; } ================================================ FILE: Exercises/NoModules/Chapter 08/Soln8_06.cpp ================================================ // Exercise 8_6 Computing grade statistics /* This is a bigger excercise, and thus many variations will be valid and correct. The focus here isn't on performance, it's on writing and calling functions. Main thing is that you: (1) got the parameter types right---mostly pass-by-reference-to-const for the input parameters, except for sort() which modifies its argument. (2) made sure that the program does something correctly for empty grade lists, small grade lists (< 5 values), and for grade lists of both odd and even size. Some things worth noticing in our solution: - we overloaded the recursive sort() function by adding a helper function that users can call without having to know what the second and third parameters are, and without having to think about the empty input case. This is common with recursive algorithms: the interface that users use will often not contain the actual recursive function. - we defined NOT_AVAILABLE equal to std::numeric_limits::max() (equivalent to static_cast(-1)) to encode missing values in case there are less than 5 inputs, and used NaN values elsewhere in case the user enters no value at all. Other solutions for the former could've been signed values, or in fact any number larger than 100. In Chapter 9 you will learn about std::optional<> which allows you to handle such "not available" or "undefined" cases more elegantly, though. */ #include #include #include #include // for std::sqrt() and std::isnan() #include // for std::numeric_limits's max() and quiet_nan() void sort(std::vector& numbers); void getHighest(const std::vector& sortedNumbers, unsigned(&highest)[5]); void getLowest(const std::vector& sortedNumbers, unsigned(&lowest)[5]); double computeAverage(const std::vector& numbers); double computeMedian(const std::vector& numbers); double computeStandardDeviation(const std::vector& numbers); double computeVariance(const std::vector& numbers); void printNumber(const std::string& label, double number); void printNumbers(const std::string& label, const unsigned(&numbers)[5]); const unsigned NOT_AVAILABLE = std::numeric_limits::max(); int main() { std::vector grades; std::cout << "Please enter a number of grades (0-100), terminated by a negative one:" << std::endl; while (true) { int number{}; std::cin >> number; if (number < 0) break; else if (number > 100) std::cout << "Only numbers < 100, please..." << std::endl; else grades.push_back(number); } sort(grades); // Note: thruth be told, this is based on an old solution. // It would be better and/or easier // a) to use std::array<> instead of C-style arrays; and // b) to return the values requested rather than using output parameters // Perhaps you can improve our solution accordingly? unsigned highest[5]{}; unsigned lowest[5]{}; getHighest(grades, highest); getLowest(grades, lowest); printNumbers("Five highest grades", highest); printNumbers("Five lowest grades", lowest); printNumber("The grade average", computeAverage(grades)); printNumber("The median grade", computeMedian(grades)); printNumber("The standard deviation of the grades", computeStandardDeviation(grades)); printNumber("The variance of the grades", computeVariance(grades)); } // Swap numbers at position first with address at position second void swap(std::vector& numbers, size_t first, size_t second) { auto temp{ numbers[first] }; numbers[first] = numbers[second]; numbers[second] = temp; } // Recursive helper function to sort numbers in ascending sequence // Numbers to be sorted are from words[start] to words[end] void sort(std::vector& numbers, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle of the [start, end] range to partition swap(numbers, start, (start + end) / 2); // Swap middle number with start // Check values against chosen word size_t current{ start }; for (size_t i{ start + 1 }; i <= end; i++) { if (numbers[i] < numbers[start]) // Is word less than chosen value? swap(numbers, ++current, i); // Yes, so swap to the left } swap(numbers, start, current); // Swap the chosen value with the last swapped number if (current > start) sort(numbers, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(numbers, current + 1, end); // Sort right subset if exists } // Sort numbers in ascending sequence void sort(std::vector& numbers) { if (!numbers.empty()) sort(numbers, 0, numbers.size() - 1); } void getHighest(const std::vector& sortedNumbers, unsigned(&highest)[5]) { const auto numHighest{ static_cast(std::size(highest)) }; for (int i{}; i < numHighest; ++i) { const int numberIndex{ static_cast(sortedNumbers.size()) - numHighest + i }; if (numberIndex >= 0) highest[i] = sortedNumbers[numberIndex]; else highest[i] = NOT_AVAILABLE; } } void getLowest(const std::vector& sortedNumbers, unsigned(&lowest)[5]) { for (size_t i{}; i < std::size(lowest); ++i) { if (i < sortedNumbers.size()) lowest[i] = sortedNumbers[i]; else lowest[i] = NOT_AVAILABLE; } } double computeAverage(const std::vector& numbers) { if (numbers.empty()) return std::numeric_limits::quiet_NaN(); double average{}; for (auto& number : numbers) average += number; return average / numbers.size(); } double computeMedian(const std::vector& numbers) { if (numbers.empty()) return std::numeric_limits::quiet_NaN(); const auto numNumbers{ numbers.size() }; const auto middleIndex{ numNumbers / 2 }; if (numNumbers % 2) { return numbers[middleIndex]; } else { return (numbers[middleIndex] + numbers[middleIndex - 1]) / 2.0; } } double computeStandardDeviation(const std::vector& numbers) { if (numbers.empty()) return std::numeric_limits::quiet_NaN(); const double average{ computeAverage(numbers) }; double sum{}; for (auto& number : numbers) sum += (number - average) * (number - average); return std::sqrt(sum / numbers.size()); } double computeVariance(const std::vector& numbers) { if (numbers.empty()) return std::numeric_limits::quiet_NaN(); const double standardDeviation{ computeStandardDeviation(numbers) }; return standardDeviation * standardDeviation; } void printNumber(const std::string& label, double number) { std::cout << label << ": "; if (std::isnan(number)) std::cout << "n/a"; else std::cout << number; std::cout << std::endl; } void printNumbers(const std::string& label, const unsigned(&numbers)[5]) { std::cout << label << ": "; for (const auto number : numbers) { if (number != NOT_AVAILABLE) std::cout << number << ' '; } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 08/Soln8_07.cpp ================================================ // Exercise 8-7 Computing Fibinacci numbers recursively. // Main thing to realise is that the recursion needs two base cases. // Key is also to use unsigned values (function would fail for negative numbers) // and not to forget about zero either (using n == 1 and n == 2 as base cases // would mean trouble if n == 0 is passed) #include unsigned long long fib(size_t n); int main() { size_t num{}; std::cout << "Good day, master. How many Fibonacci numbers shall I compute today?" << std::endl; std::cin >> num; for (size_t i{1}; i <= num; ++i) std::cout << "fib(" << i << ") = " << fib(i) << '\n'; } unsigned long long fib(size_t n) { switch (n) { case 0: return 0; case 1: return 1; default: return fib(n-2) + fib(n-1); } } ================================================ FILE: Exercises/NoModules/Chapter 08/Soln8_07A.cpp ================================================ // Exercise 8-7 Computing Fibinacci numbers iteratively. // On most systems (it depends on sizeof(unsigned long long)), // you can correctly compute up to 93 Fibonacci numbers with this program. #include unsigned long long fib(size_t n); int main() { size_t num{}; std::cout << "Good day, master. How many Fibonacci numbers shall I compute today?" << std::endl; std::cin >> num; for (size_t i{1}; i <= num; ++i) std::cout << "fib(" << i << ") = " << fib(i) << '\n'; } unsigned long long fib(size_t n) { // Initialise fib(i) and fib(i+1) for the first iteration of the loop where i == 0 unsigned long long fib_i{0}; // fib(i) = fib(0) = 0 unsigned long long fib_i_1{1}; // fib(i+1) = fib(1) = 1 for (size_t i{}; i < n; ++i) { auto fib_i_2{ fib_i + fib_i_1 }; // fib(i+2) = fib(i) + fib(i+1) // Get ready for the next iteration (mind the order!): fib_i = fib_i_1; fib_i_1 = fib_i_2; } // At the end of the loop, i was equal to n, so fib(i) == fib(n), which is what we needed return fib_i; } ================================================ FILE: Exercises/NoModules/Chapter 08/Soln8_08.cpp ================================================ // Exercise 8_8 More efficient recursive version of function for x to the power n, n positive or negative // Based on Ex8_17.cpp #include #include long double power(double x, int n); int main() { for (int i {-3}; i <= 3; ++i) // Calculate powers of 8 from -3 to +3 std::cout << std::setw(10) << power(8.0, i); std::cout << std::endl; } // Recursive function to calculate x to the power n long double power(double x, int n) { if (n == 0) return 1.0; else if (n < 0) return 1.0 / power(x, -n); else if (n % 2) return x * power(x, n - 1); // n is odd // If we make it this far, n > 0 and even const auto y{ power(x, n / 2) }; return y * y; } ================================================ FILE: Exercises/NoModules/Chapter 08/Soln8_09.cpp ================================================ // Exercise 8_9 Count the number of multiplications performed by // the divide and conquer power() function of Soln8_08.cpp #include #include long double power(double x, int n); int main() { std::cout << power(1.5, 1000) << std::endl; } inline auto mult(long double l, long double r) { static size_t count{}; std::cout << ++count << " multiplications" << std::endl; return l * r; } // Recursive function to calculate x to the power n long double power(double x, int n) { if (n == 0) return 1.0; else if (n < 0) return 1.0 / power(x, -n); else if (n % 2) return mult(x, power(x, n - 1)); // x is odd // If we make it this far, x > 0 and even const auto y{ power(x, n / 2) }; return mult(y, y); } ================================================ FILE: Exercises/NoModules/Chapter 09/Soln9_01.cpp ================================================ // Exercise 9-1 Working with std::string_view<> #include // std::optional<> is defined in the module #include #include std::optional find_last( std::string_view string, char to_find, std::optional start_index = std::nullopt); // or: ... start_index = {}); int main() { const auto string{ "Growing old is mandatory; growing up is optional." }; const std::optional found_a{ find_last(string, 'a') }; if (found_a) std::cout << "Found the last a at index " << *found_a << std::endl; const auto found_b{ find_last(string, 'b') }; if (found_b.has_value()) std::cout << "Found the last b at index " << found_b.value() << std::endl; // following line gives an error (cannot convert std::optional to size_t) // const size_t found_c{ find_last(string, 'c') }; const auto found_early_i{ find_last(string, 'i', 10) }; if (found_early_i != std::nullopt) std::cout << "Found an early i at index " << *found_early_i << std::endl; } std::optional find_last(std::string_view string, char to_find, std::optional start_index) { // code below will not work for empty strings if (string.empty()) return std::nullopt; // or: 'return std::optional{};' // or: 'return {};' // determine the starting index for the loop that follows: size_t index{ start_index.value_or(string.size() - 1) }; while (true) // never use while (index >= 0) here, as size_t is always >= 0! { if (string[index] == to_find) return index; if (index == 0) return std::nullopt; --index; } } ================================================ FILE: Exercises/NoModules/Chapter 09/Soln9_02.cpp ================================================ // Exercise 9-2 Working with std::string_view<> and std::span<> #include #include #include #include // The function prototype including defaults for parameters void show_data( std::span data, std::string_view title = "Data Values", size_t width = 10, size_t perLine = 5); // Extra overload to output a single value (. void show_data( int data, std::string_view title = "Data Values", size_t width = 10); int main() { int samples[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; int dataItem{ -99 }; show_data({ &dataItem, 1 }); // Call original function directly dataItem = 13; show_data(dataItem, "Unlucky for some!"); // Use extra overload show_data(samples); show_data(samples, "Samples"); show_data(samples, "Samples", 6); show_data(samples, "Samples", 8, 4); } void show_data( std::span data, std::string_view title, size_t width, size_t perLine) { std::cout << title << std::endl; // Display the title // Output the data values for (size_t i{}; i < data.size(); ++i) { std::cout << std::format("{:{}}", data[i], width); // Display a data item if ((i + 1) % perLine == 0) // Newline after perLine values std::cout << '\n'; } std::cout << std::endl; } void show_data(int data, std::string_view title, size_t width) { show_data({ &data, 1 }, title, width); } ================================================ FILE: Exercises/NoModules/Chapter 09/Soln9_03.cpp ================================================ // Exercise 9.3. Using vocabulary types // Your solution should use all types you learned about in Chapter 9! #include #include #include #include #include void show_data(std::span data, std::string_view title = "Data Values", size_t width = 10, size_t perLine = 5); std::optional largest(std::span data); std::optional smallest(std::span data); std::span shift_range(std::span data, double delta); std::span scale_range(std::span data, double divisor); std::span normalize_range(std::span data); int main() { double samples[] { 11.0, 23.0, 13.0, 4.0, 57.0, 36.0, 317.0, 88.0, 9.0, 100.0, 121.0, 12.0 }; show_data(samples, "Original Values"); // Output original values normalize_range(samples); // Normalize the values show_data(samples, "Normalized Values", 12); // Output normalized values } // Outputs an array of double values void show_data(std::span data, std::string_view title, size_t width, size_t perLine) { std::cout << title << std::endl; // Display the title // Output the data values for (size_t i {}; i < data.size(); ++i) { // Display a data item (uses a dynamic field width: see Chapter 7) std::cout << std::format("{:{}.6g}", data[i], width); if ((i + 1) % perLine == 0) // Newline after perLine values std::cout << '\n'; } std::cout << std::endl; } std::optional smallest(std::span data) { if (data.empty()) return {}; // There is no smallest in an empty sequence size_t index_min {}; for (size_t i {1}; i < data.size(); ++i) if (data[index_min] > data[i]) index_min = i; return data[index_min]; } std::span shift_range(std::span data, double delta) { for (size_t i {}; i < data.size(); ++i) data[i] += delta; return data; } std::optional largest(std::span data) { if (data.empty()) return {}; // There is no largest in an empty array size_t index_max {}; for (size_t i {1}; i < data.size(); ++i) if (data[index_max] < data[i]) index_max = i; return data[index_max]; } std::span scale_range(std::span data, double divisor) { if (!divisor) return data; // Do nothing for a zero divisor for (size_t i{}; i < data.size(); ++i) data[i] /= divisor; return data; } std::span normalize_range(std::span data) { shift_range(data, -(*smallest(data))); return scale_range(data, *largest(data)); } ================================================ FILE: Exercises/NoModules/Chapter 09/Soln9_04.cpp ================================================ // Exercise 9-4. Using std::optional<> #include #include #include #include #include #include // Function prototypes std::optional largest(std::span data); std::optional largest(std::span data); std::optional largest(std::span words); int main() { const double array[]{ 1.5, 44.6, 13.7, 21.2, 6.7 }; const std::vector numbers{ 15, 44, 13, 21, 6, 8, 5, 2 }; const std::vector data{ 3.5, 5.0, 6.0, -1.2, 8.7, 6.4 }; const std::array array_data{ 3.5, 5.0, 6.0, -1.2, 8.7, 6.4 }; // Throwing in an std::array for good measure const std::vector names{ "Charles Dickens", "Emily Bronte", "Jane Austen", "Henry James", "Arthur Miller" }; std::cout << "The largest of array is " << *largest(array) << std::endl; // Crashes if nullopt is returned std::cout << "The largest of numbers is " << largest(numbers).value() << std::endl; // Throws exception (see Chapter 16) for nullopt std::cout << "The largest of data is " << largest(data).value() << std::endl; std::cout << "The largest of array_data is (also) " << *largest(array_data) << std::endl; std::cout << "The largest of names is " << largest(names).value_or("") << std::endl; } // Finds the largest of a span of values std::optional largest(std::span data) { if (data.empty()) return {}; // Or return std::nullopt; double max{ data[0] }; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of int values std::optional largest(std::span data) { if (data.empty()) return {}; // Or return std::nullopt; int max{ data[0] }; for (auto value : data) if (max < value) max = value; return max; } // Finds the largest of a vector of string objects std::optional largest(std::span words) { if (words.empty()) return {}; // Or return std::nullopt; std::string max_word{ words[0] }; for (const auto& word : words) if (max_word < word) max_word = word; return max_word; } ================================================ FILE: Exercises/NoModules/Chapter 09/Soln9_05.cpp ================================================ // Exercise 9-5. Using fixed-size std::span<> #include #include double average10(std::span data); // Function prototype int main() { double values[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; // double values[]{ 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(values) << std::endl; } // Function to compute an average double average10(std::span data) { double sum{}; // Accumulate total in here for (double val : data) sum += val; // Sum array elements return sum / data.size(); // Return average } ================================================ FILE: Exercises/NoModules/Chapter 09/Soln9_06.cpp ================================================ // Exercise 9-6. Passing a vector to a fixed-size std::span<> #include #include #include double average10(std::span data); // Function prototype int main() { std::vector values { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; // double values[]{ 1.0, 2.0, 3.0 }; // Only three values!!! std::cout << "Average = " << average10(std::span{ values.data(), values.size() }) << std::endl; } // Function to compute an average double average10(std::span data) { double sum{}; // Accumulate total in here for (double val : data) sum += val; // Sum array elements return sum / data.size(); // Return average } ================================================ FILE: Exercises/NoModules/Chapter 10/Soln10_01.cpp ================================================ // Exercise 10-1.cpp // Create a function template for my_clamp(), // a template that produces functions to clamp values to a given interval. // Different variations are possible, but we opted for the same as in the Standard Library: // namely one where all three arguments are of the same type, and passed by reference-to-const. #include #include #include /* Caution: the actual std::clamp() function uses a different argument order. Where the exercise suggests: my_clamp(value, low, high) but for the Standard Library function the order is as follows: std::clamp(low, value, high) This solution does as instructed, but keep this in mind if ever using std::clamp()! */ template const T& my_clamp(const T& value, const T& low, const T& high); // Function template prototype int main() { std::cout << "2.0 clamped to interval [1.5, 2.5] is " << my_clamp(2.0, 1.5, 2.5) << std::endl; std::cout << "5.0 clamped to interval [1.5, 2.5] is " << my_clamp(5.0, 1.5, 2.5) << std::endl; int big_int {17011983}, small_int {10}, negative_int {-123}; std::cout << std::format("{} clamped to the interval [{},{}] is {}", negative_int, small_int, big_int, my_clamp(negative_int, small_int, big_int)) << std::endl; // And now for a less useful example... std::string a_string {"A"}, z_string {"Z"}; std::string shakespeare{"It is not in the stars to hold our destiny but in ourselves"}; std::cout << "William Shakespeare's quote clamped to [A-Z] is: " << my_clamp(shakespeare, a_string, z_string) << std::endl; } // Template for functions to clamp a value to a closed interval template const T& my_clamp(const T& value, const T& low, const T& high) { if (value < low) return low; else if (value < high) return value; else return high; } ================================================ FILE: Exercises/NoModules/Chapter 10/Soln10_02.cpp ================================================ // Exercise 10-2.cpp // Using auto instead of std::string resulted in a type change for a_string and z_string: // they now both have the type const char[], the type given to string literals. // If you instantiate the original template for this type, it therefore becomes // // const char* larger(const char* a, const char* b) // { // return a > b? a : b; // } // // This function now compares the pointers of both string literals rather than the // string literals themselves. To solve this, you should create a specialisation // for const char* arrays. #include #include template // Function template prototype T larger(T a, T b); template<> // Function template specialization prototype const char* larger(const char* a, const char* b); int main() { std::cout << "Larger of 1.5 and 2.5 is " << larger(1.5, 2.5) << std::endl; std::cout << "Larger of 3.5 and 4.5 is " << larger(3.5, 4.5) << std::endl; const int big_int {17011983}, small_int {10}; std::cout << "Larger of " << big_int << " and " << small_int << " is " << larger(big_int, small_int) << std::endl; const auto a_string {"A"}, z_string {"Z"}; // now string literals (type const char[])! std::cout << "Larger of \"" << a_string << "\" and \"" << z_string << "\" is " << '"' << larger(a_string, z_string) << '"' << std::endl; } // Template for functions to return the larger of two values template T larger(T a, T b) { return a > b? a : b; } // Template specialization for returning the larger of two character arrays // (such as string literals). // // Note: instead of std::string_view, you could've used std::string as well, // but that would've involved copying the character arrays. // std::string_view is therefore the preferred C++ type to use here. template<> const char* larger(const char* a, const char* b) { return std::string_view{a} > std::string_view{b}? a : b; } /* // Template specialization for returning the larger of two character arrays // (such as string literals). // Alternative implementation using the C-style function strcmp() // (you'll need to include the header to make this compile). // The strcmp() function returns a value larger than zero if a > b; // zero if both arguments are equal, a negative value if a < b. template<> const char* larger(const char* a, const char* b) { return std::strcmp(a, b) > 0; } */ ================================================ FILE: Exercises/NoModules/Chapter 10/Soln10_03.cpp ================================================ // Exercise 10-3.cpp // Defining a function template for adding numbers, // and an overload that works for pointer types. // Extra: also make plus() work with string literals... #include #include #include template T plus(const T& a, const T& b) { return a + b; } // Overload with another template for pointer types template T plus(const T* a, const T* b) { return *a + *b; } // Result cannot be const char*, but has to be a new object. // Function template specialization can thus not be used either // (but that is, as you know, never a good idea anyway!). std::string plus(const char* a, const char* b) { return std::string{ a } + b; } int main() { int n{ plus(3, 4) }; std::cout << "plus(3, 4) returns " << n << std::endl; double d{ plus(3.2, 4.2) }; std::cout << "plus(3.2, 4.2) returns " << d << std::endl; std::string s1{ "aaa" }; std::string s2{ "bbb" }; auto s3{ plus(s1, s2) }; std::cout << "With s1 as " << s1 << " and s2 as " << s2 << std::endl; std::cout << "plus(s1, s2) returns " << s3 << std::endl; // The extra part: std::string s{ plus("he", "llo") }; std::cout << "plus(\"he\", \"llo\") returns " << s << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 10/Soln10_04.cpp ================================================ // Exercise 10-4.cpp // Your very own interpretation of std::size() #include #include #include #include // Look ma, no sizeof()! template size_t my_size(const T (&array)[N]) { return N; } // Overload with two other templates for std::vector<> and array<> template size_t my_size(const std::vector& vector) { return vector.size(); } template size_t my_size(const std::array& array) { return N; } // or array.size(); /* Instead of the latter two templates, you can create one template that would make my_size accept any argument and call size() on it. This will make it work for std::array<> and std::vector<>, as well as std::string and many other containers: template size_t my_size(const Collection& collection) { return collection.size(); } Potential downside is that this will instantiate for int and double type arguments as well, which may result in somewhat verbose compiler errors. This issue can easily be fixed using a requires clause, though (see Chapter 21). template requires requires (const Collection c) { c.size(); } size_t my_size(const Collection& collection) { return collection.size(); } */ int main() { int array[] {4, 8, 15, 16, 23, 42}; std::cout << "Size of numbers is " << my_size(array) << std::endl; // A string literal is also an array: std::cout << "Size of life lesson is " << my_size("Always wear a smile. One size fits all.") << std::endl; std::vector vector{4, 8, 15, 16, 23, 42}; std::cout << "Size of vector is " << my_size(vector) << std::endl; std::array array_object{4, 8, 15, 16, 23, 42}; std::cout << "Size of array_object is " << my_size(array_object) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 10/Soln10_05.cpp ================================================ // Exercise 10-5.cpp // The easiest way to verify this is using a static variable. // If indeed each function is only instantiated once, // then all calls should share the same static variable... // You should see this being reflected in the program's output // as main() starts by calling the function for doubles twice. #include #include #include template T larger(T a, T b); // Function template prototype int main() { std::cout << "Larger of 1.5 and 2.5 is " << larger(1.5, 2.5) << std::endl; std::cout << "Larger of 3.5 and 4.5 is " << larger(3.5, 4.5) << std::endl; int big_int {17011983}, small_int {10}; std::cout << std::format("Larger of {} and {} is {}\n", big_int, small_int, larger(big_int, small_int)); std::string a_string {"A"}, z_string {"Z"}; std::cout << std::format(R"(Larger of "{}" and "{}" is "{}")", a_string, z_string, larger(a_string, z_string)) << std::endl; } // Template for functions to return the larger of two values template T larger(T a, T b) { static size_t counter{}; std::cout << "This instantation has now been called " << ++counter << " time(s)\n"; return a > b? a : b; } ================================================ FILE: Exercises/NoModules/Chapter 10/Soln10_06.cpp ================================================ // Exercise 10-6 A Quicksort function template #include #include #include template void swap(std::vector& data, size_t first, size_t second); template void sort(std::vector& data); template void sort(std::vector& data, size_t start, size_t end); template void show(const std::vector& data, size_t width = 5); int main() { std::vector numbers{ -2, 4, -5, 6, 10, -40, 56, 4, 67, 45 }; show(numbers); sort(numbers); std::cout << "\nSorted integers:\n"; show(numbers); std::cout << "\nCharacters to be sorted:\n"; std::vector letters{ 'C', 'd', 'a', 'z', 't', 'S', 'p', 'm', 'D', 'f' }; show(letters, 2); sort(letters); std::cout << "\nSorted characters:\n"; show(letters, 2); std::cout << "\nFloating-point values to be sorted:\n"; std::vector values{ -2.5, 1.4, -2.55, 6.3, 10.1, -40.5, 56.0, 4.7, 67.3, 45.0 }; show(values, 10); sort(values); std::cout << "\nSorted floaating-point values:\n"; show(values, 10); } template void swap(std::vector& data, size_t first, size_t second) { auto temp{data[first]}; data[first] = data[second]; data[second] = temp; } // Sort data in ascending sequence template void sort(std::vector& data) { if (!data.empty()) sort(data, 0, data.size() - 1); } template void sort(std::vector& data, size_t start, size_t end) { // start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle address to partition set swap(data, start, (start + end) / 2); // Swap middle element with start element // Check data against chosen element size_t current {start}; for (size_t i {start + 1}; i <= end; i++) { if (data[i] < data[start]) // Is element less than chosen element? swap(data, ++current, i); // Yes, so swap to the left } swap(data, start, current); // Swap chosen and last swapped elements if (current > start) sort(data, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(data, current + 1, end); // Sort right subset if exists } template void show(const std::vector& data, size_t width) { const size_t data_per_line{ 80 / width - 1}; std::cout << std::format("{:{}}", data[0], width); // Output first element size_t data_in_line {}; // Number of data in current line for (size_t i {1}; i < data.size(); ++i) { if (++data_in_line == data_per_line) { data_in_line = 0; std::cout << std::endl; } std::cout << std::format("{:{}}", data[i], width); // Output an element } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_01/Integer.cpp ================================================ #include "Integer.h" #include Integer::Integer(int value) : m_value{value} { std::cout << "Object created." << std::endl; } void Integer::show() const { std::cout << "Value is " << m_value << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_01/Integer.h ================================================ #ifndef INTEGER_H #define INTEGER_H class Integer { public: Integer(int value); int getValue() const { return m_value; } void setValue(int value) { m_value = value; } void show() const; private: int m_value; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_01/Soln12_01.cpp ================================================ // Implementing an Integer class #include "Integer.h" #include int main() { std::cout << "Create i with the value 10." << std::endl; Integer i {10}; i.show(); std::cout << "Change value of i to 15." << std::endl; // i.m_value = 15; // Cannot assign directly to m_value i.setValue(15); i.show(); std::cout << "Create j with a value that is 150 times that of i." << std::endl; const Integer j {150 * i.getValue()}; j.show(); std::cout << "Set value of j to ." << std::endl; // j.setValue(5000); // Cannot call setValue() on const object // (show() and getValue() work, though) std::cout << "Create k with the value 789." << std::endl; Integer k {789}; k.show(); std::cout << "Set value of k to sum of i and j values." << std::endl; k.setValue(i.getValue() + j.getValue()); k.show(); } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_02/Integer.cpp ================================================ #include "Integer.h" #include // Constructor Integer::Integer(int value) : m_value{ value } { std::cout << "Object created." << std::endl; } // Copy constructor Integer::Integer(const Integer& obj) : m_value{ obj.m_value } { std::cout << "Object created by copy constructor." << std::endl; } // Compare function with reference parameter int Integer::compare(const Integer& obj) const { if (m_value < obj.m_value) return -1; else if (m_value == obj.m_value) return 0; else return 1; } void Integer::show() const { std::cout << "Value is " << m_value << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_02/Integer.h ================================================ /***************************************************************\ This header shows two distinct ways of allowing an Integer to be constructed without an argument: 1) The first is to add a default constructor. To make sure m_value is zero, it adds zero initialization to the declaration of m_value. 2) The second (at the bottom of the file) is commented out and uses a default parameter value for the existing single-argument constructor. \***************************************************************/ #ifndef INTEGER_H #define INTEGER_H // Option 1: zero-initialize n and add a default constructor class Integer { public: Integer() = default; // Zero-arg constructor Integer(int value); // Contructor with given value Integer(const Integer& obj); // Copy constructor int getValue() const { return m_value; } void setValue(int value) { m_value = value; } // int compare(Integer obj) const; // Compare function with value parameter int compare(const Integer& obj) const; // Compare function with reference parameter void show() const; private: int m_value{}; }; // Option 2: use zero a default parameter value /* class Integer { public: Integer(int value = 0); // Contructor with given value int getValue() const { return m_value; } void setValue(int value) { m_value = value; } // int compare(Integer obj) const; // Compare function with value parameter int compare(const Integer& obj) const; // Compare function with reference parameter void show() const; private: int m_value; }; */ #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_02/Soln12_02.cpp ================================================ // Using a function with a reference parameter in the Integer class #include #include "Integer.h" /*****************************************************************\ Using the version of compare() with the pass-by-value parameter, the copy constructor is called because a copy of the argument is passed to the function. Using the version with the reference parameter a reference to the object is passed to the function so no constructor call is necessary. You cannot overload a function with a reference parameter with a function that has a non-reference parameter because the compiler cannot tell which function should be called in any particular instance. \*****************************************************************/ int main() { std::cout << "Create i with the value 0." << std::endl; Integer i; i.show(); std::cout << "Change value of i to 15." << std::endl; i.setValue(15); i.show(); std::cout << "Create j from object i." << std::endl; Integer j {i}; j.show(); std::cout << "Set value of j to 150 times that of i." << std::endl; j.setValue(150 * i.getValue()); j.show(); std::cout << "Create k with the value 789." << std::endl; Integer k {789}; k.show(); std::cout << "Set value of k to sum of i and j values." << std::endl; k.setValue(i.getValue() + j.getValue()); k.show(); std::cout << "Result of comparing i and j is " << i.compare(j) << std::endl; std::cout << "Result of comparing k and j is " << k.compare(j) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_03/Integer.cpp ================================================ #include "Integer.h" #include // Constructor Integer::Integer(int value) : m_value{ value } { } // Copy constructor Integer::Integer(const Integer& obj) : m_value{obj.m_value} { } Integer& Integer::add(const Integer& obj) { m_value += obj.m_value; return *this; } Integer& Integer::subtract(const Integer& obj) { m_value -= obj.m_value; return *this; } Integer& Integer::multiply(const Integer& obj) { m_value *= obj.m_value; return *this; } int Integer::compare(const Integer& obj) const { if (m_value < obj.m_value) return -1; else if (m_value == obj.m_value) return 0; else return 1; } void Integer::show() const { std::cout << "Value is " << m_value << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_03/Integer.h ================================================ #ifndef INTEGER_H #define INTEGER_H class Integer { public: Integer(int value = 0); Integer(const Integer& obj); int getValue() const { return m_value; } void setValue(int value) { m_value = value; } Integer& add(const Integer& obj); Integer& subtract(const Integer& obj); Integer& multiply(const Integer& obj); int compare(const Integer& obj) const; void show() const; private: int m_value; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_03/Soln12_03.cpp ================================================ // Implementing add(), subtract() and multiply() #include #include "Integer.h" /*********************************************************** By returning the dereferenced this pointer in the functions we can call the functions successively in a single statement. Note the parameter is a reference-to-const; const because the argument is not changed by the function and a reference to avoid the overhead of copying objects. The only other requirement for achieving the calculation in a single statement is figuring out how to sequence to operations to allow this. ***********************************************************/ int main() { // Create the even operands as Integers, // and use implicit conversions from int for the odd values const Integer four{4}; const Integer six{6}; const Integer eight{8}; // We can calculate 4*5*5*5+6*5*5+7*5+8 as: // ((4*5+6)*5+7)*5+8 Integer result {four}; // Set result object as copy of four std::cout << "Result is " << result.multiply(5).add(six).multiply(5).add(7).multiply(5).add(eight).getValue() << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_04/Integer.cpp ================================================ #include #include "Integer.h" /****************************************************************\ Implementing compare() as a friend is quite simple. We must declare the function as a friend in the class definition. We now need both objects as arguments and the code in the body of the function just compares the n members of the arguments. Both parameters are references-to-const. However, other than the need for you to exercise friend functions, there's no real reason for the compare() function to be a friend of the Integer class: it can be implemented perfectly fine using the public getValue() function as well. The nonFriendCompare() function given below is therefore preferred over a friend function. \****************************************************************/ // Constructor Integer::Integer(int value) : m_value{value} { std::cout << "Object created." << std::endl; } // Copy constructor Integer::Integer(const Integer& obj) : m_value{ obj.m_value } { std::cout << "Object created by copy constructor." << std::endl; } void Integer::show() const { std::cout << "Value is " << m_value << std::endl; } // friend compare function int compare(const Integer& obj1, const Integer& obj2) { if (obj1.m_value < obj2.m_value) return -1; else if (obj1.m_value == obj2.m_value) return 0; else return 1; } // non-friend compare function int nonFriendCompare(const Integer& obj1, const Integer& obj2) { if (obj1.getValue() < obj2.getValue()) return -1; else if (obj1.getValue() == obj2.getValue()) return 0; else return 1; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_04/Integer.h ================================================ #ifndef INTEGER_H #define INTEGER_H class Integer { public: Integer(int value = 0); Integer(const Integer& obj); int getValue() const { return m_value; } void setValue(int value) { m_value = value; } void show() const; friend int compare(const Integer& obj1, const Integer& obj2); // friend compare function private: int m_value; }; // A non-friend function that implements the same function int nonFriendCompare(const Integer& obj1, const Integer& obj2); #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_04/Soln12_04.cpp ================================================ // Using a friend function #include #include "Integer.h" int main() { std::cout << "Create i with the value 10." << std::endl; Integer i {10}; i.show(); std::cout << "Change value of i to 15." << std::endl; i.setValue(15); i.show(); std::cout << "Create j from object i." << std::endl; Integer j {i}; j.show(); std::cout << "Set value of j to 150 times that of i." << std::endl; j.setValue(150 * i.getValue()); j.show(); std::cout << "Create k with the value 789." << std::endl; Integer k {789}; k.show(); std::cout << "Set value of k to sum of i and j values." << std::endl; k.setValue(i.getValue() + j.getValue()); k.show(); std::cout << "Result of comparing i and j is " << compare(i, j) << std::endl; std::cout << "Result of comparing k and j is " << compare(k, j) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_05/Integer.cpp ================================================ /*****************************************************************\ To implement printCount(), you first need a static member variable to store the object count. Every constructor should then increment this count, and you need to add a destructor that decrements it. \*****************************************************************/ #include #include "Integer.h" // Constructor Integer::Integer(int value) : m_value{value} { ++s_count; std::cout << "Object created." << std::endl; } // Copy constructor Integer::Integer(const Integer& obj) : m_value{obj.m_value} { ++s_count; std::cout << "Object created by copy constructor." << std::endl; } // Destructor Integer::~Integer() { --s_count; std::cout << "Object deleted." << std::endl; } void Integer::show() const { std::cout << "Value is " << m_value << std::endl; } int Integer::compare(const Integer& obj) const { if (m_value < obj.m_value) return -1; else if (m_value == obj.m_value) return 0; else return 1; } void Integer::printCount() { std::cout << "There are now " << s_count << " Integer object(s)." << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_05/Integer.h ================================================ #ifndef INTEGER_H #define INTEGER_H class Integer { public: Integer(int value = 0); // Contructor with given value Integer(const Integer& obj); // Copy constructor ~Integer(); // Destructor int getValue() const { return m_value; } void setValue(int value) { m_value = value; } int compare(const Integer& obj) const; // Compare function with reference parameter void show() const; static void printCount(); private: int m_value; static inline unsigned int s_count {}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_05/Soln12_05.cpp ================================================ // Using static members and a destructor to keep track of an object count #include #include "Integer.h" void showIntegerVal(Integer it) { it.show(); Integer::printCount(); // passed by value, so object count temporarily increases } void showIntegerRef(const Integer& it) { it.show(); Integer::printCount(); // passed by reference, so object count did not increase } int main() { std::cout << "Create i with the value 0." << std::endl; Integer i; i.show(); Integer::printCount(); // 1 object if (i.getValue() == 0) { std::cout << "Create j from object i." << std::endl; Integer j {i}; j.show(); Integer::printCount(); // 2 objects } Integer::printCount(); // 1 object again (Integer j was deleted because its scope ended) Integer array[] { 1, 2, 3 }; Integer::printCount(); // 4 objects showIntegerRef(array[0]); showIntegerVal(array[1]); Integer::printCount(); // 4 objects again } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_06/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } void listBox() const { std::cout << std::format("Box({:.1f},{:.1f},{:.1f})", m_length, m_width, m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_06/RandomBoxes.h ================================================ #ifndef RANDOM_BOXES_H #define RANDOM_BOXES_H #include "Box.h" #include // For random number generation #include // For std::bind() #include // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max inline auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } inline Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } inline auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_06/Soln12_06.cpp ================================================ // Creating a nested iterator class #include "RandomBoxes.h" #include "Truckload.h" /* Turns out moving the getFirstBox() and getLastBox() functions to the nested iterator class is easy. We used the same names for the member variables, so it is just a matter of changing "Truckload::" into "Truckload::Iterator::". Makes sense: the logic for iterating the linked lists has not changed. The only difference is that now there are m_current and m_head pointers per iteration, instead of only the one pair in the Truckload object. This allows nested iterations, concurrent iterations, and so on. You will encounter this iterator pattern again in Chapters 19 and 20. Btw: if it strikes you as sub-optimal that our findLargestBox() and findSmallestBox() are so similar, do not despair: in Chapter 19 we teach you the techniques you need to avoid this so-called code duplication. */ SharedBox findLargestBox(const Truckload& truckload); SharedBox findSmallestBox(const Truckload& truckload); int main() { Truckload load1; // Create an empty list // Add 12 random Box objects to the list const size_t boxCount{ 12 }; for (size_t i{}; i < boxCount; ++i) load1.addBox(randomSharedBox()); std::cout << "The first list:\n"; load1.listBoxes(); // Copy the truckload Truckload copy{ load1 }; std::cout << "The copied truckload:\n"; copy.listBoxes(); // Find the largest Box in the list const auto largestBox{ findLargestBox(load1) }; std::cout << "\nThe largest box in the first list is "; largestBox->listBox(); std::cout << std::endl; load1.removeBox(largestBox); std::cout << "\nAfter deleting the largest box, the list contains:\n"; load1.listBoxes(); const size_t nBoxes{ 20 }; // Number of vector elements std::vector boxes; // Array of Box objects for (size_t i{}; i < nBoxes; ++i) boxes.push_back(randomSharedBox()); Truckload load2{ boxes }; std::cout << "\nThe second list:\n"; load2.listBoxes(); const auto smallestBox{ findSmallestBox(load2) }; std::cout << "\nThe smallest box in the second list is "; smallestBox->listBox(); std::cout << std::endl; } SharedBox findLargestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator SharedBox largestBox{ iterator.getFirstBox() }; SharedBox nextBox{ iterator.getNextBox() }; while (nextBox) { if (nextBox->compare(*largestBox) > 0) largestBox = nextBox; nextBox = iterator.getNextBox(); } return largestBox; } SharedBox findSmallestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator SharedBox smallestBox{ iterator.getFirstBox() }; SharedBox nextBox{ iterator.getNextBox() }; while (nextBox) { if (nextBox->compare(*smallestBox) < 0) smallestBox = nextBox; nextBox = iterator.getNextBox(); } return smallestBox; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_06/Truckload.cpp ================================================ #include "Truckload.h" #include // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } void Truckload::listBoxes() const { const size_t boxesPerLine{ 4 }; size_t count {}; for (Package* package{m_head}; package; package = package->m_next) { std::cout << ' '; package->m_box->listBox(); if (! (++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_06/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload void listBoxes() const; // Output the Boxes private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_07/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } void listBox() const { std::cout << std::format("Box({:.1f},{:.1f},{:.1f})", m_length, m_width, m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_07/RandomBoxes.h ================================================ #ifndef RANDOM_BOXES_H #define RANDOM_BOXES_H #include "Box.h" #include // For random number generation #include // For std::bind() #include // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max inline auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } inline Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } inline auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_07/Soln12_07.cpp ================================================ // Create a doubly-linked list of Packages #include "RandomBoxes.h" #include "Truckload.h" /* To show reverse iteration, we've modified findSmallestBox() to iterate in reverse order */ SharedBox findLargestBox(const Truckload& truckload); SharedBox findSmallestBox(const Truckload& truckload); int main() { Truckload load; // Create an empty list // Add 12 random Box objects to the list const size_t boxCount{ 12 }; for (size_t i{}; i < boxCount; ++i) load.addBox(randomSharedBox()); std::cout << "The random truckload:\n"; load.listBoxes(); std::cout << std::endl; std::cout << "The same random truckload in reverse:\n"; load.listBoxesReversed(); std::cout << std::endl; std::cout << "The largest box (found using forward iteration) is "; findLargestBox(load)->listBox(); std::cout << std::endl; std::cout << "The smallest box (found using reverse iteration) is "; findSmallestBox(load)->listBox(); std::cout << std::endl; } SharedBox findLargestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator SharedBox largestBox{ iterator.getFirstBox() }; SharedBox nextBox{ iterator.getNextBox() }; while (nextBox) { if (nextBox->compare(*largestBox) > 0) largestBox = nextBox; nextBox = iterator.getNextBox(); } return largestBox; } SharedBox findSmallestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator SharedBox smallestBox{ iterator.getLastBox() }; SharedBox nextBox{ iterator.getPreviousBox() }; while (nextBox) { if (nextBox->compare(*smallestBox) < 0) smallestBox = nextBox; nextBox = iterator.getPreviousBox(); } return smallestBox; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_07/Truckload.cpp ================================================ #include "Truckload.h" #include // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package* m_previous; // Pointer to the previous Package in the list Package(SharedBox box) : m_box{ box }, m_next{}, m_previous{} {} ~Package() { delete m_next; } }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } void Truckload::listBoxes() const { const size_t boxesPerLine{ 4 }; size_t count {}; for (Package* package{m_head}; package; package = package->m_next) { std::cout << ' '; package->m_box->listBox(); if (! (++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } void Truckload::listBoxesReversed() const { const size_t boxesPerLine{ 4 }; size_t count{}; for (Package* package{ m_tail }; package; package = package->m_previous) { std::cout << ' '; package->m_box->listBox(); if (!(++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head, m_tail }; } SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } SharedBox Truckload::Iterator::getLastBox() { // Return m_tail's box (or nullptr if the list is empty) m_current = m_tail; return m_current ? m_current->m_box : nullptr; } SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } SharedBox Truckload::Iterator::getPreviousBox() { if (!m_current) // If there's no current... return getLastBox(); // ...return the last Box m_current = m_current->m_previous; // Move to the next package return m_current ? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty { package->m_previous = m_tail; // The package is added after the old tail m_tail->m_next = package; // Append the new object to the tail } else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { // No need for a trailing pointer anymore! // (We can go back one using the m_previous pointer of the doubly-linked list...) Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // Update the doubly-linked list pointers // (make a sketch of this to better see what is going on here!) if (current->m_previous) current->m_previous->m_next = current->m_next; if (current->m_next) current->m_next->m_previous = current->m_previous; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = current->m_previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } current = current->m_next; // Move current along to the next Package } return false; // Return false: boxToRemove was not found } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_07/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload void listBoxes() const; // Output the Boxes void listBoxesReversed() const; // Output the Boxes in reversed order private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getLastBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box SharedBox getPreviousBox(); // Get the previous Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_tail; // The tail of the linked list (needed for getLastBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head, Package* tail) : m_head{ head } , m_tail{ tail } , m_current{ nullptr } {} }; #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_08/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } void listBox() const { std::cout << std::format("Box({:.1f},{:.1f},{:.1f})", m_length, m_width, m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_08/RandomBoxes.h ================================================ #ifndef RANDOM_BOXES_H #define RANDOM_BOXES_H #include "Box.h" #include // For random number generation #include // For std::bind() #include // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max inline auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } inline Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } inline auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } #endif ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_08/Soln12_08.cpp ================================================ // Remove Boxes from a Truckload without searching a second time #include "RandomBoxes.h" #include "Truckload.h" /********************************************************************************\ The key to the solution is that the Iterator object already contains a pointer to the Package that needs to be removed. Of course, external code cannot use a Package, but it can use the Iterator. To exploit the knowledge contained in the Iterator, we add an overload of the removeBox() function with an Iterator parameter instead of a Box. To facilitate things, we also added getCurrentBox() to Iterator, which allowed us to remove some duplicated code in the various other getters of that class. Both findLargestBox() and findSmallestBox() are updated to return an Iterator as well. Note that the same technique is used by all Standard Library containers and algorithms (see Chapter 20)! Notice also that we created removePackage() to avoid duplicating this code in the two overloads of removeBox(). An alternative solution that also does not duplicate the removal logic consists of using an Iterator in removeBox(SharedBox), and then invoke removeBox(Iterator) once the box is found. removePackage() is then no longer required. We recommend you give this a try! \**********************************************************************************/ Truckload::Iterator findLargestBox(const Truckload& truckload); Truckload::Iterator findSmallestBox(const Truckload& truckload); int main() { Truckload load; // Create an empty list // Add 12 random Box objects to the list const size_t boxCount{ 12 }; for (size_t i{}; i < boxCount; ++i) load.addBox(randomSharedBox()); std::cout << "The random truckload:\n"; load.listBoxes(); std::cout << std::endl; const auto largestIter{ findLargestBox(load) }; const auto smallestIter{ findSmallestBox(load) }; std::cout << "The largest box (found using forward iteration) is "; largestIter.getCurrentBox()->listBox(); std::cout << '\n' << std::endl; load.removeBox(largestIter); std::cout << "The truckload without its largest box:\n"; load.listBoxes(); std::cout << std::endl; std::cout << "The smallest box (found using reverse iteration) is "; smallestIter.getCurrentBox()->listBox(); std::cout << '\n' << std::endl; load.removeBox(smallestIter); std::cout << "The truckload without its smallest box (in reverse order):\n"; load.listBoxesReversed(); } Truckload::Iterator findLargestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator iterator.getFirstBox(); auto largestBoxIterator{ iterator }; while (iterator.getNextBox()) { if (iterator.getCurrentBox()->compare(*largestBoxIterator.getCurrentBox()) > 0) { largestBoxIterator = iterator; } } return largestBoxIterator; } Truckload::Iterator findSmallestBox(const Truckload& truckload) { auto iterator{ truckload.getIterator() }; // Type of iterator is Truckload::Iterator iterator.getLastBox(); auto smallestBoxIterator{ iterator }; while (iterator.getPreviousBox()) { if (iterator.getCurrentBox()->compare(*smallestBoxIterator.getCurrentBox()) < 0) { smallestBoxIterator = iterator; } } return smallestBoxIterator; } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_08/Truckload.cpp ================================================ #include "Truckload.h" #include // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package* m_previous; // Pointer to the previous Package in the list Package(SharedBox box) : m_box{ box }, m_next{}, m_previous{} {} ~Package() { delete m_next; } }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } void Truckload::listBoxes() const { const size_t boxesPerLine{ 4 }; size_t count {}; for (Package* package{m_head}; package; package = package->m_next) { std::cout << ' '; package->m_box->listBox(); if (! (++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } void Truckload::listBoxesReversed() const { const size_t boxesPerLine{ 4 }; size_t count{}; for (Package* package{ m_tail }; package; package = package->m_previous) { std::cout << ' '; package->m_box->listBox(); if (!(++count % boxesPerLine)) std::cout << std::endl; } if (count % boxesPerLine) std::cout << std::endl; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head, m_tail }; } SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return getCurrentBox(); } SharedBox Truckload::Iterator::getLastBox() { // Return m_tail's box (or nullptr if the list is empty) m_current = m_tail; return getCurrentBox(); } SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return getCurrentBox(); } SharedBox Truckload::Iterator::getPreviousBox() { if (!m_current) // If there's no current... return getLastBox(); // ...return the last Box m_current = m_current->m_previous; // Move to the next package return getCurrentBox(); } SharedBox Truckload::Iterator::getCurrentBox() const { return m_current ? m_current->m_box : nullptr; } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty { package->m_previous = m_tail; // The package is added after the old tail m_tail->m_next = package; // Append the new object to the tail } else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { for (auto* current{ m_head }; current != nullptr; current = current->m_next) { if (current->m_box == boxToRemove) // We found the Box! { removePackage(current); return true; } } return false; // Return false: boxToRemove was not found } bool Truckload::removeBox(Iterator iter) { if (iter.m_current) { removePackage(iter.m_current); return true; } else { return false; } } void Truckload::removePackage(Package* package) { // Update the doubly-linked list pointers if (package->m_previous) package->m_previous->m_next = package->m_next; if (package->m_next) package->m_next->m_previous = package->m_previous; // Update pointers in member variables where required: if (package == m_head) m_head = package->m_next; if (package == m_tail) m_tail = package->m_previous; package->m_next = nullptr; // Disconnect the current Package from the list delete package; // and delete it } ================================================ FILE: Exercises/NoModules/Chapter 12/Soln12_08/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload bool removeBox(Iterator iter); // Remove the Box pointed to by this Iterator void listBoxes() const; // Output the Boxes void listBoxesReversed() const; // Output the Boxes in reversed order private: class Package; void removePackage(Package* package); Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getLastBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box SharedBox getPreviousBox(); // Get the previous Box SharedBox getCurrentBox() const; // Get the current Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_tail; // The tail of the linked list (needed for getLastBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head, Package* tail) : m_head{ head } , m_tail{ tail } , m_current{ nullptr } {} }; #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_01/Box.cpp ================================================ #include "Box.h" #include #include // For the min() and max() function templates double Box::volume() const { return m_length * m_width * m_height; } Box Box::operator+(const Box& aBox) const { // New object has larger length and width, and sum of heights return Box{ std::max(m_length, aBox.m_length), std::max(m_width, aBox.m_width), m_height + aBox.m_height }; } Box Box::operator*(double factor) const { return Box{ m_length * factor, m_width * factor, m_height * factor, }; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_01/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box operator+(const Box& aBox) const; // Function to add two Box objects Box operator*(double factor) const; // Function to multiply a Box with a given factor private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; std::ostream& operator<<(std::ostream& stream, const Box& box); #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_01/Soln13_01.cpp ================================================ // Exercise 13-1 // Implementing the * operator for the Box class // to post-multiply by an integer #include #include "Box.h" int main() { Box box {2, 3, 4}; std::cout << "Box is " << box << std::endl; unsigned n {3}; Box newBox{ box * n }; std::cout << "After multiplying by " << n << " box is " << newBox << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_02/Box.cpp ================================================ #include "Box.h" #include #include // For the min() and max() function templates double Box::volume() const { return m_length * m_width * m_height; } Box Box::operator+(const Box& aBox) const { // New object has larger length and width, and sum of heights return Box{ std::max(m_length, aBox.m_length), std::max(m_width, aBox.m_width), m_height + aBox.m_height }; } Box Box::operator*(double factor) const { return Box{ m_length * factor, m_width * factor, m_height * factor }; } Box operator*(double factor, const Box& box) { // Or: return box * factor; return Box{ box.getLength() * factor, box.getWidth() * factor, box.getHeight() * factor }; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_02/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box operator+(const Box& aBox) const; // Function to add two Box objects Box operator*(double factor) const; // Function to multiply a Box with a given factor private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; // Function to pre-multiply a Box with a given factor Box operator*(double factor, const Box& box); std::ostream& operator<<(std::ostream& stream, const Box& box); #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_02/Soln13_02.cpp ================================================ // Exercise 13-2 // Implementing the * operator for the Box class to pre-multiply by a number #include #include "Box.h" int main() { Box box {2, 3, 4}; std::cout << "Box is " << box << std::endl; unsigned n {3}; Box newBox{ n * box }; std::cout << "After pre-multiplying by " << n << " box is " << newBox << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_03/Box.cpp ================================================ #include "Box.h" #include #include // For the min() and max() function templates double Box::volume() const { return m_length * m_width * m_height; } Box Box::operator+(const Box& aBox) const { Box copy{ *this }; // Implement in terms of += operator (avoid duplication) copy += aBox; return copy; // Return new object (convention) } Box Box::operator*(double factor) const { Box copy{ *this }; // Implement in terms of *= operator (avoid duplication) copy *= factor; return copy; // Return new object (convention) } Box Box::operator/(double divisor) const { Box copy{ *this }; // Implement in terms of /= operator (avoid duplication) copy /= divisor; return copy; // Return new object (convention) } Box& Box::operator+=(const Box& aBox) { // New object has larger length and width, and sum of heights m_length = std::max(m_length, aBox.m_length); m_width = std::max(m_width, aBox.m_width); m_height += aBox.m_height; return *this; // Return a reference to the left operand (convention) } Box& Box::operator*=(double factor) { m_length *= factor; m_width *= factor; m_height *= factor; return *this; // Return a reference to the left operand (convention) } Box& Box::operator/=(double divisor) { m_length /= divisor; m_width /= divisor; m_height /= divisor; return *this; // Return a reference to the left operand (convention) } Box operator*(double factor, const Box& box) { return box * factor; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_03/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box operator+(const Box& aBox) const; // Function to add two Box objects Box operator*(double factor) const; // Function to multiply a Box with a given factor Box operator/(double divisor) const; // Function to divide a Box by a given divisor Box& operator+=(const Box& aBox); Box& operator*=(double factor); Box& operator/=(double divisor); private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; // Function to pre-multiply a Box with a given factor Box operator*(double factor, const Box& box); std::ostream& operator<<(std::ostream& stream, const Box& box); #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_03/Soln13_03.cpp ================================================ // Exercise 13-3 // Implementing the missing /, *=, +=, and /= operators for the Box class #include #include "Box.h" int main() { Box box {2, 3, 4}; std::cout << "Box is " << box << std::endl; size_t n {3}; box *= 3; std::cout << "After multiplying by " << n << " box is " << box << std::endl; box /= 3; std::cout << "After dividing by " << n << ", the box is again " << box << std::endl; Box newBox {2 * box}; std::cout << "Twice " << box << " is " << newBox << std::endl; std::cout << "Half that is again " << (newBox / 2) << std::endl; std::cout << "Adding both boxes gives " << (box + newBox) << std::endl; box += newBox; std::cout << "The same can be obtained by usign += as well: " << box << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_04/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream #include class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; bool operator==(double otherVolume) const { return volume() == otherVolume; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; inline std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_04/Soln13_04.cpp ================================================ // Exercise 13-4 // Adding support for equality operators that compare Boxes and volumes #include #include "Box.h" /* Because the C++20 compiler automatically rewrites != expression in terms of == and/or even reverses the order of the operands, only one additional operator overload definition is required to make it all work. */ int main() { Box box1{ 1, 2, 3 }; Box box2{ 3, 2, 1 }; Box box3{ 1, 2, 3 }; // Try out all == and != operators (old and new, the latter in both directions) std::cout << "box1 and box2 are " << (box1 == box2 ? "" : "not ") << "equal\n"; std::cout << "box1 and box3 are " << (box1 != box3 ? "not " : "") << "equal\n"; std::cout << "box1 is " << (box1 == 6.0 ? "" : "not ") << "equal to 6.0\n"; std::cout << "10.0 is " << (10 != box2 ? "not " : "") << "equal to box2\n"; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_05/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream #include class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; // Unary negation operator (!box is true if the Box has no volume) bool operator!() const { return volume() == 0; } // Type conversion operator (converts a Box to a Boolean; true if it has volume) operator bool() const { return volume() != 0; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; inline std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_05/Soln13_05.cpp ================================================ // Exercise 13-5 // Implementing the obvious operators for the Box class // to allow it to be used, for instance, in if statements. // A Box is "true" if and only if its volume is non-zero. #include #include "Box.h" void testBox(const Box& box) { std::cout << "The box's volume is " << box.volume() << ".\n"; if (box) std::cout << "This volume is non-zero."; if (!box) std::cout << "This volume is zero."; std::cout << std::endl; } int main() { Box box1{2, 3, 4}; std::cout << "box1 is " << box1 << std::endl; testBox(box1); std::cout << std::endl;; Box box2{0, 0, 0}; std::cout << "box2 is " << box2 << std::endl; testBox(box2); } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_06/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream #include class Box { public: // Constructors Box() = default; Box(double l, double w, double h) : m_length{ l }, m_width{ w }, m_height{ h } {} double volume() const { return m_length * m_width * m_height; } // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } std::partial_ordering operator<=>(const Box& otherBox) const { return volume() <=> otherBox.volume(); } std::partial_ordering operator<=>(double otherVolume) const { return volume() <=> otherVolume; } bool operator==(const Box& otherBox) const = default; // Explicit type conversion operator (converts a Box to a Boolean; true if it has volume) explicit operator bool() const { return volume() != 0; } private: double m_length{ 1.0 }; double m_width{ 1.0 }; double m_height{ 1.0 }; }; inline std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_06/Soln13_06.cpp ================================================ // Exercise 13-6 // In canonical C++, you should only implement a single operator to allow // objects to be used in if statements, and in Boolean expressions in general: // a conversion operator for type bool. // // This conversion operator, moreover, is normally qualified as explicit. // This is far from obvious: despite the explicit qualifier, // objects still implicitly convert to bool in, for instance, if statements. // As illustrated at the bottom of main(), however, // simply assigning a Box to a variable of type bool indeed no longer works // without an explicit type conversion (in most cases this is the desired behavior). #include #include "Box.h" void testBox(const Box& box) { std::cout << "The box's volume is " << box.volume() << ".\n"; if (box) std::cout << "This volume is non-zero."; if (!box) std::cout << "This volume is zero."; std::cout << std::endl; } int main() { Box box1{2, 3, 4}; std::cout << "box1 is " << box1 << std::endl; testBox(box1); std::cout << std::endl;; Box box2{0, 0, 0}; std::cout << "box2 is " << box2 << std::endl; testBox(box2); // bool b1{ box1 }; /* Does not compile! */ bool b2{ static_cast(box2) }; // Needs an explicit type conversion } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_07/Rational.h ================================================ // Rational.h #ifndef RATIONAL_H #define RATIONAL_H #include class Rational { public: // Constructor Rational(int numerator = 0, int denominator = 1) : m_numerator {numerator}, m_denominator {denominator} {} // Accessors int getNumerator() const { return m_numerator; } int getDenominator() const { return m_denominator; } // Mutators void setNumerator(int numerator) { m_numerator = numerator; } void setDenominator(int denominator) { m_denominator = denominator; } // Casting operators (could be non-explicit as well: is a matter of taste) explicit operator double() const { return static_cast(m_numerator) / m_denominator; } explicit operator float() const { return static_cast(m_numerator) / m_denominator; } // Full support for all comparison operators with both Rational and double // through the magic of the spaceship operator. // The only other operator you need to define here is equality // (because the spaceship operator cannot be defaulted). auto operator<=>(const Rational& other) { return m_numerator * other.m_denominator <=> other.m_numerator * m_denominator; } auto operator<=>(double value) { return static_cast(*this) <=> value; } bool operator==(const Rational& other) { return m_numerator * other.m_denominator == other.m_numerator * m_denominator; } bool operator==(double value) { return static_cast(*this) == value; } // Unary arithmetic operator Rational operator-() const { return Rational{-m_numerator, m_denominator}; } // A Rational is false if and only if its numerator equals zero. // Note: for operator bool(), we used explicit here to sidestep ambiguities with other operators. // We did not add operator!, // because this conversion operator covers all use in Boolean expressions // (see also previous exercise). explicit operator bool() const { return m_numerator != 0; } // Compound assignment operators Rational& operator+=(const Rational& other) { m_numerator = m_numerator * other.m_denominator + other.m_numerator * m_denominator; m_denominator = m_denominator * other.m_denominator; return *this; }; Rational& operator-=(const Rational& other) { m_numerator = m_numerator * other.m_denominator - other.m_numerator * m_denominator; m_denominator = m_denominator * other.m_denominator; return *this; }; Rational& operator*=(const Rational& other) { m_numerator *= other.m_numerator; m_denominator *= other.m_denominator; return *this; }; Rational& operator/=(const Rational& other) { m_numerator *= other.m_denominator; m_denominator *= other.m_numerator; return *this; }; // Prefix and postfix increment and decrement operators Rational& operator++() { m_numerator += m_denominator; return *this; } const Rational operator++(int) { auto copy(*this); // Create a copy of the current object ++(*this); // Increment the current object using the prefix operator... return copy; // Return the unincremented copy } Rational& operator--() { m_numerator -= m_denominator; return *this; } const Rational operator--(int) { auto copy(*this); // Create a copy of the current object --(*this); // Increment the current object using the prefix operator... return copy; // Return the unincremented copy } private: int m_numerator, m_denominator; }; // Stream output operator inline std::ostream& operator<<(std::ostream& stream, const Rational& r) { return stream << r.getNumerator() << '/' << r.getDenominator(); } // Binary arithmetic operators: non-member functions to allow for implicit conversions // This allows expressions such as 2 * (myRationale + 1) inline Rational operator+(const Rational& one, const Rational& other) { auto copy{ one }; return copy += other; } inline Rational operator-(const Rational& one, const Rational& other) { auto copy{ one }; return copy -= other; } inline Rational operator*(const Rational& one, const Rational& other) { auto copy{ one }; return copy *= other; } inline Rational operator/(const Rational& one, const Rational& other) { auto copy{ one }; return copy /= other; } #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_07/Soln13_07.cpp ================================================ // Exercise 13-7 // Rational operators #include #include "Rational.h" int main() { Rational x{3, 4}; Rational y{1, 2}; std::cout << "x = " << x << std::endl; std::cout << "y = " << y << std::endl; std::cout << "x = " << static_cast(x) << std::endl; std::cout << "y = " << static_cast(y) << std::endl; std::cout << "-x = " << -x << std::endl; std::cout << "x + y = " << x + y << std::endl; std::cout << "x - y = " << x - y << std::endl; std::cout << "x * y = " << x * y << std::endl; std::cout << "x / y = " << x / y << std::endl; std::cout << "x + 2 = " << x + 2 << std::endl; std::cout << "3 - y = " << 3 - y << std::endl; std::cout << "x * 4 = " << x * 4 << std::endl; std::cout << "5 / y = " << 5 / y << std::endl; std::cout << std::boolalpha; // Print true and false as "true" and "false" instead of "1" and "0" std::cout << "x < y = " << (x < y) << std::endl; std::cout << "x > y = " << (x > y) << std::endl; std::cout << "x == y = " << (x == y) << std::endl; std::cout << "x != y = " << (x != y) << std::endl; std::cout << "x >= y = " << (x >= y) << std::endl; std::cout << "x >= y = " << (x <= y) << std::endl; std::cout << "x < 1 = " << (x < 1) << std::endl; std::cout << "2 > y = " << (2 > y) << std::endl; std::cout << "x == 3 = " << (x == 3) << std::endl; std::cout << "4 != y = " << (4 != y) << std::endl; std::cout << "x >= 5 = " << (x >= 5) << std::endl; std::cout << "6 >= y = " << (6 <= y) << std::endl; std::cout << "x < 1.0 = " << (x < 1.0) << std::endl; std::cout << "2 > y = " << (2.0 > y) << std::endl; std::cout << "x == 0.75 = " << (x == 0.75) << std::endl; std::cout << "1.5 != y = " << (1.5 != y) << std::endl; std::cout << "x >= 5 = " << (x >= 5.0) << std::endl; std::cout << "6 >= y = " << (6.0 <= y) << std::endl; x += Rational(1, 4); std::cout << "x += 1/4 --> x = " << x << std::endl; x *= 2; std::cout << "x *= 2 --> x = " << x << std::endl; y += 1; std::cout << "y += 1 --> y = " << y << std::endl; std::cout << "y++ = " << y++ << std::endl; std::cout << "y = " << y << std::endl; std::cout << "--y = " << --y << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_08/Box.cpp ================================================ #include "Box.h" #include #include // For the min() and max() function templates double Box::volume() const { return m_length * m_width * m_height; } // Overloaded += operator Box& Box::operator+=(const Box& aBox) { // New object has larger length and width, and sum of heights m_length = std::max(m_length, aBox.m_length); m_width = std::max(m_width, aBox.m_width); m_height += aBox.m_height; return *this; } // Function to add two Box objects Box Box::operator+(const Box& aBox) const { Box copy{ *this }; copy += aBox; return copy; } std::partial_ordering Box::operator<=>(const Box& aBox) const { return volume() <=> aBox.volume(); } std::partial_ordering Box::operator<=>(double value) const { return volume() <=> value; } std::ostream& operator<<(std::ostream& stream, const Box& box) { stream << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.getLength(), box.getWidth(), box.getHeight()); return stream; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_08/Box.h ================================================ #ifndef BOX_H #define BOX_H #include // For std::partial_ordering (see Chapter 4) #include // For std::ostream class Box { public: Box() = default; // Default constructor Box(double length, double width, double height) : m_length{ std::max(length,width) } , m_width { std::min(length,width) } , m_height{ height } {} double volume() const; // Function to calculate the volume // Accessors double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } // Functions that add full support for comparison operators std::partial_ordering operator<=>(const Box& aBox) const; std::partial_ordering operator<=>(double value) const; bool operator==(const Box& aBox) const = default; Box& operator+=(const Box& aBox); // Function to add a Box objects Box operator+(const Box& aBox) const; // Function to add two Box objects private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; std::ostream& operator<<(std::ostream& stream, const Box& box); #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_08/PRNG.h ================================================ #ifndef PRNG_H #define PRNG_H class PseudoRandomNumberGenerator { public: PseudoRandomNumberGenerator(int n = 0) : m_n{ n } {} // A function call operator (no parameters, return type int) // Unlike many function call operators, this one alters the state of the object. int operator()() { const int current{ m_n }; m_n = (m_n * 41 + 7) % 100; // See chapter 12 return current; } private: int m_n; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_08/Soln13_08.cpp ================================================ // Exercise 13-8 // Creating a simply pseudo-random number generator (PRNG) functor class. // Ours is a very limited one that generates numbers between 0 and 100. // Seeding and using it requires some type conversions, // but other than that our PRNG functor acts as a drop-in replacement to the original one! #include #include #include #include // For random number generation #include // For std::bind() #include "Box.h" #include "PRNG.h" int main() { const double limit {99}; // Upper limit on Box dimensions std::random_device seeder; // True random number generator to obtain a seed (slow) auto random{ PseudoRandomNumberGenerator{ static_cast(seeder()) } }; const size_t boxCount {20}; // Number of Box object to be created std::vector boxes; // Vector of Box objects // Create 20 Box objects for (size_t i {}; i < boxCount; ++i) boxes.push_back(Box{ static_cast(random()), static_cast(random()), static_cast(random()) }); size_t first {}; // Index of first Box object of pair size_t second {1}; // Index of second Box object of pair double minVolume {(boxes[first] + boxes[second]).volume()}; for (size_t i {}; i < boxCount - 1; ++i) { for (size_t j {i + 1}; j < boxCount; j++) { if (boxes[i] + boxes[j] < minVolume) { first = i; second = j; minVolume = (boxes[i] + boxes[j]).volume(); } } } std::cout << "The two boxes that sum to the smallest volume are " << boxes[first] << " and " << boxes[second] << '\n'; std::cout << std::format("The volume of the first box is {:.1f}\n", boxes[first].volume()); std::cout << std::format("The volume of the second box is {:.1f}\n", boxes[second].volume()); std::cout << "The sum of these boxes is " << (boxes[first] + boxes[second]) << '\n'; std::cout << std::format("The volume of the sum is {:.1f}", minVolume) << std::endl; Box sum{ 0, 0, 0 }; // Start from an empty Box for (const auto& box : boxes) // And then add all randomly generated Box objects sum += box; std::cout << "The sum of " << boxCount << " random boxes is " << sum << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_09/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include #include // For the std::min()/max() function templates class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } int compare(const Box& box) const { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return +1; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f},{:.1f},{:.1f})", box.m_length, box.m_width, box.m_height); } Box operator+(const Box& aBox) const // Function to add two Box objects { return Box{ std::max(m_length, aBox.m_length), std::max(m_width, aBox.m_width), m_height + aBox.m_height }; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_09/Soln13_09.cpp ================================================ // Exercise 13-9 // Adding an assignment operator for Truckload #include #include #include // For random number generation #include // For std::bind() #include "Truckload.h" // See Chapter 12 for an explanation of this function auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 1.0, max }; // Generate in [1, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const double limit{ 99.0 }; // Upper limit on Box dimensions auto random{ createUniformPseudoRandomNumberGenerator(limit) }; Truckload load; const size_t boxCount {20}; // Number of Box object to be created // Create boxCount Box objects for (size_t i {}; i < boxCount; ++i) load.addBox(std::make_shared(random(), random(), random())); std::cout << "The boxes in the Truckload are:\n"; std::cout << load << std::endl; Truckload copied; copied = load; // Use copy assignment std::cout << "The boxes in the copied Truckload are:\n"; std::cout << copied; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_09/Truckload.cpp ================================================ #include "Truckload.h" #include // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } Truckload& Truckload::operator=(const Truckload& other) { if (&other != this) // Do not forget: avoid issues with self-assignment! { delete m_head; // Delete all current packages m_head = m_tail = nullptr; // Reset both pointers // Same as copy constructor // (see Chapter 17 for the copy-and-swap idiom that allows you to avoid // duplicating logic like this...) for (Package* package{ other.m_head }; package; package = package->m_next) { addBox(package->m_box); } } return *this; } // Destructor: clean up the list Truckload::~Truckload() { delete m_head; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox& Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } return nullBox; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Exercises/NoModules/Chapter 13/Soln13_09/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor Truckload& operator=(const Truckload& other); // Copy assignment operator ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list static inline SharedBox nullBox{}; // Pointer to nullptr }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; std::ostream& operator<<(std::ostream& stream, const Truckload& load); #endif ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_01/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal #include #include "Animals.h" // Constructor Animal::Animal(std::string_view name, int weight) : m_name{name}, m_weight{weight} {} // Identify the animal void Animal::who() const { std::cout << "My name is " << m_name << " and I weigh " << m_weight << "lbs." << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_01/Animals.h ================================================ // Animal class and classes derived from Animal // Note: Animal, Lion, and Aardvark would typically be declared each in their // own header files, and then defined in three corresponding source files as well. // This example shows that this does not have to be the case; // that is: // - file names do not have to match the name of a class; and // - multiple classes may be declared and defined in the same file #ifndef ANIMALS_H #define ANIMALS_H #include #include class Animal { public: Animal(std::string_view name, int weight); // Constructor void who() const; // Display name and weight private: std::string m_name; // Name of the animal int m_weight; // Weight of the animal }; class Lion : public Animal { public: // Define Lion constructor that calls base class constructor Lion(std::string_view name, int weight) : Animal{name, weight} {} }; class Aardvark : public Animal { public: using Animal::Animal; // Inherit constructor instead (preferred) }; #endif ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_01/Soln14_01.cpp ================================================ // Exercise 14-1 Exercising the Animal classes // The solution shows two options, with the second one being the preferred option. #include "Animals.h" int main() { Lion myLion{"Leo", 400}; Aardvark myAardvark{"Algernon", 50}; myLion.who(); myAardvark.who(); } ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_02/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal #include #include "Animals.h" // Constructor Animal::Animal(std::string_view name, int weight) : m_name{name}, m_weight{weight} {} // Identify the animal void Animal::who() const { std::cout << "My name is " << m_name << " and I weigh " << m_weight << "lbs." << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_02/Animals.h ================================================ // Animal class and classes derived from Animal #ifndef ANIMALS_H #define ANIMALS_H #include #include class Animal { public: Animal(std::string_view name, int weight); // Constructor protected: void who() const; // Display name and weight private: std::string m_name; // Name of the animal int m_weight; // Weight of the animal }; class Lion : public Animal { public: // Define Lion constructor that calls base class constructor Lion(std::string_view name, int weight) : Animal{name, weight} {} // Create function in derived class that hides and explicitly // invokes the protected function of the base class void who() { return Animal::who(); } }; class Aardvark : public Animal { public: using Animal::Animal; // Inherit constructor instead (preferred) using Animal::who; // Use using to alter acccess specifier instead (preferred) }; #endif ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_02/Soln14_02.cpp ================================================ // Exercise 14-2 The who() function for the base class has the protected access specifier, // so we ensure the derived classes allow public access to the who() function. // The solution shows two alternatives, the second one being the preferred option. #include "Animals.h" int main() { Lion myLion{"Leo", 400}; Aardvark myAardvark{"Algernon", 50}; myLion.who(); myAardvark.who(); } ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_03/Animals.cpp ================================================ // Exercise 14-3 - Animals.cpp // Implementations of the Animal class and classes derived from Animal #include #include "Animals.h" // Constructor Animal::Animal(std::string_view name, int weight) : m_name{name}, m_weight{weight} {} // Identify the animal void Animal::who() const { std::cout << "My name is " << m_name << " and I weigh " << m_weight << "lbs." << std::endl; } // Identify the Lion void Lion::who() const { std::cout << "Rrroarrrr... I am a lion. "; Animal::who(); // Call base function } // Identify the Aardvark void Aardvark::who() const { Animal::who(); // Call base function std::cout << "Oh. And I'm an aardvark. " << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_03/Animals.h ================================================ // Animal class and classes derived from Animal #ifndef ANIMALS_H #define ANIMALS_H #include #include class Animal { public: Animal(std::string_view name, int weight); // Constructor void who() const; // Display name and weight private: std::string m_name; // Name of the animal int m_weight; // Weight of the animal }; class Lion : public Animal { public: // Define Lion constructor that calls base class constructor Lion(std::string_view name, int weight) : Animal{name, weight} {} void who() const; // Define Lion-specific function }; class Aardvark : public Animal { public: using Animal::Animal; // Inherit constructor void who() const; // Define Aardvark-specific function }; #endif ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_03/Soln14_03.cpp ================================================ // Exercise 14-3 By adding a few lines to the test program, we can see the difference // between the calls to the base class and derived class who() functions. #include #include "Animals.h" int main() { Lion myLion{"Leo", 400}; Aardvark myAardvark{"Algernon", 50}; std::cout << "Calling derived versions of who():\n"; myLion.who(); myAardvark.who(); std::cout << "\nCalling base versions of who():\n"; myLion.Animal::who(); // By qualifying the base class static_cast(myAardvark).who(); // By casting } ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_04/Person.cpp ================================================ // Person class implementation #include "Person.h" #include Person::Person(size_t age, std::string_view name, Gender gender) : m_age {age}, m_name {name}, m_gender {gender} { // Instead of just initializing the members with the argument values, // you could validate the arguments by doing reasonableness checks. // e.g. Name mustn't be empty, and age should be less than 130 say. // To handle a failure sensibly we really need exceptions, // but we don't get to those until chapter 16. } // Display details of Person object void Person::who() const { std::cout << "\nThis is " << m_name << " who is " << m_age << " years old." << std::endl; } void Person::haveBirthday() { ++m_age; } std::string_view Person::getGenderString() const { switch (m_gender) { case Gender::male: return "a male"; case Gender::female: return "a female"; case Gender::other: return "an other-gendered"; } // Unreachable return statement required by some compilers // (switch statement is exhaustive) return {}; } // Display details of Employee object void Employee::who() const { std::cout << getName() << " is " << getGenderString() << " employee aged " << getAge() << "." << std::endl; } // Display details of Executive object (execs are particularly sensitive about their age...) void Executive::who() const { std::cout << getName() << " is " << getGenderString() << " executive." << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_04/Person.h ================================================ // Person class and classes derived from Person #ifndef PERSON_H #define PERSON_H #include #include /* We covered the advantages of the principle of data hiding extensively in Chapter 12. In practice, a common convention is to make all data members of a class private. This should thus probably become your default as well. Getter and setter member functions can then be added to either the public or protected interface to control access and/or modifications. Alternatively, you could make the member variables of Person and Employee themselves protected. Often, people take this shortcut because it is somewhat less work: then you wouldn't need the protected getter functions. However, protected data is a bad idea, much for the same reason as public ones are. We refer to the corresponding section in Chapter 14 for a more detailed motivation. */ // Possible alternatives for representing a gender include: // - a Boolean value, but limited, and which is the 'true' gender? // - a char value (say 'm', 'f', 'o'), but then how to enforce that no other values are assigned? // - a string ("male", "female", etc), but same problem as with chars (and also excessively expensive) // The safest, most readable solution is therefore probably an enumeration: enum class Gender { male, female, other }; class Person { public: Person() = default; // Default constructor - necessary to define arrays Person(size_t age, std::string_view name, Gender gender); void who() const; // Display details void haveBirthday(); protected: // Functions to allow derived classes to access to a Person's details size_t getAge() const { return m_age; } const std::string& getName() const { return m_name; } Gender getGender() const { return m_gender; } // Convenience function: std::string_view getGenderString() const; private: size_t m_age{}; // Age in years std::string m_name; Gender m_gender{ Gender::female }; }; class Employee : public Person { public: Employee() = default; // Default constructor - necessary to define arrays Employee(size_t age, std::string_view name, Gender gender, long num) : Person{age, name, gender}, m_personnelNumber {num} {} void who() const; // Display details protected: long getPersonnelNumber() { return m_personnelNumber; } private: long m_personnelNumber{}; }; class Executive : public Employee { public: using Employee::Employee; // Inherit all constructors void who() const; // Display details }; #endif ================================================ FILE: Exercises/NoModules/Chapter 14/Soln14_04/Soln14_04.cpp ================================================ // Exercise 14-4 Working with Employee and Executive objects #include #include #include "Person.h" int main() { std::vector employees { Employee{ 21, "Randy Marathon", Gender::male, 34567 }, Employee{ 32, "Anna Pothecary", Gender::female, 34578 }, Employee{ 46, "Peter Out", Gender::male, 34589 }, Employee{ 37, "Sheila Rangeit", Gender::female, 34598 }, Employee{ 65, "Jack Ittin", Gender::male, 34667 } }; for (const auto& employee : employees) employee.who(); std::cout << std::endl; // Note: explicitly specifying the type in front of every {...} // in a vector's initializer list, like we did for Employees, // is actually not required... std::vector executives { { 44, "Irwin Pootlemeyer", Gender::other, 35567 }, { 32, "Alexa Workwell", Gender::female, 35578 }, { 42, "Steve Stove", Gender::male, 35589 }, { 33, "Sue Neenuf", Gender::female, 35598 }, { 29, "Melanie Clair", Gender::female, 35667 } }; for (const auto& executive : executives) { executive.who(); executive.Employee::who(); // Executive, I shall know thy age! } } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_01/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal #include "Animals.h" // Constructor Animal::Animal(std::string_view name, unsigned weight) : m_name{ name }, m_weight{ weight } {} // Return string describing the animal std::string Animal::who() const { return "My name is " + m_name + ". My weight is " + std::to_string(m_weight) + " lbs."; } // Make like a sheep std::string_view Sheep::sound() const { return "Baaaa!!"; } // Make like a dog std::string_view Dog::sound() const { return "Woof woof!!"; } // Make like a cow std::string_view Cow::sound() const { return "Mooooo!!"; } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_01/Animals.h ================================================ // Exercise 15-1 Animals.h // Animal classes #ifndef ANIMALS_H #define ANIMALS_H #include #include class Animal { public: Animal(std::string_view name, unsigned weight);// Constructor virtual ~Animal() = default; // Very important: a virtual destructor! virtual std::string who() const; // Return string containing name and weight virtual std::string_view sound() const = 0; // Return the sound of an animal private: std::string m_name; // Name of the animal unsigned m_weight; // Weight of the animal }; class Sheep : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a sheep }; class Dog : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a dog }; class Cow : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a cow }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_01/Soln15_01.cpp ================================================ // Exercise 15-1 Exercising Zoo and Animal classes #include "Zoo.h" #include // For random number generation #include // For std::bind() #include #include #include // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_name_options{ 10 }; using NameOptions = std::array; const NameOptions dogNames { "Fido", "Rover" , "Lassie" , "Lambikins", "Poochy", "Spit", "Gnasher", "Samuel" , "Wellington", "Patch" }; const NameOptions sheepNames { "Bozo", "Killer", "Tasty", "Pete", "Chops", "Blackie", "Whitey", "Eric" , "Sean", "Shep" }; const NameOptions cowNames { "Dolly", "Daisy", "Shakey", "Amy", "Dilly", "Dizzy", "Eleanor", "Zippy" , "Zappy", "Happy" }; const unsigned minDogWt{ 1 }; // Minimum weight of a dog in pounds const unsigned maxDogWt{ 120 }; // Maximum weight of a dog in pounds const unsigned minSheepWt{ 80 }; // Minimum weight of a dog in pounds const unsigned maxSheepWt{ 150 }; // Maximum weight of a dog in pounds const unsigned minCowWt{ 800 }; // Minimum weight of a dog in pounds const unsigned maxCowWt{ 1500 }; // Maximum weight of a dog in pounds auto randomAnimalType { createUniformPseudoRandomNumberGenerator(0, 2) }; // 0, 1, or 2 auto randomNameIndex { createUniformPseudoRandomNumberGenerator(0, num_name_options - 1) }; auto randomDogWeight { createUniformPseudoRandomNumberGenerator(minDogWt, maxDogWt) }; auto randomSheepWeight{ createUniformPseudoRandomNumberGenerator(minSheepWt, maxSheepWt) }; auto randomCowWeight { createUniformPseudoRandomNumberGenerator(minCowWt, maxCowWt) } ; std::vector animals; // Stores smart pointers to animals size_t numAnimals {}; // Number of animals to be created std::cout << "How many animals in the zoo? "; std::cin >> numAnimals; Zoo zoo; // Create an empty Zoo // Create random animals and add them to the Zoo for (size_t i {}; i < numAnimals; ++i) { switch (randomAnimalType()) { case 0: // Create a sheep zoo.addAnimal(std::make_shared(sheepNames[randomNameIndex()], randomSheepWeight())); break; case 1: // Create a dog zoo.addAnimal(std::make_shared(dogNames[randomNameIndex()], randomDogWeight())); break; case 2: // Create a cow zoo.addAnimal(std::make_shared(cowNames[randomNameIndex()], randomCowWeight())); break; } } zoo.showAnimals(); // Display the animals } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_01/Zoo.cpp ================================================ // Implementations of the Zoo class that stores pointers to Animals #include "Zoo.h" #include "Animals.h" #include // Constructor from a vector of animals Zoo::Zoo(const std::vector& animals) : m_animals{ animals } {} // Add an animal to the zoo void Zoo::addAnimal(AnimalPtr animal) { m_animals.push_back(animal); } // Output the animals and the sound they make void Zoo::showAnimals() const { for (const auto& animal : m_animals) { std::cout << animal->who() << ' ' << animal->sound() << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_01/Zoo.h ================================================ // The Zoo class representing a collection of animals #ifndef ZOO_H #define ZOO_H #include "Animals.h" #include #include using AnimalPtr = std::shared_ptr; // Define a type alias for convenience class Zoo { public: Zoo() = default; // Default constructor for an empty zoo Zoo(const std::vector& new_animals); // Constructor from a vector of animals virtual ~Zoo() = default; // Add a virtual destructor to allow classes to safely derive from Zoo; // possible examples of Zoo specializations include SafariPark, PettingZoo, ... void addAnimal(AnimalPtr animal); // Add an animal to the zoo void showAnimals() const; // Output the animals and the sound they make private: std::vector m_animals; // Stores pointers to the animals }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_02/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal #include "Animals.h" // Constructor Animal::Animal(std::string_view name, unsigned weight) : m_name{ name }, m_weight{ weight } {} // Return string describing the animal std::string Animal::who() const { return "My name is " + m_name + ". My weight is " + std::to_string(m_weight) + " lbs."; } // Sheep constructors Sheep::Sheep(std::string_view name, unsigned weight) : Sheep{name, weight, static_cast(0.1 * weight)} { } Sheep::Sheep(std::string_view name, unsigned weight, unsigned wool) : Animal{name, weight} , m_wool_weight{wool} { } // Override the behaviour of who() for Sheep to prepend "Woolly " to their name, // and subtract the weight of their wool from weight. std::string Sheep::who() const { return "My name is Woolly " + getName() + ". My weight is " + std::to_string(getWeight() - m_wool_weight) + " lbs."; } // Make like a sheep std::string_view Sheep::sound() const { return "Baaaa!!"; } // Make like a dog std::string_view Dog::sound() const { return "Woof woof!!"; } // Override the behaviour of who() for Cows to hide their weight std::string Cow::who() const { return "My name is " + getName() + "."; } // Make like a cow std::string_view Cow::sound() const { return "Mooooo!!"; } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_02/Animals.h ================================================ // Animal classes #ifndef ANIMALS_H #define ANIMALS_H #include #include class Animal { public: Animal(std::string_view name, unsigned weight); // Constructor virtual ~Animal() = default; // Very important: a virtual destructor! virtual std::string who() const; // Return string containing name and weight virtual std::string_view sound() const = 0; // Return the sound of an animal protected: // Protected getters for use in derived classes const std::string& getName() const { return m_name; } unsigned getWeight() const { return m_weight; } private: std::string m_name; // Name of the animal unsigned m_weight; // Weight of the animal }; class Sheep : public Animal { public: Sheep(std::string_view name, unsigned weight); Sheep(std::string_view name, unsigned weight, unsigned wool); std::string who() const override; // Override the behaviour of who() for Sheep std::string_view sound() const override; // Return the sound of a sheep private: unsigned m_wool_weight; }; class Dog : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a dog }; class Cow : public Animal { public: using Animal::Animal; // Inherit constructor std::string who() const override; // Override the behaviour of who() for Cows std::string_view sound() const override; // Return the sound of a cow }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_02/Soln15_02.cpp ================================================ // Exercise 15-2 Exercising Zoo and Animal classes #include "Zoo.h" #include // For random number generation #include // For std::bind() #include #include #include // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_name_options{ 10 }; using NameOptions = std::array; const NameOptions dogNames { "Fido", "Rover" , "Lassie" , "Lambikins", "Poochy", "Spit", "Gnasher", "Samuel" , "Wellington", "Patch" }; const NameOptions sheepNames { "Bozo", "Killer", "Tasty", "Pete", "Chops", "Blackie", "Whitey", "Eric" , "Sean", "Shep" }; const NameOptions cowNames { "Dolly", "Daisy", "Shakey", "Amy", "Dilly", "Dizzy", "Eleanor", "Zippy" , "Zappy", "Happy" }; const unsigned minDogWt{ 1 }; // Minimum weight of a dog in pounds const unsigned maxDogWt{ 120 }; // Maximum weight of a dog in pounds const unsigned minSheepWt{ 80 }; // Minimum weight of a dog in pounds const unsigned maxSheepWt{ 150 }; // Maximum weight of a dog in pounds const unsigned minCowWt{ 800 }; // Minimum weight of a dog in pounds const unsigned maxCowWt{ 1500 }; // Maximum weight of a dog in pounds auto randomAnimalType { createUniformPseudoRandomNumberGenerator(0, 2) }; // 0, 1, or 2 auto randomNameIndex { createUniformPseudoRandomNumberGenerator(0, num_name_options - 1) }; auto randomDogWeight { createUniformPseudoRandomNumberGenerator(minDogWt, maxDogWt) }; auto randomSheepWeight{ createUniformPseudoRandomNumberGenerator(minSheepWt, maxSheepWt) }; auto randomCowWeight { createUniformPseudoRandomNumberGenerator(minCowWt, maxCowWt) }; std::vector animals; // Stores smart pointers to animals size_t numAnimals {}; // Number of animals to be created std::cout << "How many animals in the zoo? "; std::cin >> numAnimals; Zoo zoo; // Create an empty Zoo // Create random animals and add them to the Zoo for (size_t i {}; i < numAnimals; ++i) { switch (randomAnimalType()) { case 0: // Create a sheep zoo.addAnimal(std::make_shared(sheepNames[randomNameIndex()], randomSheepWeight())); break; case 1: // Create a dog zoo.addAnimal(std::make_shared(dogNames[randomNameIndex()], randomDogWeight())); break; case 2: // Create a cow zoo.addAnimal(std::make_shared(cowNames[randomNameIndex()], randomCowWeight())); break; } } zoo.showAnimals(); // Display the animals } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_02/Zoo.cpp ================================================ // Implementations of the Zoo class that stores pointers to Animals #include "Zoo.h" #include "Animals.h" #include // Constructor from a vector of animals Zoo::Zoo(const std::vector& animals) : m_animals{ animals } {} // Add an animal to the zoo void Zoo::addAnimal(AnimalPtr animal) { m_animals.push_back(animal); } // Output the animals and the sound they make void Zoo::showAnimals() const { for (const auto& animal : m_animals) { std::cout << animal->who() << ' ' << animal->sound() << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_02/Zoo.h ================================================ // The Zoo class representing a collection of animals #ifndef ZOO_H #define ZOO_H #include "Animals.h" #include #include using AnimalPtr = std::shared_ptr; // Define a type alias for convenience class Zoo { public: Zoo() = default; // Default constructor for an empty zoo Zoo(const std::vector& new_animals); // Constructor from a vector of animals virtual ~Zoo() = default; // Add a virtual destructor to allow classes to safely derive from Zoo; // possible examples of Zoo specializations include SafariPark, PettingZoo, ... void addAnimal(AnimalPtr animal); // Add an animal to the zoo void showAnimals() const; // Output the animals and the sound they make private: std::vector m_animals; // Stores pointers to the animals }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_03/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal #include "Animals.h" // Constructor Animal::Animal(std::string_view name, unsigned weight) : m_name{ name }, m_weight{ weight } {} // Return string describing the animal std::string Animal::who() const { return "My name is " + m_name + ". My weight is " + std::to_string(m_weight) + " lbs."; } // Sheep constructors Sheep::Sheep(std::string_view name, unsigned weight) : Sheep{name, weight, static_cast(0.1 * weight)} { } Sheep::Sheep(std::string_view name, unsigned weight, unsigned wool) : Animal{name, weight} , m_wool_weight{wool} { } // Override the behaviour of getName() for Sheep to prepend "Woolly " std::string Sheep::getName() const { return "Woolly " + Animal::getName(); } // Override getWeight() to subtract the weight of their wool from weight. unsigned Sheep::getWeight() const { return Animal::getWeight() - m_wool_weight; } // Make like a sheep std::string_view Sheep::sound() const { return "Baaaa!!"; } // Make like a dog std::string_view Dog::sound() const { return "Woof woof!!"; } // Override the behaviour of who() for Cows to hide their weight std::string Cow::who() const { return "My name is " + getName() + "."; } // Make like a cow std::string_view Cow::sound() const { return "Mooooo!!"; } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_03/Animals.h ================================================ // Animal classes #ifndef ANIMALS_H #define ANIMALS_H #include #include class Animal { public: Animal(std::string_view name, unsigned weight); // Constructor virtual ~Animal() = default; // Very important: a virtual destructor! virtual std::string who() const; // Return string containing name and weight virtual std::string_view sound() const = 0; // Return the sound of an animal protected: // Protected getters for use in derived classes virtual std::string getName() const { return m_name; } virtual unsigned getWeight() const { return m_weight; } private: std::string m_name; // Name of the animal unsigned m_weight; // Weight of the animal }; class Sheep : public Animal { public: Sheep(std::string_view name, unsigned weight); Sheep(std::string_view name, unsigned weight, unsigned wool); std::string_view sound() const override; // Return the sound of a sheep protected: std::string getName() const override; unsigned getWeight() const override; private: unsigned m_wool_weight; }; class Dog : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a dog }; class Cow : public Animal { public: using Animal::Animal; // Inherit constructor std::string who() const override; // Override the behaviour of who() for Cows std::string_view sound() const override; // Return the sound of a cow }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_03/Soln15_03.cpp ================================================ // Exercise 15-3 Exercising Zoo and Animal classes #include "Zoo.h" #include // For random number generation #include // For std::bind() #include #include #include // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_name_options{ 10 }; using NameOptions = std::array; const NameOptions dogNames { "Fido", "Rover" , "Lassie" , "Lambikins", "Poochy", "Spit", "Gnasher", "Samuel" , "Wellington", "Patch" }; const NameOptions sheepNames { "Bozo", "Killer", "Tasty", "Pete", "Chops", "Blackie", "Whitey", "Eric" , "Sean", "Shep" }; const NameOptions cowNames { "Dolly", "Daisy", "Shakey", "Amy", "Dilly", "Dizzy", "Eleanor", "Zippy" , "Zappy", "Happy" }; const unsigned minDogWt{ 1 }; // Minimum weight of a dog in pounds const unsigned maxDogWt{ 120 }; // Maximum weight of a dog in pounds const unsigned minSheepWt{ 80 }; // Minimum weight of a dog in pounds const unsigned maxSheepWt{ 150 }; // Maximum weight of a dog in pounds const unsigned minCowWt{ 800 }; // Minimum weight of a dog in pounds const unsigned maxCowWt{ 1500 }; // Maximum weight of a dog in pounds auto randomAnimalType { createUniformPseudoRandomNumberGenerator(0, 2) }; // 0, 1, or 2 auto randomNameIndex { createUniformPseudoRandomNumberGenerator(0, num_name_options - 1) }; auto randomDogWeight { createUniformPseudoRandomNumberGenerator(minDogWt, maxDogWt) }; auto randomSheepWeight{ createUniformPseudoRandomNumberGenerator(minSheepWt, maxSheepWt) }; auto randomCowWeight { createUniformPseudoRandomNumberGenerator(minCowWt, maxCowWt) }; std::vector animals; // Stores smart pointers to animals size_t numAnimals {}; // Number of animals to be created std::cout << "How many animals in the zoo? "; std::cin >> numAnimals; Zoo zoo; // Create an empty Zoo // Create random animals and add them to the Zoo for (size_t i {}; i < numAnimals; ++i) { switch (randomAnimalType()) { case 0: // Create a sheep zoo.addAnimal(std::make_shared(sheepNames[randomNameIndex()], randomSheepWeight())); break; case 1: // Create a dog zoo.addAnimal(std::make_shared(dogNames[randomNameIndex()], randomDogWeight())); break; case 2: // Create a cow zoo.addAnimal(std::make_shared(cowNames[randomNameIndex()], randomCowWeight())); break; } } zoo.showAnimals(); // Display the animals } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_03/Zoo.cpp ================================================ // Implementations of the Zoo class that stores pointers to Animals #include "Zoo.h" #include "Animals.h" #include // Constructor from a vector of animals Zoo::Zoo(const std::vector& animals) : m_animals{ animals } {} // Add an animal to the zoo void Zoo::addAnimal(AnimalPtr animal) { m_animals.push_back(animal); } // Output the animals and the sound they make void Zoo::showAnimals() const { for (const auto& animal : m_animals) { std::cout << animal->who() << ' ' << animal->sound() << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_03/Zoo.h ================================================ // The Zoo class representing a collection of animals #ifndef ZOO_H #define ZOO_H #include "Animals.h" #include #include using AnimalPtr = std::shared_ptr; // Define a type alias for convenience class Zoo { public: Zoo() = default; // Default constructor for an empty zoo Zoo(const std::vector& new_animals); // Constructor from a vector of animals virtual ~Zoo() = default; // Add a virtual destructor to allow classes to safely derive from Zoo; // possible examples of Zoo specializations include SafariPark, PettingZoo, ... void addAnimal(AnimalPtr animal); // Add an animal to the zoo void showAnimals() const; // Output the animals and the sound they make private: std::vector m_animals; // Stores pointers to the animals }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_04/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal /* Because the Animal::m_weight member currently represents the total weight of the animal (we kept this from previous versions of these classes), we need to update the Animal's (total) weight during Sheep::shear(). In an alternate, arguably more elegant solution: - Animal::m_weight could reprent the "true" weight of the animal - Sheep::getWeight() could return Animal::getWeight() + m_wool_weight - Sheep::shear() could simply set m_wool_weight to 0 */ #include "Animals.h" // Constructor Animal::Animal(std::string_view name, unsigned weight) : m_name{ name }, m_weight{ weight } {} // Return string describing the animal std::string Animal::who() const { return "My name is " + getName() + ". My weight is " + std::to_string(getWeight()) + " lbs."; } void Animal::setWeight(unsigned weight) { m_weight = weight; } // Sheep constructors Sheep::Sheep(std::string_view name, unsigned weight) : Sheep{name, weight, static_cast(0.1 * weight)} { } Sheep::Sheep(std::string_view name, unsigned weight, unsigned wool) : Animal{name, weight} , m_wool_weight{wool} { } // Override the behaviour of getName() for Sheep to prepend "Woolly " std::string Sheep::getName() const { return "Woolly " + Animal::getName(); } // Override getWeight() to subtract the weight of their wool from weight. unsigned Sheep::getWeight() const { return Animal::getWeight() - m_wool_weight; } unsigned Sheep::shear() { // Somewhat odd statement that updates the total weight (stored in the Animal subobject) // to the actual weight of the sheep (see Sheep::getWeight()). // Of course, we do this *before* setting the wool's weight to 0. setWeight(getWeight()); const unsigned wool_weight{ m_wool_weight }; m_wool_weight = 0; return wool_weight; // Alternative: use std::exchange() (see Exercise 18-5) //return std::exchange(m_wool_weight, 0); } // Make like a sheep std::string_view Sheep::sound() const { return "Baaaa!!"; } // Make like a dog std::string_view Dog::sound() const { return "Woof woof!!"; } // Override the behaviour of who() for Cows to hide their weight std::string Cow::who() const { return "My name is " + getName() + "."; } // Make like a cow std::string_view Cow::sound() const { return "Mooooo!!"; } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_04/Animals.h ================================================ // Exercise 14-2 Animals.h // Animal classes #ifndef ANIMALS_H #define ANIMALS_H #include #include class Animal { public: Animal(std::string_view name, unsigned weight); // Constructor virtual ~Animal() = default; // Very important: a virtual destructor! virtual std::string who() const; // Return string containing name and weight virtual std::string_view sound() const = 0; // Return the sound of an animal protected: // Protected getters for use in derived classes virtual std::string getName() const { return m_name; } virtual unsigned getWeight() const { return m_weight; } void setWeight(unsigned weight); private: std::string m_name; // Name of the animal unsigned m_weight; // Weight of the animal }; class Sheep : public Animal { public: Sheep(std::string_view name, unsigned weight); Sheep(std::string_view name, unsigned weight, unsigned wool); std::string_view sound() const override; // Return the sound of a sheep unsigned shear(); protected: std::string getName() const override; unsigned getWeight() const override; private: unsigned m_wool_weight; }; class Dog : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a dog }; class Cow : public Animal { public: using Animal::Animal; // Inherit constructor std::string who() const override; // Override the behaviour of who() for Cows std::string_view sound() const override; // Return the sound of a cow }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_04/Soln15_04.cpp ================================================ // Exercise 15-4 Exercising Zoo and Animal classes #include "Zoo.h" #include // For random number generation #include // For std::bind() #include #include #include // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_name_options{ 10 }; using NameOptions = std::array; const NameOptions dogNames { "Fido", "Rover" , "Lassie" , "Lambikins", "Poochy", "Spit", "Gnasher", "Samuel" , "Wellington", "Patch" }; const NameOptions sheepNames { "Bozo", "Killer", "Tasty", "Pete", "Chops", "Blackie", "Whitey", "Eric" , "Sean", "Shep" }; const NameOptions cowNames { "Dolly", "Daisy", "Shakey", "Amy", "Dilly", "Dizzy", "Eleanor", "Zippy" , "Zappy", "Happy" }; const unsigned minDogWt{ 1 }; // Minimum weight of a dog in pounds const unsigned maxDogWt{ 120 }; // Maximum weight of a dog in pounds const unsigned minSheepWt{ 80 }; // Minimum weight of a dog in pounds const unsigned maxSheepWt{ 150 }; // Maximum weight of a dog in pounds const unsigned minCowWt{ 800 }; // Minimum weight of a dog in pounds const unsigned maxCowWt{ 1500 }; // Maximum weight of a dog in pounds auto randomAnimalType { createUniformPseudoRandomNumberGenerator(0, 2) }; // 0, 1, or 2 auto randomNameIndex { createUniformPseudoRandomNumberGenerator(0, num_name_options - 1) }; auto randomDogWeight { createUniformPseudoRandomNumberGenerator(minDogWt, maxDogWt) }; auto randomSheepWeight{ createUniformPseudoRandomNumberGenerator(minSheepWt, maxSheepWt) }; auto randomCowWeight { createUniformPseudoRandomNumberGenerator(minCowWt, maxCowWt) }; std::vector animals; // Stores smart pointers to animals size_t numAnimals {}; // Number of animals to be created std::cout << "How many animals in the zoo? "; std::cin >> numAnimals; Zoo zoo; // Create an empty Zoo // Create random animals and add them to the Zoo for (size_t i {}; i < numAnimals; ++i) { switch (randomAnimalType()) { case 0: // Create a sheep zoo.addAnimal(std::make_shared(sheepNames[randomNameIndex()], randomSheepWeight())); break; case 1: // Create a dog zoo.addAnimal(std::make_shared(dogNames[randomNameIndex()], randomDogWeight())); break; case 2: // Create a cow zoo.addAnimal(std::make_shared(cowNames[randomNameIndex()], randomCowWeight())); break; } } zoo.showAnimals(); // Display the animals std::cout << "\nHerding and shearing all sheep..." << std::endl; for (auto* sheep : zoo.herd()) { sheep->shear(); } zoo.showAnimals(); // Display the animals again } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_04/Zoo.cpp ================================================ // Implementations of the Zoo class that stores pointers to Animals #include "Zoo.h" #include "Animals.h" #include // Constructor from a vector of animals Zoo::Zoo(const std::vector& animals) : m_animals{ animals } {} // Add an animal to the zoo void Zoo::addAnimal(AnimalPtr animal) { m_animals.push_back(animal); } // Output the animals and the sound they make void Zoo::showAnimals() const { for (const auto& animal : m_animals) { std::cout << animal->who() << ' ' << animal->sound() << std::endl; } } // Collect all Sheep in the Zoo using dynamic cast (the recommended way) std::vector Zoo::herd() const { std::vector sheep; for (auto animal : m_animals) { auto* casted{ dynamic_cast(animal.get()) }; if (casted) { sheep.push_back(casted); } } return sheep; } /* // Collect all Sheep in the Zoo using the typeid() operator and a static cast // (for the sake of the exercise only) #include // required when using typeid() std::vector Zoo::herd() const { std::vector sheep; for (auto animal : m_animals) { if (typeid(*animal) == typeid(Sheep)) { sheep.push_back(static_cast(animal.get())); } } return sheep; } */ ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_04/Zoo.h ================================================ // The Zoo class representing a collection of animals #ifndef ZOO_H #define ZOO_H #include "Animals.h" #include #include using AnimalPtr = std::shared_ptr; // Define a type alias for convenience class Zoo { public: Zoo() = default; // Default constructor for an empty zoo Zoo(const std::vector& new_animals); // Constructor from a vector of animals virtual ~Zoo() = default; // Add a virtual destructor to allow classes to safely derive from Zoo; // possible examples of Zoo specializations include SafariPark, PettingZoo, ... void addAnimal(AnimalPtr animal); // Add an animal to the zoo void showAnimals() const; // Output the animals and the sound they make std::vector herd() const; // Collect all Sheep in the Zoo private: std::vector m_animals; // Stores pointers to the animals }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_05/Animals.cpp ================================================ // Implementations of the Animal class and classes derived from Animal /* Because the Animal::m_weight member currently represents the total weight of the animal (we kept this from previous versions of these classes), we need to update the Animal's (total) weight during Sheep::shear(). In an alternate, arguably more elegant solution: - Animal::m_weight could reprent the "true" weight of the animal - Sheep::getWeight() could return Animal::getWeight() + m_wool_weight - Sheep::shear() could simply set m_wool_weight to 0 */ #include "Animals.h" // Constructor Animal::Animal(std::string_view name, unsigned weight) : m_name{ name }, m_weight{ weight } {} // Return string describing the animal std::string Animal::who() const { return "My name is " + getName() + ". My weight is " + std::to_string(getWeight()) + " lbs."; } void Animal::setWeight(unsigned weight) { m_weight = weight; } // Sheep constructors Sheep::Sheep(std::string_view name, unsigned weight) : Sheep{name, weight, static_cast(0.1 * weight)} { } Sheep::Sheep(std::string_view name, unsigned weight, unsigned wool) : Animal{name, weight} , m_wool_weight{wool} { } // Override the behaviour of getName() for Sheep to prepend "Woolly " std::string Sheep::getName() const { return "Woolly " + Animal::getName(); } // Override getWeight() to subtract the weight of their wool from weight. unsigned Sheep::getWeight() const { return Animal::getWeight() - m_wool_weight; } unsigned Sheep::shear() { // Somewhat odd statement that updates the total weight (stored in the Animal subobject) // to the actual weight of the sheep (see Sheep::getWeight()). // Of course, we do this *before* setting the wool's weight to 0. setWeight(getWeight()); const unsigned wool_weight{ m_wool_weight }; m_wool_weight = 0; return wool_weight; // Alternative: use std::exchange() (see Exercise 18-5) //return std::exchange(m_wool_weight, 0); } // Make like a sheep std::string_view Sheep::sound() const { return "Baaaa!!"; } // Make like a dog std::string_view Dog::sound() const { return "Woof woof!!"; } // Override the behaviour of who() for Cows to hide their weight std::string Cow::who() const { return "My name is " + getName() + "."; } // Make like a cow std::string_view Cow::sound() const { return "Mooooo!!"; } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_05/Animals.h ================================================ // Exercise 14-2 Animals.h // Animal classes #ifndef ANIMALS_H #define ANIMALS_H #include #include class Animal { public: Animal(std::string_view name, unsigned weight); // Constructor virtual ~Animal() = default; // Very important: a virtual destructor! virtual std::string who() const; // Return string containing name and weight virtual std::string_view sound() const = 0; // Return the sound of an animal protected: // Protected getters for use in derived classes virtual std::string getName() const { return m_name; } virtual unsigned getWeight() const { return m_weight; } void setWeight(unsigned weight); private: std::string m_name; // Name of the animal unsigned m_weight; // Weight of the animal }; class Sheep : public Animal { public: Sheep(std::string_view name, unsigned weight); Sheep(std::string_view name, unsigned weight, unsigned wool); std::string_view sound() const override; // Return the sound of a sheep unsigned shear(); protected: std::string getName() const override; unsigned getWeight() const override; private: unsigned m_wool_weight; }; class Dog : public Animal { public: using Animal::Animal; // Inherit constructor std::string_view sound() const override; // Return the sound of a dog }; class Cow : public Animal { public: using Animal::Animal; // Inherit constructor std::string who() const override; // Override the behaviour of who() for Cows std::string_view sound() const override; // Return the sound of a cow }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_05/Soln15_05.cpp ================================================ // Exercise 15-5 Exercising Zoo and Animal classes #include "Zoo.h" #include // For random number generation #include // For std::bind() #include #include #include // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_name_options{ 10 }; using NameOptions = std::array; const NameOptions dogNames { "Fido", "Rover" , "Lassie" , "Lambikins", "Poochy", "Spit", "Gnasher", "Samuel" , "Wellington", "Patch" }; const NameOptions sheepNames { "Bozo", "Killer", "Tasty", "Pete", "Chops", "Blackie", "Whitey", "Eric" , "Sean", "Shep" }; const NameOptions cowNames { "Dolly", "Daisy", "Shakey", "Amy", "Dilly", "Dizzy", "Eleanor", "Zippy" , "Zappy", "Happy" }; const unsigned minDogWt{ 1 }; // Minimum weight of a dog in pounds const unsigned maxDogWt{ 120 }; // Maximum weight of a dog in pounds const unsigned minSheepWt{ 80 }; // Minimum weight of a dog in pounds const unsigned maxSheepWt{ 150 }; // Maximum weight of a dog in pounds const unsigned minCowWt{ 800 }; // Minimum weight of a dog in pounds const unsigned maxCowWt{ 1500 }; // Maximum weight of a dog in pounds auto randomAnimalType { createUniformPseudoRandomNumberGenerator(0, 2) }; // 0, 1, or 2 auto randomNameIndex { createUniformPseudoRandomNumberGenerator(0, num_name_options - 1) }; auto randomDogWeight { createUniformPseudoRandomNumberGenerator(minDogWt, maxDogWt) }; auto randomSheepWeight{ createUniformPseudoRandomNumberGenerator(minSheepWt, maxSheepWt) }; auto randomCowWeight { createUniformPseudoRandomNumberGenerator(minCowWt, maxCowWt) }; std::vector animals; // Stores smart pointers to animals size_t numAnimals {}; // Number of animals to be created std::cout << "How many animals in the zoo? "; std::cin >> numAnimals; Zoo zoo; // Create an empty Zoo // Create random animals and add them to the Zoo for (size_t i {}; i < numAnimals; ++i) { switch (randomAnimalType()) { case 0: // Create a sheep zoo.addAnimal(std::make_shared(sheepNames[randomNameIndex()], randomSheepWeight())); break; case 1: // Create a dog zoo.addAnimal(std::make_shared(dogNames[randomNameIndex()], randomDogWeight())); break; case 2: // Create a cow zoo.addAnimal(std::make_shared(cowNames[randomNameIndex()], randomCowWeight())); break; } } zoo.showAnimals(); // Display the animals std::cout << "\nHerding and shearing all sheep..." << std::endl; for (auto sheep : zoo.herd()) { sheep->shear(); } zoo.showAnimals(); // Display the animals again } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_05/Zoo.cpp ================================================ // Implementations of the Zoo class that stores pointers to Animals #include "Zoo.h" #include "Animals.h" #include // Constructor from a vector of animals Zoo::Zoo(const std::vector& animals) : m_animals{ animals } {} // Add an animal to the zoo void Zoo::addAnimal(AnimalPtr animal) { m_animals.push_back(animal); } // Output the animals and the sound they make void Zoo::showAnimals() const { for (const auto& animal : m_animals) { std::cout << animal->who() << ' ' << animal->sound() << std::endl; } } // Collect all Sheep in the Zoo using dynamic cast (the recommended way) std::vector Zoo::herd() const { std::vector sheep; for (auto animal : m_animals) { auto casted{ std::dynamic_pointer_cast(animal) }; if (casted) { sheep.push_back(casted); } } return sheep; } ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_05/Zoo.h ================================================ // The Zoo class representing a collection of animals #ifndef ZOO_H #define ZOO_H #include "Animals.h" #include #include using AnimalPtr = std::shared_ptr; // Define a type alias for convenience using SheepPtr = std::shared_ptr; class Zoo { public: Zoo() = default; // Default constructor for an empty zoo Zoo(const std::vector& new_animals); // Constructor from a vector of animals virtual ~Zoo() = default; // Add a virtual destructor to allow classes to safely derive from Zoo; // possible examples of Zoo specializations include SafariPark, PettingZoo, ... void addAnimal(AnimalPtr animal); // Add an animal to the zoo void showAnimals() const; // Output the animals and the sound they make std::vector herd() const; // Collect all Sheep in the Zoo private: std::vector m_animals; // Stores pointers to the animals }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_06/Point.h ================================================ // A simple class for 2-dimensional points #ifndef POINT_H #define POINT_H class Point final { public: Point(double x, double y) : m_x{x}, m_y{y} {} double getX() const { return m_x; } double getY() const { return m_y; } void setX(double x) { m_x = x; } void setY(double y) { m_y = y; } private: double m_x; double m_y; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_06/Shapes.h ================================================ // Shape classes #ifndef SHAPE_H #define SHAPE_H #include "Point.h" #include // Generic base class for shapes class Shape { public: Shape(const Point& position) : m_position {position} {} virtual ~Shape() = default; // Remember: always use virtual destructors for base classes! virtual double area() const = 0; // Pure virtual function to compute a shape's area virtual double perimeter() const = 0; // Pure virtual function to compute a shape's perimeter virtual void scale(double factor) = 0; // Pure virtual function to scale a shape // Regular virtual function to move a shape virtual void move(const Point& position) { m_position = position; }; private: Point m_position; // Position of a shape }; // Class defining a circle class Circle : public Shape { public: Circle(const Point& center, double radius) : Shape{center}, m_radius{radius} {} double area() const override { return m_radius * m_radius * std::numbers::pi; } double perimeter() const override { return 2 * std::numbers::pi * m_radius; } void scale(double factor) override { m_radius *= factor; } private: double m_radius; // Radius of a circle }; // Class defining a rectangle class Rectangle : public Shape { public: Rectangle(const Point& center, double rectWidth, double rectHeight) : Shape{center}, m_width{rectWidth}, m_height{rectHeight} {} double area() const override { return m_width * m_height; } double perimeter() const override { return 2 * (m_width + m_height); } void scale(double factor) override { m_width *= factor; m_height *= factor; } private: double m_width; // Width of a rectangle double m_height; // Height of a rectangle }; #endif ================================================ FILE: Exercises/NoModules/Chapter 15/Soln15_06/Soln15_06.cpp ================================================ // Exercise 15-6 Exercising Shape classes #include "Shapes.h" #include #include double calculateSumAreas(const std::vector& shapes); double calculateSumPerimeters(const std::vector& shapes); void printSums(const std::vector& shapes); int main() { Circle c1{ Point{1, 1}, 1 }; Circle c2{ Point{2, 2}, 2 }; Circle c3{ Point{3, 3}, 3 }; Rectangle r1{ {4, 5}, 4, 5 }; // Shorter notation (omitted Point type) could of course be used for Circles as well! Rectangle r2{ {6, 7}, 6, 7 }; Rectangle r3{ {8, 9}, 8, 9 }; std::vector shapes{ &c1, &r1, &r2, &c2, &c3, &r3 }; printSums(shapes); for (auto* shape : shapes) shape->scale(1.5); std::cout << std::endl; printSums(shapes); } double calculateSumAreas(const std::vector& shapes) { double sum {}; for (auto* shape : shapes) { sum += shape->area(); } return sum; } double calculateSumPerimeters(const std::vector& shapes) { double sum {}; for (auto* shape : shapes) { sum += shape->perimeter(); } return sum; } void printSums(const std::vector& shapes) { std::cout << "Sum of areas: " << calculateSumAreas(shapes) << std::endl; std::cout << "Sum of perimeters: " << calculateSumPerimeters(shapes) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 16/Exer16_06/Customer.cpp ================================================ // A simple C++ customer class #include "Customer.h" Customer::Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city) : m_surname{ surname } , m_name{ name } , m_street{ street } , m_streetNumber{ streetNumber } , m_city{ city } {} std::string Customer::toString() const { std::string result; result += m_surname; result += ' '; result += m_name; result += ", "; result += m_street; result += ' '; result += std::to_string(m_streetNumber); result += ", "; result += m_city; return result; } ================================================ FILE: Exercises/NoModules/Chapter 16/Exer16_06/Customer.h ================================================ // A simple C++ customer class #ifndef CUSTOMER_H #define CUSTOMER_H #include #include class Customer { public: Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city ); std::string toString() const; private: std::string m_surname; std::string m_name; std::string m_street; int m_streetNumber; std::string m_city; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 16/Exer16_06/DB.cpp ================================================ // A mocked database #include "DB.h" #include // For std::strcmp() #include #include namespace { struct QueryResult { std::vector> data; size_t index {}; }; class Database { public: Database() = default; bool hasConnection() const { return m_connected; } void connect() { m_connected = true; } void disconnect() { m_connected = false; } QueryResult* query(const char* query); private: bool m_connected{}; }; } DB_CONNECTION* db_connect() { // We only have one single database, which allows only one single connection: static Database theDatabase; if (theDatabase.hasConnection()) { return nullptr; } else { theDatabase.connect(); return &theDatabase; } } void db_disconnect(DB_CONNECTION* connection) { // reinterpret_cast<> is used to cast between pointers/references of // unrelated types (such as void* and Database*) reinterpret_cast(connection)->disconnect(); } DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query) { return reinterpret_cast(connection)->query(query); } QueryResult* Database::query(const char* query) { if (!hasConnection()) { return nullptr; } // Our database only understands one single query! if (std::strcmp(query, "SELECT * FROM CUSTOMER_TABEL") != 0) { return nullptr; } auto result{ std::make_unique() }; result->data = std::vector>{ { "Sherlock", "Holmes", "Baker Street", "221", "London" }, { "Joe", "Biden", "Pennsylvania Avenue", "1600", "Washington DC" }, { "Donald", "Duck", "Webfoot Walk", "1313", "Duckville" }, { "Sirius", "Black", "Grimmauld Place", "12", "London" }, { "Nemo", "Clownfish", "Wallaby Way", "42", "Sydney" }, { "Sam", "Malone", "Beacon Street", "112", "Boston" } }; return result.release(); } int db_num_fields(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->data.empty()) { return -1; } else { return static_cast(theResult->data.front().size()); } } DB_ROW db_fetch_row(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->index >= theResult->data.size()) { return nullptr; } else { return theResult->data[theResult->index++].data(); } } void db_free_result(DB_QUERY_RESULT* result) { delete reinterpret_cast(result); } ================================================ FILE: Exercises/NoModules/Chapter 16/Exer16_06/DB.h ================================================ // A C-style interface to a mock database // (a simplified subset of the MySQL C interface) #ifndef DB_INCLUDES #define DB_INCLUDES using DB_CONNECTION = void; using DB_QUERY_RESULT = void; using DB_ROW = const char**; /*! Make a connection to the database * Do not forget to close the database connection (db_disconnect()) once done with it. * \return a DB_CONNECTION handle; nullptr upon failure */ DB_CONNECTION* db_connect(); /*! Query the database * \param[in] connection A database connection handle returned by db_connect() * \param[in] query A SQL query * \return A handle to the result of the query (memory to be freed using db_free_result()!) */ DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query); /*! Retrieve the number of fields per row stored by the given query result * \param[in] result A handle returned by db_query * \return The number of fields per row stored by the result; -1 upon failure */ int db_num_fields(DB_QUERY_RESULT* result); /*! Fetch a single row from the result. * \param[in] result A handle returned by db_query * \return An array of strings. Each field is represented as a string (zero-terminated char array). * Let row be the result, then the first field is accessed using result[0]. */ DB_ROW db_fetch_row(DB_QUERY_RESULT* result); /*! Release the memory allocated for the result. * \param[in] result A handle returned by db_query() */ void db_free_result(DB_QUERY_RESULT* result); /*! Disconnect from the database * \param[in] connection A connection handle returned by db_connect() */ void db_disconnect(DB_CONNECTION* connection); #endif ================================================ FILE: Exercises/NoModules/Chapter 16/Exer16_06/DBException.h ================================================ // A simple C++ exception type #ifndef DB_EXCEPTION_H #define DB_EXCEPTION_H #include class DatabaseException : public std::runtime_error { public: using std::runtime_error::runtime_error; // Inherit constructor }; #endif ================================================ FILE: Exercises/NoModules/Chapter 16/Exer16_06/Exer16_06.cpp ================================================ /* Creating RAII classes to manage resource handles returned by a C interface Remember: RAII is not just for dynamic memory: every resource should be managed by an object! */ #include #include #include "DB.h" #include "DBException.h" #include "Customer.h" void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream int main() { auto* connection{ db_connect() }; try { auto* result{ db_query(connection, "SELECT * FROM CUSTOMER_TABEL") }; if (!result) { db_disconnect(connection); throw DatabaseException{"Query failed"}; } std::vector customers{ readCustomers(result) }; if (customers.empty()) { std::cerr << "No customers found?" << std::endl; return 2; } for (auto& customer : customers) { print(std::cout, customer); } db_free_result(result); } catch (std::exception& caught) { std::cerr << caught.what() << std::endl; return 1; } db_disconnect(connection); return 0; } std::vector readCustomers(DB_QUERY_RESULT* result) { // Sanity check // (if the number of fields does not match 5, // the code below would crash!) verifyCustomerFields(result); std::vector customers; auto row{ db_fetch_row(result) }; while (row) { customers.push_back(Customer{ row[0], // Surname row[1], // Name row[2], // Street std::stoi(row[3]), // Street number row[4] // City }); row = db_fetch_row(result); } return customers; } void verifyCustomerFields(DB_QUERY_RESULT* result) { const int numFields{ db_num_fields(result) }; if (numFields < 0) { throw DatabaseException{"db_num_fields() failed"}; } if (numFields != 5) { throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; } } void print(std::ostream& stream, const Customer& customer) { stream << customer.toString() << std::endl; if (std::cout.fail()) { std::cout.clear(); throw std::runtime_error("Failed to output customer"); } } ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_01/Curveball.h ================================================ // Definition of Curveball exception class #ifndef CURVEBALL_H #define CURVEBALL_H #include class Curveball : public std::exception { public: const char* what() const noexcept override { return "Curveball exception"; } }; #endif //CURVEBALL_H ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_01/Soln16_01.cpp ================================================ // Throwing and catching Curveballs #include "Curveball.h" #include #include // For random number generation #include // For std::bind() void throwCurveballSometimes(); // Suggested solution void throwCurveballSometimesBernouilli(); // Alternate solution int main() { size_t number {1000}; // Number of loop iterations size_t exceptionCount {}; // Count of exceptions thrown for (size_t i {}; i < number; ++i) { try { throwCurveballSometimes(); // throwCurveballSometimesBernouilli(); } catch (const Curveball&) { exceptionCount++; } } std::cout << "Curveball exception thrown " << exceptionCount << " times out of " << number << ".\n"; } // See Chapter 12 for an explanation of this function principle. auto createUniformPseudoRandomNumberGenerator(double min, double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ min, max }; // Generate in [min, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } // Throw a Curveball exception 25% of the time void throwCurveballSometimes() { static auto random{ createUniformPseudoRandomNumberGenerator(0, 100) }; if (random() < 25) throw Curveball{}; } /* Alternate solution: instead of generating numbers in the interval [0,100) using a std::uniform_real_distribution, and comparing against 25, you could also generate Boolean values directly using a std::bernoulli_distribution (see https://en.cppreference.com/w/cpp/numeric/random/bernoulli_distribution; named after https://en.wikipedia.org/wiki/Bernoulli_distribution): */ auto createUniformPseudoRandomBooleanGenerator(double probabilityOfTrue) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::bernoulli_distribution distribution{ probabilityOfTrue }; // The name says it all... return std::bind(distribution, generator); //... and in the darkness bind them! } // Throw a Curveball exception 25% of the time void throwCurveballSometimesBernouilli() { static auto random{ createUniformPseudoRandomBooleanGenerator(0.25) }; if (random()) throw Curveball{}; } ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_02/Curveball.h ================================================ // Definition of Curveball exception class #ifndef CURVEBALL_H #define CURVEBALL_H #include class Curveball : public std::exception { public: const char* what() const noexcept override { return "Curveball exception"; } }; #endif //CURVEBALL_H ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_02/Soln16_02.cpp ================================================ // Throwing and catching Curveballs and throwing TooManyExceptions #include "Curveball.h" #include "TooManyExceptions.h" #include #include // For random number generation #include // For std::bind() void throwCurveballSometimes(); // This program will terminate abnormally when the TooManyExceptions exception is thrown. int main() { size_t number{ 1000 }; // Number of loop iterations size_t exceptionCount {}; // Count of exceptions thrown const size_t maxExceptions{ 10 }; // Maximum number of exceptions for (size_t i {}; i < number; ++i) { try { throwCurveballSometimes(); } catch(Curveball& e) { std::cout << e.what() << std::endl; if (++exceptionCount > maxExceptions) throw TooManyExceptions{ maxExceptions }; } } } // See Soln16_01 (to generate Booleans, a bernoulli_distribution is actually most appropriate) auto createUniformPseudoRandomBooleanGenerator(double probabilityOfTrue) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::bernoulli_distribution distribution{ probabilityOfTrue }; // The name says it all... return std::bind(distribution, generator); //... and in the darkness bind them! } // Throw a Curveball exception 25% of the time void throwCurveballSometimes() { static auto random{ createUniformPseudoRandomBooleanGenerator(0.25) }; if (random()) throw Curveball{}; } ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_02/TooManyExceptions.h ================================================ // Definition of TooManyExceptions exception class // We added an extra "how many" members to illustrate derived // exceptions can carry extra information regarding their cause. // Notice how the constructor is explicit (implicit conversions // from size_t to TooManyExceptions are not desired). #ifndef TOOMANYEXCEPTIONS_H #define TOOMANYEXCEPTIONS_H #include #include // For std::string / std::to_string() class TooManyExceptions : public std::exception { public: explicit TooManyExceptions(size_t howMany) : m_how_many{ howMany } , m_message{ "Too many exceptions occurred: " + std::to_string(m_how_many) } {} const char* what() const noexcept override { return m_message.c_str(); } size_t howMany() const noexcept { return m_how_many; } private: size_t m_how_many; std::string m_message; }; #endif //TOOMANYEXCEPTIONS_H ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_03/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.m_length, box.m_width, box.m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_03/RandomBoxes.h ================================================ #ifndef RANDOM_BOXES_H #define RANDOM_BOXES_H #include "Box.h" #include // For random number generation #include // For std::bind() #include // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max inline auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } inline Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } inline auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } #endif ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_03/Soln16_03.cpp ================================================ // Using exceptions to signal index-out-of-bounds errors. #include #include #include "Truckload.h" #include "RandomBoxes.h" int main() { Truckload load; const size_t boxCount {20}; // Number of Box object to be created // Create boxCount Box objects for (size_t i {}; i < boxCount; ++i) load.addBox(randomSharedBox()); try { std::cout << "The truckload contains the following boxes: " << std::endl; for (size_t i {}; i < 100; ++i) { std::cout << *load[i] << std::endl; } } catch (const std::exception& caughtException) { std::cerr << "Oops: " << caughtException.what() << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_03/Truckload.cpp ================================================ #include "Truckload.h" #include #include // For standard exception type std::out_of_range #include // For std::string and std::to_string() // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } Truckload& Truckload::operator=(const Truckload& other) { if (&other != this) // Do not forget: avoid issues with self-assignment! { delete m_head; // Delete all current packages m_head = m_tail = nullptr; // Reset both pointers // Same as copy constructor // (see Chapter 17 for the copy-and-swap idiom that allows you to avoid // duplicating logic like this...) for (Package* package{ other.m_head }; package; package = package->m_next) { addBox(package->m_box); } } return *this; } // Destructor: clean up the list Truckload::~Truckload() { delete m_head; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox& Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } throw std::out_of_range{ "Index too large: " + std::to_string(index) }; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_03/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor Truckload& operator=(const Truckload& other); // Copy assignment operator ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; std::ostream& operator<<(std::ostream& stream, const Truckload& load); #endif ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_04/Soln16_04.cpp ================================================ // Throwing and catching standard exceptions #include #include #include #include #include #include /* This solution triggers all exceptions mentioned in the text accompanying the Table with all Standard Library exceptions, in order. To know how to trigger any other type, you will have to consult your Standard Library reference. Note: while you'll typically catch exceptions by reference-to-const-std::exception (unless more specific handling for concrete types is required, of course), we specify the concrete exception type instead here for clarity. */ // First, some dummy class types... class BaseClass { public: virtual ~BaseClass() = default; }; class DerivedClass1 : public BaseClass {}; class DerivedClass2 : public BaseClass {}; int main() { try { std::vector v{ 1, 2, 3, 4, 5 }; std::cout << v.at(10) << std::endl; } catch (const std::out_of_range& exception) { std::cout << "std::out_of_range: " << exception.what() << std::endl; } try { std::cout << std::format("Hello {:g}!\n", "World"); } catch (const std::format_error& exception) { std::cout << "std::format_error: " << exception.what() << std::endl; } try { // Remember: a polymorphic class is a class with at least one virtual function. BaseClass* polymorphic{ nullptr }; std::cout << typeid(*polymorphic).name(); } catch (const std::bad_typeid& exception) { std::cout << "std::bad_typeid: " << exception.what() << std::endl; } try { DerivedClass1 derived; BaseClass& reference_to_base{ derived }; dynamic_cast(reference_to_base); } catch (const std::bad_cast& exception) { std::cout << "std::bad_cast: " << exception.what() << std::endl; } try { std::optional empty; std::cout << empty.value() << std::endl; } catch (const std::bad_optional_access& exception) { std::cout << "std::bad_optional_access: " << exception.what() << std::endl; } try { int size{ -1 }; new int[size]; } catch (const std::bad_alloc& exception) { std::cout << "std::bad_alloc: " << exception.what() << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_05/Curveball.h ================================================ // Definition of Curveball exception class #ifndef CURVEBALL_H #define CURVEBALL_H #include class Curveball : public std::exception { public: const char* what() const noexcept override { return "Curveball exception"; } }; #endif //CURVEBALL_H ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_05/DomainExceptions.h ================================================ // Definitions of various domain exception classes #ifndef DOMAINEXCEPTIONS_H #define DOMAINEXCEPTIONS_H #include #include /* std::domain_error is one of the exception types defined by the Standard Libray. It is intended to be used mostly inside mathematical functions in case an argument is provided for which the function is not defined (for instance, should a regular square root function be called with a negative number) */ class NotANumber : public std::domain_error { public: explicit NotANumber(const std::string& nan) : std::domain_error{"Not a number: " + nan} {} }; class NegativeNumber : public std::domain_error { public: explicit NegativeNumber(int number) : std::domain_error{"A negative number was entered: " + std::to_string(number)} {} }; class OddNumber : public std::domain_error { public: explicit OddNumber(int number) : std::domain_error{"An odd number was entered: " + std::to_string(number)} {} }; #endif //DOMAINEXCEPTIONS_H ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_05/Soln16_05.cpp ================================================ // Exercise 16-5 /* Somewhat artificial example to practice lists of different, * related exception types (mind the order + catch-by-reference!) * and rethrowing (catch-by-reference + "throw;" !) */ #include #include // For random number generation #include // For std::bind() #include #include "Curveball.h" #include "DomainExceptions.h" void askEvenNumber(); // Ask the user to provide an even number int main() { try { askEvenNumber(); } catch (const Curveball& /*caught*/) { std::cerr << "...hit it out of the park!" << std::endl; } } /* Helper functions for askEvenNumber() */ void throwCurveballSometimes(); // Throw a Curveball exception 25% of the time int readEvenNumber(); // Reads an even number from std::cin and verifies the input // (throws upon failure) // Option 1: use recursion void askEvenNumber() { try { std::cout << "Please enter an even number: "; const int read = readEvenNumber(); std::cout << std::format("Well done. {} is a beautiful even number. Thank you!\n", read); } catch (const NotANumber& nan) { std::cerr << nan.what() << std::endl; return; } catch (const std::domain_error& domainException) { std::cerr << domainException.what() << std::endl; askEvenNumber(); // Recursive call } catch (const std::exception& exception) { std::cerr << exception.what() << std::endl; throw; } } /* // Option 2: use a loop void askEvenNumber() { while (true) { try { std::cout << "Please enter an even number: "; const int read = readEvenNumber(); std::cout << std::format("Well done. {} is a beautiful even number. Thank you!\n", read); break; } catch (const NotANumber& nan) { std::cerr << nan.what() << std::endl; return; } catch (const std::out_of_range& range) { std::cerr << range.what() << std::endl; } catch (const std::exception& exception) { std::cerr << exception.what() << std::endl; throw; } } } */ int readEvenNumber() { int number; std::cin >> number; if (std::cin.fail()) // Check whether the user has effectively entered a number { std::cin.clear(); // Reset the stream's failure state std::string line; // Read the erroneous input and discard it std::getline(std::cin, line); throw NotANumber{line}; } throwCurveballSometimes(); if (number < 0) throw NegativeNumber{number}; if (number % 2) throw OddNumber{number}; return number; } // See Soln16_01 for an explanation of this function auto createUniformPseudoRandomBooleanGenerator(double probabilityOfTrue) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::bernoulli_distribution distribution{ probabilityOfTrue }; // The name says it all... return std::bind(distribution, generator); //... and in the darkness bind them! } // Throw a Curveball exception 25% of the time void throwCurveballSometimes() { static auto random{ createUniformPseudoRandomBooleanGenerator(0.25) }; if (random()) throw Curveball{}; } ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_06/Customer.cpp ================================================ // A simple C++ customer class #include "Customer.h" Customer::Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city) : m_surname{ surname } , m_name{ name } , m_street{ street } , m_streetNumber{ streetNumber } , m_city{ city } {} std::string Customer::toString() const { std::string result; result += m_surname; result += ' '; result += m_name; result += ", "; result += m_street; result += ' '; result += std::to_string(m_streetNumber); result += ", "; result += m_city; return result; } ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_06/Customer.h ================================================ // A simple C++ customer class #ifndef CUSTOMER_H #define CUSTOMER_H #include #include class Customer { public: Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city ); std::string toString() const; private: std::string m_surname; std::string m_name; std::string m_street; int m_streetNumber; std::string m_city; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_06/DB.cpp ================================================ // A mocked database #include "DB.h" #include // For std::strcmp() #include #include namespace { struct QueryResult { std::vector> data; size_t index {}; }; class Database { public: Database() = default; bool hasConnection() const { return m_connected; } void connect() { m_connected = true; } void disconnect() { m_connected = false; } QueryResult* query(const char* query); private: bool m_connected{}; }; } DB_CONNECTION* db_connect() { // We only have one single database, which allows only one single connection: static Database theDatabase; if (theDatabase.hasConnection()) { return nullptr; } else { theDatabase.connect(); return &theDatabase; } } void db_disconnect(DB_CONNECTION* connection) { // reinterpret_cast<> is used to cast between pointers/references of // unrelated types (such as void* and Database*) reinterpret_cast(connection)->disconnect(); } DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query) { return reinterpret_cast(connection)->query(query); } QueryResult* Database::query(const char* query) { if (!hasConnection()) { return nullptr; } // Our database only understands one single query! if (std::strcmp(query, "SELECT * FROM CUSTOMER_TABEL") != 0) { return nullptr; } auto result{ std::make_unique() }; result->data = std::vector>{ { "Sherlock", "Holmes", "Baker Street", "221", "London" }, { "Joe", "Biden", "Pennsylvania Avenue", "1600", "Washington DC" }, { "Donald", "Duck", "Webfoot Walk", "1313", "Duckville" }, { "Sirius", "Black", "Grimmauld Place", "12", "London" }, { "Nemo", "Clownfish", "Wallaby Way", "42", "Sydney" }, { "Sam", "Malone", "Beacon Street", "112", "Boston" } }; return result.release(); } int db_num_fields(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->data.empty()) { return -1; } else { return static_cast(theResult->data.front().size()); } } DB_ROW db_fetch_row(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->index >= theResult->data.size()) { return nullptr; } else { return theResult->data[theResult->index++].data(); } } void db_free_result(DB_QUERY_RESULT* result) { delete reinterpret_cast(result); } ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_06/DB.h ================================================ // A C-style interface to a mock database // (a simplified subset of the MySQL C interface) #ifndef DB_INCLUDES #define DB_INCLUDES using DB_CONNECTION = void; using DB_QUERY_RESULT = void; using DB_ROW = const char**; /*! Make a connection to the database * Do not forget to close the database connection (db_disconnect()) once done with it. * \return a DB_CONNECTION handle; nullptr upon failure */ DB_CONNECTION* db_connect(); /*! Query the database * \param[in] connection A database connection handle returned by db_connect() * \param[in] query A SQL query * \return A handle to the result of the query (memory to be freed using db_free_result()!) */ DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query); /*! Retrieve the number of fields per row stored by the given query result * \param[in] result A handle returned by db_query * \return The number of fields per row stored by the result; -1 upon failure */ int db_num_fields(DB_QUERY_RESULT* result); /*! Fetch a single row from the result. * \param[in] result A handle returned by db_query * \return An array of strings. Each field is represented as a string (zero-terminated char array). * Let row be the result, then the first field is accessed using result[0]. */ DB_ROW db_fetch_row(DB_QUERY_RESULT* result); /*! Release the memory allocated for the result. * \param[in] result A handle returned by db_query() */ void db_free_result(DB_QUERY_RESULT* result); /*! Disconnect from the database * \param[in] connection A connection handle returned by db_connect() */ void db_disconnect(DB_CONNECTION* connection); #endif ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_06/DBException.h ================================================ // A simple C++ exception type #ifndef DB_EXCEPTION_H #define DB_EXCEPTION_H #include class DatabaseException : public std::runtime_error { public: using std::runtime_error::runtime_error; // Inherit constructor }; #endif ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_06/DB_RAII.h ================================================ // RAII classes for handles returned by DB.h C interface functions /* In this solution we have the RAII classes accept the resource handle in their constructor. Alternatively, you could also acquire the resources inside the constructors of the RAII class. For instance: you could call db_connect() from within the DBConnectionRAII() contructor. */ #ifndef DB_RAII_H #define DB_RAII_H #include "DB.h" /** * RAII object that ensures that a given database connection is closed * once the RAII object goes out of scope. */ class DBConnectionRAII { public: DBConnectionRAII(DB_CONNECTION* connection) noexcept : m_connection{ connection } { } ~DBConnectionRAII() // implicitly noexcept { if (m_connection) { db_disconnect(m_connection); } } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_CONNECTION*() const noexcept { return m_connection; } private: DB_CONNECTION* m_connection; }; /* RAII object that takes a DB_QUERY_RESULT and ensures it is closed */ class DBQueryResultRAII { public: DBQueryResultRAII(DB_QUERY_RESULT* result) noexcept : m_result{ result } { } ~DBQueryResultRAII() // implicitly noexcept { if (m_result) { db_free_result(m_result); } } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_QUERY_RESULT* () const noexcept { return m_result; } private: DB_QUERY_RESULT* m_result; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 16/Soln16_06/Soln16_06.cpp ================================================ // Exercise 16-6 /* Creating RAII classes to manage resource handles returned by a C interface Remember: RAII is not just for dynamic memory: every resource should be managed by an object! Leaks in the original code: - The database connection was not disconnected if an exception occurred indirectly in the main try-catch block. For the DatabaseException thrown in the block of db_query() the connection was correctly disconnected. Possible other exceptions, though, include: a) verifyCustomerFields() discovers a problem. This verification step may've been added later by someone trying to make the program more robuust, but not familiar enough with the surrounding program... b) std::stoi() throws std::invalid_argument because an empty string was passed, or a string that does not start with a number c) Customer::toString() throws std::bad_alloc because memory allocation failed for the new string d) If the output stream used fails for whatever reason, a std::runtime_exception occurs. While for std::cout this is less likely, perhaps the program is changed later to output to some other stream that writes to a GUI element or a file. These might fail... - The memory leaks for the query result are analogous - If no customers are found, someone decided to add an extra return statement. This again leaks all resources... Bottom line: the larger the program becomes, the more resources there are to keep track of, and at the same time the more exceptions and return statements there appear. Moreover, in real life often many different people collaborate on the same code, often less familiar with the original program and the resources it uses. It is just too easy to forget one case, and introduce a leak. Even the most disciplined developer will make mistakes this way---believe us! Hence: always use some form of RAII to manage a resource! */ #include #include #include "DB.h" #include "DBException.h" #include "Customer.h" #include "DB_RAII.h" void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream int main() { DBConnectionRAII connection{ db_connect() }; try { DBQueryResultRAII result{ db_query(connection, "SELECT * FROM CUSTOMER_TABEL") }; if (!result) { throw DatabaseException{"Query failed"}; } std::vector customers{ readCustomers(result) }; if (customers.empty()) { std::cerr << "No customers found?" << std::endl; return 2; } for (auto& customer : customers) { print(std::cout, customer); } } catch (const std::exception& caught) { std::cerr << caught.what() << std::endl; return 1; } } std::vector readCustomers(DB_QUERY_RESULT* result) { // Sanity check // (if the number of fields does not match 5, // the code below would crash!) verifyCustomerFields(result); std::vector customers; auto row{ db_fetch_row(result) }; while (row) { customers.push_back(Customer{ row[0], // Surname row[1], // Name row[2], // Street std::stoi(row[3]), // Street number row[4] // City }); row = db_fetch_row(result); } return customers; } void verifyCustomerFields(DB_QUERY_RESULT* result) { const int numFields{ db_num_fields(result) }; if (numFields < 0) { throw DatabaseException{"db_num_fields() failed"}; } if (numFields != 5) { throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; } } void print(std::ostream& stream, const Customer& customer) { stream << customer.toString() << std::endl; if (std::cout.fail()) { std::cout.clear(); throw std::runtime_error("Failed to output customer"); } } ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_01/Array.h ================================================ // Same as Ex17_01B, but with a push_back() member and a default constructor #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For std::to_string() #include // For std::as_const() template class Array { public: Array(); // Default constructor Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array& operator=(const Array& rhs); // Copy assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays void push_back(const T& element); // Function to add new element to the end size_t getSize() const { return m_size; } // Accessor for m_size private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Template for functions that add new element to the end of the array // Uses the 'copy-and-swap' idiom. // If either the Array<> constructor throws // (std::bad_alloc or some exception from T's default constructor) // or any of the copy assignments of a T element throw, // then the original Array<> remains untouched. template void Array::push_back(const T& newElement) { Array copy{ m_size + 1 }; // Copy... for (size_t i {}; i < m_size; ++i) copy[i] = m_elements[i]; copy[m_size] = newElement; // ... modify ... swap(copy); // ... and swap! (noexcept) } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } #endif ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_01/Soln17_01.cpp ================================================ // Adding a push_back() member function and a default constructor to the Array<> // class template. Using copy-and-swap for a memory-safe push_back(). #include "Array.h" #include int main() { const unsigned numElements{ 100 }; Array squares; // default construction for (unsigned i {}; i < numElements; ++i) squares.push_back(i * i); // push_back() std::cout << squares.getSize() << " squares were added." << std::endl; std::cout << "For instance: 13 squared equals " << squares[13] << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_02/Pair.h ================================================ #ifndef PAIR_H #define PAIR_H #include template class Pair { public: // Public members + no m_ prefix analogous to std::pair<> (see module) First first; Second second; Pair() = default; Pair(const First& f, const Second& s); // In retrospect, this is no longer such an interesting exercise on writing // member function templates now that the compiler generates all lexicographical // comparison operators for you. // // Note: not all compiler may fully support this generation yet (C++20)... // (replacing auto with std::strong_ordering may make Soln17_02.cpp compile then, // but in general auto is better because First and/or Second could be types // that are not strongly ordered, such as a floating-point types) auto operator<=>(const Pair& other) const = default; }; // Constructor template Pair::Pair(const First& f, const Second& s) : first{f}, second{s} {} #endif ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_02/Soln17_02.cpp ================================================ // Exercise 16-2 // Create a basic Pair template #include "Pair.h" #include #include int main() { auto my_pair{ Pair{122, "abc"} }; ++my_pair.first; std::cout << "my_pair equals (" << my_pair.first << ", " << my_pair.second << ')' << std::endl; auto pair1{ Pair{0, "def"} }; using namespace std::string_literals; // To make s suffix work (see below) // CTAD works as well. The deduced type for both pair2 and pair3 is Pair: auto pair2{ Pair{123, std::string{"abc"}} }; // Option 1: specify std::string yourself (otherwise the type is const char[]) auto pair3{ Pair{123, "def"s} }; // Option 2: use string literals: s suffix creates a std::string object std::cout << (pair1 < pair2 && pair2 < pair3? "operator< seems to be working" : "oops") << std::endl; std::cout << (pair1 == pair2? "oops" : "operator== works as well") << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_03/Pair.h ================================================ #ifndef PAIR_H #define PAIR_H #include #include template class Pair { public: // Public members + no m_ prefix analogous to std::pair<> (see module) First first; Second second; Pair() = default; Pair(const First& f, const Second& s); // Note: not all compiler may fully support this generation yet (C++20)... // (replacing auto with std::strong_ordering may make Soln17_02.cpp compile then, // but in general auto is better because First and/or Second could be types // that are not strongly ordered, such as a floating-point types) auto operator<=>(const Pair& other) const = default; }; // Constructor template Pair::Pair(const First& f, const Second& s) : first{f}, second{s} {} template std::ostream& operator<<(std::ostream& out, const Pair& pair) { return out << '(' << pair.first << ", " << pair.second << ')'; } #endif ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_03/Soln17_03.cpp ================================================ // Create a << operator template for Pairs #include "Pair.h" #include #include int main() { // To make the s string literal suffix work // (facilitates creation of Pairs using CTAD, or Constructor Template Argument Deduction). // This syntactic sugar for creating std::string objects is not really covered in the book. using namespace std::string_literals; auto my_pair{ Pair{122, "abc"s} }; ++my_pair.first; std::cout << "my_pair equals " << my_pair << std::endl; auto pair1{ Pair{ 0, "def"s} }; auto pair2{ Pair{123, "abc"s} }; auto pair3{ Pair{123, "def"s} }; std::cout << (pair1 < pair2 && pair2 < pair3? "operator< seems to be working" : "oops") << std::endl; std::cout << (pair1 == pair2? "oops" : "operator== works as well") << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_04/Pair.h ================================================ #ifndef PAIR_H #define PAIR_H #include #include template class Pair { public: // Public members + no m_ prefix analogous to std::pair<> (see module) First first; Second second; Pair() = default; Pair(const First& f, const Second& s); // Note: not all compiler may fully support this generation yet (C++20)... // (replacing auto with std::strong_ordering may make Soln17_02.cpp compile then, // but in general auto is better because First and/or Second could be types // that are not strongly ordered, such as a floating-point types) auto operator<=>(const Pair& other) const = default; }; // Constructor template Pair::Pair(const First& f, const Second& s) : first{f}, second{s} {} template std::ostream& operator<<(std::ostream& out, const Pair& pair) { return out << '(' << pair.first << ", " << pair.second << ')'; } #endif ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_04/Soln17_04.cpp ================================================ // Exercising the SparseArray class template // We create a sparse array of integers, populate 20 of its entries // (checking for duplicates among the randomly generated indices) // and output the resulting index/value pairs. #include "SparseArray.h" #include #include // For random number generation #include // For std::bind() #include // For std::make_shared<>() and std::shared_ptr<> // See Chapter 12 for an explanation of this principle. // The main difference here is that we need a std::uniform_int_distribution // instead of a std::uniform_real_distribution to generate integers instead // of float-point (or, "real") numbers. auto createUniformPseudoRandomNumberGenerator(int min, int max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t count {20}; // Number of elements to be created const int min_value{32}; const int max_value{212}; const size_t max_index{499}; // Create the (pseudo)-random number generators // (we use +1 because these generate integers in a half-open interval [min,max)...) auto generate_random_index{ createUniformPseudoRandomNumberGenerator(0, max_index + 1) }; auto generate_random_value{ createUniformPseudoRandomNumberGenerator(min_value, max_value + 1) }; SparseArray numbers; // Create empty sparse array for (size_t i {}; i < count; ++i) // Create count entries in numbers array { size_t index {}; // Stores new index value // Must ensure that indexes after the first are not duplicates do { index = generate_random_index(); // Get a random index 0 to max_index-1 } while (numbers.element_exists_at(index)); numbers[index] = generate_random_value(); // Store value at new index position } for (size_t i {}; i <= max_index; ++i) // Create count entries in numbers array { if (numbers.element_exists_at(i)) std::cout << "Element at index " << i << " equals " << numbers.at(i) << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_04/SparseArray.h ================================================ // SparseArray class template definition // Note the use of the find() helper function and the const-and-back-again idiom to minimize code duplication #ifndef SPARSEARRAY_H #define SPARSEARRAY_H #include "Pair.h" #include #include // For std::to_string() #include // For std::as_const() #include template class SparseArray { public: T& operator[](size_t index); // Subscript operator (creates default-constructed value if no value exists for the given index) T& at(size_t index); // Access function (throws std::out_of_range if no value exists for the given index) const T& at(size_t index) const; // const overload of at() bool element_exists_at(size_t index) const; // Return true iff an element exists at the given index private: T* find(size_t index); // Helper function (returns nullptr if no value exists for the given index) const T* find(size_t index) const; std::vector> m_values; }; template T& SparseArray::operator[](size_t index) { if (auto* found{ find(index) }; found) // Using C++17 initialization statements for if statement { // (see at() function for common, traditional equivalent) return *found; } else { m_values.push_back({ index, T{} }); // Or push_back(Pair{...}), or push_back(Pair{...}) return m_values.back().second; // Remember: std::vector<>::back() returns a reference to its last element } } template const T& SparseArray::at(size_t index) const { const auto* found{ find(index) }; if (found) { return *found; } else { throw std::out_of_range{"No value exists at index " + std::to_string(index)}; } } template T& SparseArray::at(size_t index) { return const_cast(std::as_const(*this).at(index)); } template bool SparseArray::element_exists_at(size_t index) const { return find(index) != nullptr; } template const T* SparseArray::find(size_t index) const { for (auto& pair : m_values) { if (pair.first == index) return &pair.second; } return nullptr; } template T* SparseArray::find(size_t index) { return const_cast(std::as_const(*this).find(index)); } #endif //SPARSEARRAY_H ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_05/LinkedList.h ================================================ #ifndef LINKEDLIST_H #define LINKEDLIST_H /* This LinkedList template follows some (not all) conventions of Standard Library containers: some member function names are analogous, as is the choice not to work with exceptions in case you do something wrong (such as calling back() or pop_front() on an empty list). The latter has the implication that if you mis-use the container, your program will likely crash... */ #include // for the size_t typedef #include // for std::swap() template class LinkedList { public: LinkedList() = default; // Default constructor (all pointers are initialised to nullptr) ~LinkedList(); LinkedList(const LinkedList& list); // Copy constructor LinkedList& operator=(LinkedList list); // Assignment operator void push_front(const T& value); // Add an object to the head void push_back(const T& value); // Add an object to the tail void pop_back(); // Removes the last element from the list (undefined behavior for empty lists) void pop_front(); // Removes the first element from the list (undefined behavior for empty lists) T& front(); // Get the object at the head (undefined behavior for empty lists) T& back(); // Get the object at the tail (undefined behavior for empty lists) const T& front() const; // Get the object at the head (undefined behavior for empty lists) const T& back() const; // Get the object at the tail (undefined behavior for empty lists) bool empty() const; // Checks whether the list is empty or not void clear(); // Function to remove all elements from the list size_t size() const; // Get the number of elements from the list class Iterator; // Nested Iterator class declaration (definition below) Iterator front_iterator() const; // Get an Iterator that starts at the head Iterator back_iterator() const; // Get an Iterator that starts at the tail void swap(LinkedList& other) noexcept; // Swap function private: class Node // Node class definition { public: Node(const T& theValue) : m_value{ theValue }, m_next{}, m_previous{} {} T m_value; Node* m_next; Node* m_previous; }; Node* m_head{}; // Pointer to first node Node* m_tail{}; // Pointer to last node size_t m_size{}; }; // Non-member swap function. // Convention dictates swap is present as non-member function. // For class types it is often easiest (and recommended) // to implement it using a member swap function. template void swap(LinkedList& one, LinkedList& other) { one.swap(other); } // --------------------------------------------- // Member definitions // --------------------------------------------- // Destructor template template LinkedList::~LinkedList() { clear(); } // Copy constructor template template LinkedList::LinkedList(const LinkedList& list) { // Use existing members (iteration, push_back()) to implement the copying // This avoids duplicating any code that manipulates the list's pointers / size members. for (auto iterator{ list.front_iterator() }; iterator; iterator.next()) push_back(iterator.value()); } // Assignment operator template (uses copy-and-swap idiom) template LinkedList& LinkedList::operator=(LinkedList copy) { swap(*this, copy); return *this; } // Template for member functions that add an object to the head of the list template void LinkedList::push_front(const T& value) { // Note: this function is strongly exception-safe because // any and all change occur after the line annotated with (*), // which is the only line that may throw. Node* oldHead{ m_head }; m_head = new Node{ value }; // (*) ++m_size; if (oldHead) { oldHead->m_previous = m_head; m_head->m_next = oldHead; } else // list was empty before, and now has one element { m_tail = m_head; } } // Template for member functions that add an object to the tail of the list template void LinkedList::push_back(const T& value) { // Note: this function is strongly exception-safe because // any and all change occur after the line annotated with (*), // which is the only line that may throw. Node* oldTail{ m_tail }; m_tail = new Node{value}; // (*) ++m_size; if (oldTail) { oldTail->m_next = m_tail; m_tail->m_previous = oldTail; } else // list was empty before, and now has one element { m_head = m_tail; } } // Template for member functions that remove an object from the head of the list template void LinkedList::pop_front() { Node* oldHead{ m_head }; if (oldHead == m_tail) { m_head = m_tail = nullptr; } else { m_head = oldHead->m_next; m_head->m_previous = nullptr; } --m_size; delete oldHead; } // Template function member to remove an object from the tail of the list template void LinkedList::pop_back() { Node* oldTail{ m_tail }; if (oldTail == m_head) { m_head = m_tail = nullptr; } else { m_tail = oldTail->m_previous; m_tail->next = nullptr; } --m_size; delete oldTail; } // Template function members to get the object at the head of the list template T& LinkedList::front() { return m_head->value; } template const T& LinkedList::front() const { return m_head->value; } // Template function members to get the object at the tail of the list template T& LinkedList::back() { return m_tail->value; } template const T& LinkedList::back() const { return m_tail->value; } // Check whether list is empty or not template bool LinkedList::empty() const { return m_size == 0; // (or m_head == nullptr, or m_tail == nullptr) } // Template to get the size of a list template size_t LinkedList::size() const { return m_size; } // Template to clear a list template void LinkedList::clear() { // Use existing functions (avoid code duplication!) while (!empty()) pop_front(); } // Member function template to swap two lists template void LinkedList::swap(LinkedList& other) noexcept { std::swap(m_head, other.m_head); std::swap(m_tail, other.m_tail); std::swap(m_size, other.m_size); } // Definition of the nested Iterator class template class LinkedList::Iterator { public: explicit Iterator(Node* node) : m_current{ node } {} const T& value() const { return m_current->m_value; } bool hasValue() const { return m_current != nullptr; } operator bool() const { return m_current != nullptr; } void next() { m_current = m_current->m_next; } void previous() { m_current = m_current->m_previous; } private: Node* m_current; }; // Get an Iterator that starts at the head template typename LinkedList::Iterator LinkedList::front_iterator() const { return Iterator{ m_head }; } // Get an Iterator that starts at the tail template typename LinkedList::Iterator LinkedList::back_iterator() const { return Iterator{ m_tail }; } #endif //LINKEDLIST_H ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_05/Soln17_05.cpp ================================================ // Exercise 17-5 Exercising the LinkedList template class // This program reverses the text that is entered #include "LinkedList.h" #include #include #include int main() { std::string text; // Stores input prose or poem std::cout << "\nEnter a poem or prose over one or more lines.\n" << "Terminate the input with #:\n"; getline(std::cin, text, '#'); LinkedList words; // List to store words // Extract words and store in the list std::string_view separators{ " ,.\"?!;:\n" }; // Separators between words size_t start {}; // Start of a word size_t end {}; // separator position after a word while (std::string::npos != (start = text.find_first_not_of(separators, start))) { end = text.find_first_of(separators, start+1); words.push_back(text.substr(start,end-start)); start = end; } // List the words 5 to a line std::cout << "\nThe words are:\n\n"; auto iterator{ words.front_iterator() }; size_t count {}; // Word counter const size_t perline {5}; // Worde per line while (iterator.hasValue()) { std::cout << iterator.value() << ' '; if (!(++count % perline)) std::cout << std::endl; iterator.next(); } std::cout << std::endl; // List the words in reverse order 5 to a line std::cout << "\nIn reverse order, the words are:\n\n"; iterator = words.back_iterator(); count = 0; while (iterator.hasValue()) { std::cout << iterator.value() << ' '; if(!(++count % perline)) std::cout << std::endl; iterator.previous(); } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_06/LinkedList.h ================================================ #ifndef LINKEDLIST_H #define LINKEDLIST_H /* This LinkedList template follows some (not all) conventions of Standard Library containers: some member function names are analogous, as is the choice not to work with exceptions in case you do something wrong (such as calling back() or pop_front() on an empty list). The latter has the implication that if you mis-use the container, your program will likely crash... */ #include // for the size_t typedef #include // for std::swap() template class LinkedList { public: LinkedList() = default; // Default constructor (all pointers are initialised to nullptr) ~LinkedList(); LinkedList(const LinkedList& list); // Copy constructor LinkedList& operator=(LinkedList list); // Assignment operator void push_front(const T& value); // Add an object to the head void push_back(const T& value); // Add an object to the tail void pop_back(); // Removes the last element from the list (undefined behavior for empty lists) void pop_front(); // Removes the first element from the list (undefined behavior for empty lists) T& front(); // Get the object at the head (undefined behavior for empty lists) T& back(); // Get the object at the tail (undefined behavior for empty lists) const T& front() const; // Get the object at the head (undefined behavior for empty lists) const T& back() const; // Get the object at the tail (undefined behavior for empty lists) bool empty() const; // Checks whether the list is empty or not void clear(); // Function to remove all elements from the list size_t size() const; // Get the number of elements from the list class Iterator; // Nested Iterator class declaration (definition below) Iterator front_iterator() const; // Get an Iterator that starts at the head Iterator back_iterator() const; // Get an Iterator that starts at the tail void swap(LinkedList& other) noexcept; // Swap function private: class Node // Node class definition { public: Node(const T& theValue) : m_value{ theValue }, m_next{}, m_previous{} {} T m_value; Node* m_next; Node* m_previous; }; Node* m_head{}; // Pointer to first node Node* m_tail{}; // Pointer to last node size_t m_size{}; }; // Non-member swap function. // Convention dictates swap is present as non-member function. // For class types it is often easiest (and recommended) // to implement it using a member swap function. template void swap(LinkedList& one, LinkedList& other) { one.swap(other); } // --------------------------------------------- // Member definitions // --------------------------------------------- // Destructor template template LinkedList::~LinkedList() { clear(); } // Copy constructor template template LinkedList::LinkedList(const LinkedList& list) { // Use existing members (iteration, push_back()) to implement the copying // This avoids duplicating any code that manipulates the list's pointers / size members. for (auto iterator{ list.front_iterator() }; iterator; iterator.next()) push_back(iterator.value()); } // Assignment operator template (uses copy-and-swap idiom) template LinkedList& LinkedList::operator=(LinkedList copy) { swap(*this, copy); return *this; } // Template for member functions that add an object to the head of the list template void LinkedList::push_front(const T& value) { // Note: this function is strongly exception-safe because // any and all change occur after the line annotated with (*), // which is the only line that may throw. Node* oldHead{ m_head }; m_head = new Node{ value }; // (*) ++m_size; if (oldHead) { oldHead->m_previous = m_head; m_head->m_next = oldHead; } else // list was empty before, and now has one element { m_tail = m_head; } } // Template for member functions that add an object to the tail of the list template void LinkedList::push_back(const T& value) { // Note: this function is strongly exception-safe because // any and all change occur after the line annotated with (*), // which is the only line that may throw. Node* oldTail{ m_tail }; m_tail = new Node{value}; // (*) ++m_size; if (oldTail) { oldTail->m_next = m_tail; m_tail->m_previous = oldTail; } else // list was empty before, and now has one element { m_head = m_tail; } } // Template for member functions that remove an object from the head of the list template void LinkedList::pop_front() { Node* oldHead{ m_head }; if (oldHead == m_tail) { m_head = m_tail = nullptr; } else { m_head = oldHead->m_next; m_head->m_previous = nullptr; } --m_size; delete oldHead; } // Template function member to remove an object from the tail of the list template void LinkedList::pop_back() { Node* oldTail{ m_tail }; if (oldTail == m_head) { m_head = m_tail = nullptr; } else { m_tail = oldTail->m_previous; m_tail->next = nullptr; } --m_size; delete oldTail; } // Template function members to get the object at the head of the list template T& LinkedList::front() { return m_head->value; } template const T& LinkedList::front() const { return m_head->value; } // Template function members to get the object at the tail of the list template T& LinkedList::back() { return m_tail->value; } template const T& LinkedList::back() const { return m_tail->value; } // Check whether list is empty or not template bool LinkedList::empty() const { return m_size == 0; // (or m_head == nullptr, or m_tail == nullptr) } // Template to get the size of a list template size_t LinkedList::size() const { return m_size; } // Template to clear a list template void LinkedList::clear() { // Use existing functions (avoid code duplication!) while (!empty()) pop_front(); } // Member function template to swap two lists template void LinkedList::swap(LinkedList& other) noexcept { std::swap(m_head, other.m_head); std::swap(m_tail, other.m_tail); std::swap(m_size, other.m_size); } // Definition of the nested Iterator class template class LinkedList::Iterator { public: explicit Iterator(Node* node) : m_current{ node } {} const T& value() const { return m_current->m_value; } bool hasValue() const { return m_current != nullptr; } operator bool() const { return m_current != nullptr; } void next() { m_current = m_current->m_next; } void previous() { m_current = m_current->m_previous; } private: Node* m_current; }; // Get an Iterator that starts at the head template typename LinkedList::Iterator LinkedList::front_iterator() const { return Iterator{ m_head }; } // Get an Iterator that starts at the tail template typename LinkedList::Iterator LinkedList::back_iterator() const { return Iterator{ m_tail }; } #endif //LINKEDLIST_H ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_06/Pair.h ================================================ #ifndef PAIR_H #define PAIR_H #include #include template class Pair { public: // Public members + no m_ prefix analogous to std::pair<> (see module) First first; Second second; Pair() = default; Pair(const First& f, const Second& s); // Note: not all compiler may fully support this generation yet (C++20)... // (replacing auto with std::strong_ordering may make Soln17_02.cpp compile then, // but in general auto is better because First and/or Second could be types // that are not strongly ordered, such as a floating-point types) auto operator<=>(const Pair& other) const = default; }; // Constructor template Pair::Pair(const First& f, const Second& s) : first{f}, second{s} {} template std::ostream& operator<<(std::ostream& out, const Pair& pair) { return out << '(' << pair.first << ", " << pair.second << ')'; } #endif ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_06/Soln17_06.cpp ================================================ // Exercising the SparseArray class template in combination with the LinkedList class template #include "SparseArray.h" #include "LinkedList.h" #include #include #include #include #include int main() { std::string text; // Stores input prose or poem std::cout << "Enter a poem or prose over one or more lines.\n" << "Terminate the input with #:\n"; getline(std::cin, text, '#'); SparseArray> lists; // Sparse array of linked lists const std::string_view letters {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"}; // Extract words and store in the appropriate list // A list in the SparseArray is selected by the index in letters of the first letter in a word. const std::string_view separators {" \n\t,.\"?!;:"}; // Separators between words size_t start {}; // Start of a word size_t end {}; // separator position after a word while (std::string::npos != (start = text.find_first_not_of(separators, start))) { end = text.find_first_of(separators, start+1); const auto word{ text.substr(start, end - start) }; const auto letter{ static_cast(std::toupper(word[0])) }; lists[letters.find(letter)].push_back(word); start = end; } // List the words in order 5 to a line const size_t perline {5}; for (size_t i {}; i < std::size(letters); ++i) { if (!lists.element_exists_at(i)) continue; size_t count {}; // Word counter for (auto iterator { lists[i].front_iterator() }; iterator; iterator.next()) { std::cout << iterator.value() << ' '; if (!(++count % perline)) std::cout << std::endl; } std::cout << std::endl; } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_06/SparseArray.h ================================================ // SparseArray class template definition // Note the use of the find() helper function and the const-and-back-again idiom to minimize code duplication #ifndef SPARSEARRAY_H #define SPARSEARRAY_H #include "Pair.h" #include #include // For std::to_string() #include // For std::as_const() #include template class SparseArray { public: T& operator[](size_t index); // Subscript operator (creates default-constructed value if no value exists for the given index) T& at(size_t index); // Access function (throws std::out_of_range if no value exists for the given index) const T& at(size_t index) const; // const overload of at() bool element_exists_at(size_t index) const; // Return true iff an element exists at the given index private: T* find(size_t index); // Helper function (returns nullptr if no value exists for the given index) const T* find(size_t index) const; std::vector> m_values; }; template T& SparseArray::operator[](size_t index) { if (auto* found{ find(index) }; found) // Using C++17 initialization statements for if statement { // (see at() function for common, traditional equivalent) return *found; } else { m_values.push_back({ index, T{} }); // Or push_back(Pair{...}), or push_back(Pair{...}) return m_values.back().second; // Remember: std::vector<>::back() returns a reference to its last element } } template const T& SparseArray::at(size_t index) const { const auto* found{ find(index) }; if (found) { return *found; } else { throw std::out_of_range{"No value exists at index " + std::to_string(index)}; } } template T& SparseArray::at(size_t index) { return const_cast(std::as_const(*this).at(index)); } template bool SparseArray::element_exists_at(size_t index) const { return find(index) != nullptr; } template const T* SparseArray::find(size_t index) const { for (auto& pair : m_values) { if (pair.first == index) return &pair.second; } return nullptr; } template T* SparseArray::find(size_t index) { return const_cast(std::as_const(*this).find(index)); } #endif //SPARSEARRAY_H ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_07/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_07/BoxFormatter.h ================================================ #ifndef BOX_FORMATTER_H #define BOX_FORMATTER_H #include "Box.h" #include // Adding specific specializations to the std namespace is allowed template <> class std::formatter : public std::formatter { public: /* This was no easy exercise. Fixes required compared to the outline in the exercise: - the member is called advance_to() and not advance() (we actually used advance() by mistake in the book, but hey, it did force you to practice consulting a Standard Library reference?) - you need to specify std::formatter to access the base class format() function - of course you still needed to add "Box(., ., .)" as well. One way to do this is repeatedly call std::format_to(), a function similar to std::format() except that it outputs not to a stream but an output iterator. You can also manipulate the iterator directly, as we show in for outputting the second ", " and the ")". (Iterator manipulation is explained in more detail in Chapter 20...) */ auto format(const Box& box, auto& context) { auto iter{ std::format_to(context.out(), "Box(") }; context.advance_to(iter); iter = std::formatter::format(box.getLength(), context); iter = std::format_to(iter, ", "); context.advance_to(iter); iter = std::formatter::format(box.getWidth(), context); *iter++ = ','; *iter++ = ' '; context.advance_to(iter); std::formatter::format(box.getHeight(), context); *iter++ = ')'; context.advance_to(iter); return iter; } }; #endif ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_07/Soln17_07.cpp ================================================ // Implementing a custom std::formatter<> specialization to format Box objects #include "Box.h" #include "BoxFormatter.h" #include int main() { Box box{ 1, 2, 3 }; std::cout << std::format("My new box, {:.2}, is fabulous!", box); } ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_07A/Box.h ================================================ #ifndef BOX_H #define BOX_H class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } double getLength() const { return m_length; } double getWidth() const { return m_width; } double getHeight() const { return m_height; } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_07A/BoxFormatter.h ================================================ #ifndef BOX_FORMATTER_H #define BOX_FORMATTER_H #include "Box.h" #include #include // Adding specific specializations to the std namespace is allowed template <> class std::formatter { public: auto parse(auto& context) { // [context.begin(), context.end()) is a character range that contains a part of // the format string starting from the format specifications to be parsed, // e.g. in // // std::format("My new box, {:.2}, is fabulous!", box) // // the range will contain ".2}, is fabulous!". The formatter should // parse specifiers until '}' or the end of the range. // // Our goal for this same example is to store "Box({:.2}, {:.2}, {:.2})" in m_format. // We first find the range where for instance ".2" is present, // and then inject that three times into a format string of the correct form. auto iter{ context.begin() }; if (iter == context.end()) // May happen for empty {} format specifiers { m_format = "Box({}, {}, {})"; return iter; } // Search for the closing '}' while (iter != context.end() && *iter != '}') ++iter; if (*iter != '}') // If not found, fail { throw std::format_error{ "missing closing braces, }" }; } // Main trick in this format expression is that to get { or } // in the output you have to write {{ or }}. // Otherwise std::format() will see these characters as the begin or end of a replacement field. m_format = std::format("Box({{:{0}}}, {{:{0}}}, {{:{0}}})", std::string(context.begin(), iter)); return iter; } auto format(const Box& box, auto& context) { return std::format_to( context.out(), m_format, box.getLength(), box.getWidth(), box.getHeight() ); } private: std::string m_format; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 17/Soln17_07A/Soln17_07A.cpp ================================================ // Implementing a custom std::formatter<> specialization to format Box objects // (Alternate solution) #include "Box.h" #include "BoxFormatter.h" #include int main() { Box box{ 1, 2, 3 }; std::cout << std::format("My new box, {:.2}, is fabulous!", box); } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_01/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.m_length, box.m_width, box.m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_01/RandomBoxes.h ================================================ #ifndef RANDOM_BOXES_H #define RANDOM_BOXES_H #include "Box.h" #include // For random number generation #include // For std::bind() #include // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max inline auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } inline Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } inline auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_01/Soln18_01.cpp ================================================ // Exercise 18-1 Define move operators for the Truckload class #include #include #include "Truckload.h" #include "RandomBoxes.h" /* Things to watch out for: - use of copy-and-swap / move-and-swap function - noexcept move and swap members - dislodge the linked list from the moved load in the move constructor */ int main() { const double dimLimit {99.0}; // Upper limit on Box dimensions Truckload load; const size_t boxCount {20}; // Number of Box object to be created // Create boxCount Box objects for (size_t i {}; i < boxCount; ++i) load.addBox(randomSharedBox()); std::cout << "The boxes in the Truckload are:\n"; std::cout << load << std::endl; Truckload moveConstructedLoad{ std::move(load) }; std::cout << "The boxes in the move constructed Truckload are:\n"; std::cout << moveConstructedLoad << std::endl; Truckload moveAssignedLoad; moveAssignedLoad = std::move(moveConstructedLoad); std::cout << "The boxes in the move assigned Truckload are:\n"; std::cout << moveAssignedLoad << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_01/Truckload.cpp ================================================ #include "Truckload.h" #include #include // For standard exception type std::out_of_range #include // For std::string and std::to_string() #include // For std::swap() // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Destructor: clean up the list (moved to source file to gain access to definition of Package) Truckload::~Truckload() { delete m_head; } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } // Copy assignment operator (updated to use copy-and-swap of course!) Truckload& Truckload::operator=(const Truckload& src) { auto copy{src}; swap(copy); return *this; } // Move constructor (noexcept!) Truckload::Truckload(Truckload&& src) noexcept : m_head{ src.m_head } , m_tail{ src.m_tail } { // Do not forget to dislodge the linked list from the moved src Truckload src.m_head = src.m_tail = nullptr; } // Move assignment operator (noexcept + move-and-swap of course!) Truckload& Truckload::operator=(Truckload&& src) noexcept { auto moved{std::move(src)}; swap(moved); return *this; } // Swap assignment operator (noexcept + move-and-swap of course!) void Truckload::swap(Truckload& other) noexcept { std::swap(m_head, other.m_head); std::swap(m_tail, other.m_tail); } // Optional yet conventional non-member function (forwards to member function) void swap(Truckload& one, Truckload& other) noexcept { one.swap(other); } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox& Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } throw std::out_of_range{ "Index too large: " + std::to_string(index) }; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_01/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload ~Truckload(); // Destructor Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor Truckload& operator=(const Truckload& src); // Copy assignment operator Truckload(Truckload&& src) noexcept; // Move constructor Truckload& operator=(Truckload&& src) noexcept; // Move assignment operator void swap(Truckload& other) noexcept; class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; std::ostream& operator<<(std::ostream& stream, const Truckload& load); // Optional yet conventional non-member function (forwards to member function) void swap(Truckload& one, Truckload& other) noexcept; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_02/LinkedList.h ================================================ #ifndef LINKEDLIST_H #define LINKEDLIST_H /* The LinkedList template below follows some (not all) conventions of Standard Library containers: some member function names are analogous, as is the choice not to work with exceptions in case you do something wrong (such as calling back() or pop_front() on an empty list). The latter has the implication that if you mis-use the container, your program will likely crash... New in this version are the move constructor / assignment operators (mind the noexcepts, as always), and the fact that the push_back() and push_front() members have been updated to allow new T values to be moved in as well. Do not forget to add a move constructor for the nested Node class as well to prevent any unnecessary copies there! Note that if you are not defining a generic container template, you'd usually not add multiple overloads for copying and moving anymore. Even here, you could consider simply defining these two pass-by-value members: void push_front(T value); // Copy or move an object to the head void push_back(T value); // Copy or move an object to the tail And then apply the same simplification to the constructor of the nested Node class. The only downside then is that this is not optimal for objects without support for move semantics, bit given that move semantics has been around for almost a decade should be increasingly unlikely. We invite you to give it a try, and simplify the code along these lines. */ #include // for the size_t typedef #include // for std::swap() template class LinkedList { public: LinkedList() = default; // Default constructor (all pointers are initialised to nullptr) ~LinkedList(); LinkedList(const LinkedList& list); // Copy constructor LinkedList& operator=(const LinkedList& list); // Copy assignment operator LinkedList(LinkedList&& list) noexcept; // Move constructor LinkedList& operator=(LinkedList&& list) noexcept; // Move assignment operator void push_front(const T& value); // Add an object to the head void push_back(const T& value); // Add an object to the tail void push_front(T&& value); // Move an object to the head void push_back(T&& value); // Move an object to the tail void pop_back(); // Removes the last element from the list (undefined behavior for empty lists) void pop_front(); // Removes the first element from the list (undefined behavior for empty lists) T& front(); // Get the object at the head (undefined behavior for empty lists) T& back(); // Get the object at the tail (undefined behavior for empty lists) const T& front() const; // Get the object at the head (undefined behavior for empty lists) const T& back() const; // Get the object at the tail (undefined behavior for empty lists) bool empty() const; // Checks whether the list is empty or not void clear(); // Function to remove all elements from the list size_t size() const; // Get the number of elements from the list class Iterator; // Nested Iterator class declaration (definition below) Iterator front_iterator() const; // Get an Iterator that starts at the head Iterator back_iterator() const; // Get an Iterator that starts at the tail void swap(LinkedList& other) noexcept; // Swap function private: class Node // Node class definition { public: Node(const T& value) : m_value{ value } {} Node(T&& value) : m_value{ std::move(value) } {} T m_value; Node* m_next{}; Node* m_previous{}; }; void push_front(Node* node) noexcept; void push_back(Node* node) noexcept; Node* m_head{}; // Pointer to first node Node* m_tail{}; // Pointer to last node size_t m_size{}; }; // Non-member swap function. // Convention dictates swap is present as non-member function. // For class types it is often easiest (and recommended) // to implement it using a member swap function. template void swap(LinkedList& one, LinkedList& other) { one.swap(other); } // --------------------------------------------- // Member definitions // --------------------------------------------- // Destructor template template LinkedList::~LinkedList() { clear(); } // Copy constructor template template LinkedList::LinkedList(const LinkedList& list) { // Use existing members (iteration, push_back()) to implement the copying // This avoids duplicating any code that manipulates the list's pointers / size members. for (auto iterator{ list.front_iterator() }; iterator; iterator.next()) push_back(iterator.value()); } // Move constructor template template LinkedList::LinkedList(LinkedList&& list) noexcept : m_head{ list.m_head } , m_tail{ list.m_tail } , m_size{ list.m_size } { // Make sure list no longer deletes the nodes when destructed list.m_size = 0; list.m_head = list.m_tail = nullptr; // <-- optional } // Copy assignment operator template (uses copy-and-swap idiom) template LinkedList& LinkedList::operator=(const LinkedList& other) { auto copy{ other }; swap(copy); return *this; } // Move assignment operator template (uses move-and-swap idiom) template LinkedList& LinkedList::operator=(LinkedList&& other) noexcept { auto moved{ std::move(other) }; swap(moved); return *this; } // Template for member functions that adds a node to the head of the list template void LinkedList::push_front(Node* node) noexcept { Node* oldHead{ m_head }; m_head = node; ++m_size; if (oldHead) { oldHead->m_previous = m_head; m_head->m_next = oldHead; } else // list was empty before, and now has one element { m_tail = m_head; } } // Template for member functions that copy a value to the head of the list // Use push_front(Node*) to avoid any duplication! template void LinkedList::push_front(const T& value) { push_front(new Node{value}); } // Template for member functions that moves a value to the head of the list // Use push_front(Node*) to avoid any duplication! template void LinkedList::push_front(T&& value) { push_front(new Node{std::move(value)}); } // Template for member functions that add a node to the tail of the list template void LinkedList::push_back(Node* node) noexcept { Node* oldTail{ m_tail }; m_tail = node; ++m_size; if (oldTail) { oldTail->m_next = m_tail; m_tail->m_previous = oldTail; } else // list was empty before, and now has one element { m_head = m_tail; } } // Template for member functions that copy a value to the tail of the list // Use push_back(Node*) to avoid any duplication! template void LinkedList::push_back(const T& value) { push_back(new Node{value}); } // Template for member functions that moves a value to the tail of the list // Use push_back(Node*) to avoid any duplication! template void LinkedList::push_back(T&& value) { push_back(new Node{std::move(value)}); } // Template for member functions that remove an object from the head of the list template void LinkedList::pop_front() { Node* oldHead{ m_head }; if (oldHead == m_tail) { m_head = m_tail = nullptr; } else { m_head = oldHead->m_next; m_head->m_previous = nullptr; } --m_size; delete oldHead; } // Template function member to remove an object from the tail of the list template void LinkedList::pop_back() { Node* oldTail{ m_tail }; if (oldTail == m_head) { m_head = m_tail = nullptr; } else { m_tail = oldTail->m_previous; m_tail->next = nullptr; } --m_size; delete oldTail; } // Template function members to get the object at the head of the list template T& LinkedList::front() { return m_head->value; } template const T& LinkedList::front() const { return m_head->value; } // Template function members to get the object at the tail of the list template T& LinkedList::back() { return m_tail->value; } template const T& LinkedList::back() const { return m_tail->value; } // Check whether list is empty or not template bool LinkedList::empty() const { return m_size == 0; // (or m_head == nullptr, or m_tail == nullptr) } // Template to get the size of a list template size_t LinkedList::size() const { return m_size; } // Template to clear a list template void LinkedList::clear() { // Use existing functions (avoid code duplication!) while (!empty()) pop_front(); } // Member function template to swap two lists template void LinkedList::swap(LinkedList& other) noexcept { std::swap(m_head, other.m_head); std::swap(m_tail, other.m_tail); std::swap(m_size, other.m_size); } // Definition of the nested Iterator class template class LinkedList::Iterator { public: explicit Iterator(Node* node) : m_current{ node } {} const T& value() const { return m_current->m_value; } bool hasValue() const { return m_current != nullptr; } operator bool() const { return m_current != nullptr; } void next() { m_current = m_current->m_next; } void previous() { m_current = m_current->m_previous; } private: Node* m_current; }; // Get an Iterator that starts at the head template typename LinkedList::Iterator LinkedList::front_iterator() const { return Iterator{ m_head }; } // Get an Iterator that starts at the tail template typename LinkedList::Iterator LinkedList::back_iterator() const { return Iterator{ m_tail }; } #endif //LINKEDLIST_H ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_02/Soln18_02.cpp ================================================ // Exercise 18-2 Exercising the LinkedList's move capabilities // We do so by creating a linked list of std::unique_ptr<> elements: // a perfect example of an element type that cannot be copied! #include "LinkedList.h" #include // for std::unique_ptr<> #include #include void printList(std::string_view message, const LinkedList>& list) { std::cout << message << ": "; for (auto iterator{ list.front_iterator() }; iterator; iterator.next()) { std::cout << *iterator.value() << ' '; } std::cout << std::endl; } int main() { // In real life, you'd rarely use std::unique_ptr. // A more realistic use would be std::unique_ptr, // where ClassType is a (potentially polymorphic) class type. // For our example here, std::unique_ptr will do just fine: // the idea is simply to use an uncopyable type as template type argument for LinkedList. LinkedList> number_pointers; auto one{ std::make_unique(1) }; number_pointers.push_back(std::move(one)); number_pointers.push_back(std::make_unique(2)); printList("Elements in the original list", number_pointers); LinkedList> move_constructed{std::move(number_pointers)}; printList("Elements in the move constructed list", move_constructed); LinkedList> move_assigned; move_assigned = std::move(move_constructed); printList("Elements in the move assigned list", move_assigned); } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_03/Customer.cpp ================================================ // A simple C++ customer class #include "Customer.h" Customer::Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city) : m_surname{ surname } , m_name{ name } , m_street{ street } , m_streetNumber{ streetNumber } , m_city{ city } {} std::string Customer::toString() const { std::string result; result += m_surname; result += ' '; result += m_name; result += ", "; result += m_street; result += ' '; result += std::to_string(m_streetNumber); result += ", "; result += m_city; return result; } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_03/Customer.h ================================================ // A simple C++ customer class #ifndef CUSTOMER_H #define CUSTOMER_H #include #include class Customer { public: Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city ); std::string toString() const; private: std::string m_surname; std::string m_name; std::string m_street; int m_streetNumber; std::string m_city; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_03/DB.cpp ================================================ // A mocked database #include "DB.h" #include // For std::strcmp() #include #include namespace { struct QueryResult { std::vector> data; size_t index {}; }; class Database { public: Database() = default; bool hasConnection() const { return m_connected; } void connect() { m_connected = true; } void disconnect() { m_connected = false; } QueryResult* query(const char* query); private: bool m_connected{}; }; } DB_CONNECTION* db_connect() { // We only have one single database, which allows only one single connection: static Database theDatabase; if (theDatabase.hasConnection()) { return nullptr; } else { theDatabase.connect(); return &theDatabase; } } void db_disconnect(DB_CONNECTION* connection) { // reinterpret_cast<> is used to cast between pointers/references of // unrelated types (such as void* and Database*) reinterpret_cast(connection)->disconnect(); } DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query) { return reinterpret_cast(connection)->query(query); } QueryResult* Database::query(const char* query) { if (!hasConnection()) { return nullptr; } // Our database only understands one single query! if (std::strcmp(query, "SELECT * FROM CUSTOMER_TABEL") != 0) { return nullptr; } auto result{ std::make_unique() }; result->data = std::vector>{ { "Sherlock", "Holmes", "Baker Street", "221", "London" }, { "Joe", "Biden", "Pennsylvania Avenue", "1600", "Washington DC" }, { "Donald", "Duck", "Webfoot Walk", "1313", "Duckville" }, { "Sirius", "Black", "Grimmauld Place", "12", "London" }, { "Nemo", "Clownfish", "Wallaby Way", "42", "Sydney" }, { "Sam", "Malone", "Beacon Street", "112", "Boston" } }; return result.release(); } int db_num_fields(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->data.empty()) { return -1; } else { return static_cast(theResult->data.front().size()); } } DB_ROW db_fetch_row(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->index >= theResult->data.size()) { return nullptr; } else { return theResult->data[theResult->index++].data(); } } void db_free_result(DB_QUERY_RESULT* result) { delete reinterpret_cast(result); } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_03/DB.h ================================================ // A C-style interface to a mock database // (a simplified subset of the MySQL C interface) #ifndef DB_INCLUDES #define DB_INCLUDES using DB_CONNECTION = void; using DB_QUERY_RESULT = void; using DB_ROW = const char**; /*! Make a connection to the database * Do not forget to close the database connection (db_disconnect()) once done with it. * \return a DB_CONNECTION handle; nullptr upon failure */ DB_CONNECTION* db_connect(); /*! Query the database * \param[in] connection A database connection handle returned by db_connect() * \param[in] query A SQL query * \return A handle to the result of the query (memory to be freed using db_free_result()!) */ DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query); /*! Retrieve the number of fields per row stored by the given query result * \param[in] result A handle returned by db_query * \return The number of fields per row stored by the result; -1 upon failure */ int db_num_fields(DB_QUERY_RESULT* result); /*! Fetch a single row from the result. * \param[in] result A handle returned by db_query * \return An array of strings. Each field is represented as a string (zero-terminated char array). * Let row be the result, then the first field is accessed using result[0]. */ DB_ROW db_fetch_row(DB_QUERY_RESULT* result); /*! Release the memory allocated for the result. * \param[in] result A handle returned by db_query() */ void db_free_result(DB_QUERY_RESULT* result); /*! Disconnect from the database * \param[in] connection A connection handle returned by db_connect() */ void db_disconnect(DB_CONNECTION* connection); #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_03/DBException.h ================================================ // A simple C++ exception type #ifndef DB_EXCEPTION_H #define DB_EXCEPTION_H #include class DatabaseException : public std::runtime_error { public: using std::runtime_error::runtime_error; // Inherit constructor }; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_03/DB_RAII.h ================================================ // RAII classes for handles returned by DB.h C interface functions /* In this solution we have the RAII classes accept the resource handle in their constructor. Alternatively, you could also acquire the resources inside the constructors of the RAII class. For instance: you could call db_connect() from within the DBConnectionRAII() contructor. When creating RAII classes, it is typically crucial they cannot be copied (otherwise multiple objects would be releasing the same resource, which is typically not allowed). To accomplish this, you delete both copy members. */ #ifndef DB_RAII_H #define DB_RAII_H #include "DB.h" /** * RAII object that ensures that a given database connection is closed * once the RAII object goes out of scope. */ class DBConnectionRAII { public: DBConnectionRAII(DB_CONNECTION* connection) noexcept : m_connection(connection) { } ~DBConnectionRAII() // implicitly noexcept { if (m_connection) { db_disconnect(m_connection); } } // Prevent copying by deleting both copy members DBConnectionRAII(const DBConnectionRAII& other) = delete; DBConnectionRAII& operator=(const DBConnectionRAII& other) = delete; // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_CONNECTION*() const noexcept { return m_connection; } private: DB_CONNECTION* m_connection; }; /* RAII object that takes a DB_QUERY_RESULT and ensures it is freed */ class DBQueryResultRAII { public: DBQueryResultRAII(DB_QUERY_RESULT* result) noexcept : m_result(result) { } ~DBQueryResultRAII() // implicitly noexcept { if (m_result) { db_free_result(m_result); } } // Prevent copying by deleting both copy members DBQueryResultRAII(const DBQueryResultRAII& other) = delete; DBQueryResultRAII& operator=(const DBQueryResultRAII& other) = delete; // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_QUERY_RESULT* () const noexcept { return m_result; } private: DB_QUERY_RESULT* m_result; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_03/Soln18_03.cpp ================================================ // Exercise 18-3 /* Below is the same test program as used in Exercise 16-6, with some additional commented lines to show that the RAII objects cannot be copied. */ #include #include #include "DB.h" #include "DBException.h" #include "Customer.h" #include "DB_RAII.h" void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream int main() { DBConnectionRAII connection{ db_connect() }; /* DBConnectionRAII copy{ connection }; // Will not compile (copy constructor is deleted) DBConnectionRAII otherConnection{ db_connect() }; otherConnection = connection; // Will not compile (copy assignment operator is deleted) */ try { DBQueryResultRAII result{ db_query(connection, "SELECT * FROM CUSTOMER_TABEL") }; if (!result) { throw DatabaseException{"Query failed"}; } /* DBQueryResultRAII copy{ result }; // Will not compile (copy constructor is deleted) DBQueryResultRAII otherResult{ db_query(connection, "SELECT * FROM CUSTOMER_TABEL") }; otherResult = result; // Will not compile (copy assignment operator is deleted) */ std::vector customers{ readCustomers(result) }; if (customers.empty()) { std::cerr << "No customers found?" << std::endl; return 2; } for (auto& customer : customers) { print(std::cout, customer); } } catch (std::exception& caught) { std::cerr << caught.what() << std::endl; return 1; } } std::vector readCustomers(DB_QUERY_RESULT* result) { // Sanity check // (if the number of fields does not match 5, // the code below would crash!) verifyCustomerFields(result); std::vector customers; auto row{ db_fetch_row(result) }; while (row) { customers.push_back(Customer{ row[0], // Surname row[1], // Name row[2], // Street std::stoi(row[3]), // Street number row[4] // City }); row = db_fetch_row(result); } return customers; } void verifyCustomerFields(DB_QUERY_RESULT* result) { int numFields{ db_num_fields(result) }; if (numFields < 0) { throw DatabaseException{"db_num_fields() failed"}; } if (numFields != 5) { throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; } } void print(std::ostream& stream, const Customer& customer) { stream << customer.toString() << std::endl; if (std::cout.fail()) { std::cout.clear(); throw std::runtime_error("Failed to output customer"); } } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_04/Customer.cpp ================================================ // A simple C++ customer class #include "Customer.h" Customer::Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city) : m_surname{ surname } , m_name{ name } , m_street{ street } , m_streetNumber{ streetNumber } , m_city{ city } {} std::string Customer::toString() const { std::string result; result += m_surname; result += ' '; result += m_name; result += ", "; result += m_street; result += ' '; result += std::to_string(m_streetNumber); result += ", "; result += m_city; return result; } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_04/Customer.h ================================================ // A simple C++ customer class #ifndef CUSTOMER_H #define CUSTOMER_H #include #include class Customer { public: Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city ); std::string toString() const; private: std::string m_surname; std::string m_name; std::string m_street; int m_streetNumber; std::string m_city; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_04/DB.cpp ================================================ // A mocked database #include "DB.h" #include // For std::strcmp() #include #include namespace { struct QueryResult { std::vector> data; size_t index {}; }; class Database { public: Database() = default; bool hasConnection() const { return m_connected; } void connect() { m_connected = true; } void disconnect() { m_connected = false; } QueryResult* query(const char* query); private: bool m_connected{}; }; } DB_CONNECTION* db_connect() { // We only have one single database, which allows only one single connection: static Database theDatabase; if (theDatabase.hasConnection()) { return nullptr; } else { theDatabase.connect(); return &theDatabase; } } void db_disconnect(DB_CONNECTION* connection) { // reinterpret_cast<> is used to cast between pointers/references of // unrelated types (such as void* and Database*) reinterpret_cast(connection)->disconnect(); } DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query) { return reinterpret_cast(connection)->query(query); } QueryResult* Database::query(const char* query) { if (!hasConnection()) { return nullptr; } // Our database only understands one single query! if (std::strcmp(query, "SELECT * FROM CUSTOMER_TABEL") != 0) { return nullptr; } auto result{ std::make_unique() }; result->data = std::vector>{ { "Sherlock", "Holmes", "Baker Street", "221", "London" }, { "Joe", "Biden", "Pennsylvania Avenue", "1600", "Washington DC" }, { "Donald", "Duck", "Webfoot Walk", "1313", "Duckville" }, { "Sirius", "Black", "Grimmauld Place", "12", "London" }, { "Nemo", "Clownfish", "Wallaby Way", "42", "Sydney" }, { "Sam", "Malone", "Beacon Street", "112", "Boston" } }; return result.release(); } int db_num_fields(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->data.empty()) { return -1; } else { return static_cast(theResult->data.front().size()); } } DB_ROW db_fetch_row(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->index >= theResult->data.size()) { return nullptr; } else { return theResult->data[theResult->index++].data(); } } void db_free_result(DB_QUERY_RESULT* result) { delete reinterpret_cast(result); } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_04/DB.h ================================================ // A C-style interface to a mock database // (a simplified subset of the MySQL C interface) #ifndef DB_INCLUDES #define DB_INCLUDES using DB_CONNECTION = void; using DB_QUERY_RESULT = void; using DB_ROW = const char**; /*! Make a connection to the database * Do not forget to close the database connection (db_disconnect()) once done with it. * \return a DB_CONNECTION handle; nullptr upon failure */ DB_CONNECTION* db_connect(); /*! Query the database * \param[in] connection A database connection handle returned by db_connect() * \param[in] query A SQL query * \return A handle to the result of the query (memory to be freed using db_free_result()!) */ DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query); /*! Retrieve the number of fields per row stored by the given query result * \param[in] result A handle returned by db_query * \return The number of fields per row stored by the result; -1 upon failure */ int db_num_fields(DB_QUERY_RESULT* result); /*! Fetch a single row from the result. * \param[in] result A handle returned by db_query * \return An array of strings. Each field is represented as a string (zero-terminated char array). * Let row be the result, then the first field is accessed using result[0]. */ DB_ROW db_fetch_row(DB_QUERY_RESULT* result); /*! Release the memory allocated for the result. * \param[in] result A handle returned by db_query() */ void db_free_result(DB_QUERY_RESULT* result); /*! Disconnect from the database * \param[in] connection A connection handle returned by db_connect() */ void db_disconnect(DB_CONNECTION* connection); #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_04/DBException.h ================================================ // A simple C++ exception type #ifndef DB_EXCEPTION_H #define DB_EXCEPTION_H #include class DatabaseException : public std::runtime_error { public: using std::runtime_error::runtime_error; // Inherit constructor }; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_04/DB_RAII.h ================================================ // RAII classes for handles returned by DB.h C interface functions /* In this solution we have the RAII classes accept the resource handle in their constructor. Alternatively, you could also acquire the resources inside the constructors of the RAII class. For instance: you could call db_connect() from within the DBConnectionRAII() contructor. When creating RAII classes, it is typically crucial they cannot be copied (otherwise multiple objects would be releasing the same resource, which is typically not allowed). To accomplish this, you delete both copy members. Moving RAII objects, on the other hand, is usually possible. */ #ifndef DB_RAII_H #define DB_RAII_H #include "DB.h" /** * RAII object that ensures that a given database connection is closed * once the RAII object goes out of scope. */ class DBConnectionRAII { public: DBConnectionRAII(DB_CONNECTION* connection = nullptr) noexcept : m_connection(connection) { } ~DBConnectionRAII() // implicitly noexcept { if (m_connection) { db_disconnect(m_connection); } } // Prevent copying by deleting both copy members DBConnectionRAII(const DBConnectionRAII& other) = delete; DBConnectionRAII& operator=(const DBConnectionRAII& other) = delete; // Allow moving by adding the appropriate members DBConnectionRAII(DBConnectionRAII&& other) noexcept : m_connection{ other.m_connection } { other.m_connection = nullptr; // Make sure other no longer closes the connection } DBConnectionRAII& operator=(DBConnectionRAII&& other) noexcept { // You could consider move-and-swap here, but it's so trivial we decided against it here. m_connection = other.m_connection; other.m_connection = nullptr; return *this; } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_CONNECTION*() const noexcept { return m_connection; } private: DB_CONNECTION* m_connection; }; /* RAII object that takes a DB_QUERY_RESULT and ensures it is freed */ class DBQueryResultRAII { public: DBQueryResultRAII(DB_QUERY_RESULT* result = nullptr) noexcept : m_result{ result } { } ~DBQueryResultRAII() // implicitly noexcept { if (m_result) { db_free_result(m_result); } } // Prevent copying by deleting both copy members DBQueryResultRAII(const DBQueryResultRAII& other) = delete; DBQueryResultRAII& operator=(const DBQueryResultRAII& other) = delete; // Allow moving by adding the appropriate members DBQueryResultRAII(DBQueryResultRAII&& other) noexcept : m_result{ other.m_result } { other.m_result = nullptr; // Make sure other no longer closes the connection } DBQueryResultRAII& operator=(DBQueryResultRAII&& other) noexcept { // You could consider move-and-swap here, but it's so trivial we decided against it here. m_result = other.m_result; other.m_result = nullptr; return *this; } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_QUERY_RESULT* () const noexcept { return m_result; } private: DB_QUERY_RESULT* m_result; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_04/Soln18_04.cpp ================================================ // Exercise 18-4 /* Below is the same test program as used in Exercise 16-6, with some additional lines to show that the RAII objects can be moved. */ #include #include #include "DB.h" #include "DBException.h" #include "Customer.h" #include "DB_RAII.h" void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream int main() { DBConnectionRAII connection{ db_connect() }; DBConnectionRAII moved_connection{ std::move(connection) }; try { DBQueryResultRAII result{ db_query(moved_connection, "SELECT * FROM CUSTOMER_TABEL") }; if (!result) { throw DatabaseException{"Query failed"}; } DBQueryResultRAII moved_result; moved_result = std::move(result); std::vector customers{ readCustomers(moved_result) }; if (customers.empty()) { std::cerr << "No customers found?" << std::endl; return 2; } for (auto& customer : customers) { print(std::cout, customer); } } catch (std::exception& caught) { std::cerr << caught.what() << std::endl; return 1; } } std::vector readCustomers(DB_QUERY_RESULT* result) { // Sanity check // (if the number of fields does not match 5, // the code below would crash!) verifyCustomerFields(result); std::vector customers; auto row{ db_fetch_row(result) }; while (row) { customers.push_back(Customer{ row[0], // Surname row[1], // Name row[2], // Street std::stoi(row[3]), // Street number row[4] // City }); row = db_fetch_row(result); } return customers; } void verifyCustomerFields(DB_QUERY_RESULT* result) { int numFields{ db_num_fields(result) }; if (numFields < 0) { throw DatabaseException{"db_num_fields() failed"}; } if (numFields != 5) { throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; } } void print(std::ostream& stream, const Customer& customer) { stream << customer.toString() << std::endl; if (std::cout.fail()) { std::cout.clear(); throw std::runtime_error("Failed to output customer"); } } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_05/Customer.cpp ================================================ // A simple C++ customer class #include "Customer.h" Customer::Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city) : m_surname{ surname } , m_name{ name } , m_street{ street } , m_streetNumber{ streetNumber } , m_city{ city } {} std::string Customer::toString() const { std::string result; result += m_surname; result += ' '; result += m_name; result += ", "; result += m_street; result += ' '; result += std::to_string(m_streetNumber); result += ", "; result += m_city; return result; } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_05/Customer.h ================================================ // A simple C++ customer class #ifndef CUSTOMER_H #define CUSTOMER_H #include #include class Customer { public: Customer( std::string_view surname, std::string_view name, std::string_view street, int streetNumber, std::string_view city ); std::string toString() const; private: std::string m_surname; std::string m_name; std::string m_street; int m_streetNumber; std::string m_city; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_05/DB.cpp ================================================ // A mocked database #include "DB.h" #include // For std::strcmp() #include #include namespace { struct QueryResult { std::vector> data; size_t index {}; }; class Database { public: Database() = default; bool hasConnection() const { return m_connected; } void connect() { m_connected = true; } void disconnect() { m_connected = false; } QueryResult* query(const char* query); private: bool m_connected{}; }; } DB_CONNECTION* db_connect() { // We only have one single database, which allows only one single connection: static Database theDatabase; if (theDatabase.hasConnection()) { return nullptr; } else { theDatabase.connect(); return &theDatabase; } } void db_disconnect(DB_CONNECTION* connection) { // reinterpret_cast<> is used to cast between pointers/references of // unrelated types (such as void* and Database*) reinterpret_cast(connection)->disconnect(); } DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query) { return reinterpret_cast(connection)->query(query); } QueryResult* Database::query(const char* query) { if (!hasConnection()) { return nullptr; } // Our database only understands one single query! if (std::strcmp(query, "SELECT * FROM CUSTOMER_TABEL") != 0) { return nullptr; } auto result{ std::make_unique() }; result->data = std::vector>{ { "Sherlock", "Holmes", "Baker Street", "221", "London" }, { "Joe", "Biden", "Pennsylvania Avenue", "1600", "Washington DC" }, { "Donald", "Duck", "Webfoot Walk", "1313", "Duckville" }, { "Sirius", "Black", "Grimmauld Place", "12", "London" }, { "Nemo", "Clownfish", "Wallaby Way", "42", "Sydney" }, { "Sam", "Malone", "Beacon Street", "112", "Boston" } }; return result.release(); } int db_num_fields(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->data.empty()) { return -1; } else { return static_cast(theResult->data.front().size()); } } DB_ROW db_fetch_row(DB_QUERY_RESULT* result) { auto* theResult{ reinterpret_cast(result) }; if (!theResult || theResult->index >= theResult->data.size()) { return nullptr; } else { return theResult->data[theResult->index++].data(); } } void db_free_result(DB_QUERY_RESULT* result) { delete reinterpret_cast(result); } ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_05/DB.h ================================================ // A C-style interface to a mock database // (a simplified subset of the MySQL C interface) #ifndef DB_INCLUDES #define DB_INCLUDES using DB_CONNECTION = void; using DB_QUERY_RESULT = void; using DB_ROW = const char**; /*! Make a connection to the database * Do not forget to close the database connection (db_disconnect()) once done with it. * \return a DB_CONNECTION handle; nullptr upon failure */ DB_CONNECTION* db_connect(); /*! Query the database * \param[in] connection A database connection handle returned by db_connect() * \param[in] query A SQL query * \return A handle to the result of the query (memory to be freed using db_free_result()!) */ DB_QUERY_RESULT* db_query(DB_CONNECTION* connection, const char* query); /*! Retrieve the number of fields per row stored by the given query result * \param[in] result A handle returned by db_query * \return The number of fields per row stored by the result; -1 upon failure */ int db_num_fields(DB_QUERY_RESULT* result); /*! Fetch a single row from the result. * \param[in] result A handle returned by db_query * \return An array of strings. Each field is represented as a string (zero-terminated char array). * Let row be the result, then the first field is accessed using result[0]. */ DB_ROW db_fetch_row(DB_QUERY_RESULT* result); /*! Release the memory allocated for the result. * \param[in] result A handle returned by db_query() */ void db_free_result(DB_QUERY_RESULT* result); /*! Disconnect from the database * \param[in] connection A connection handle returned by db_connect() */ void db_disconnect(DB_CONNECTION* connection); #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_05/DBException.h ================================================ // A simple C++ exception type #ifndef DB_EXCEPTION_H #define DB_EXCEPTION_H #include class DatabaseException : public std::runtime_error { public: using std::runtime_error::runtime_error; // Inherit constructor }; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_05/DB_RAII.h ================================================ // RAII classes for handles returned by DB.h C interface functions /* In this solution we have the RAII classes accept the resource handle in their constructor. Alternatively, you could also acquire the resources inside the constructors of the RAII class. For instance: you could call db_connect() from within the DBConnectionRAII() contructor. When creating RAII classes, it is typically crucial they cannot be copied (otherwise multiple objects would be releasing the same resource, which is typically not allowed). To accomplish this, you delete both copy members. Moving RAII objects, on the other hand, is usually possible. */ #ifndef DB_RAII_H #define DB_RAII_H #include "DB.h" #include // For std::exchange<>() /** * RAII object that ensures that a given database connection is closed * once the RAII object goes out of scope. */ class DBConnectionRAII { public: DBConnectionRAII(DB_CONNECTION* connection = nullptr) noexcept : m_connection(connection) { } ~DBConnectionRAII() // implicitly noexcept { if (m_connection) { db_disconnect(m_connection); } } // Prevent copying by deleting both copy members DBConnectionRAII(const DBConnectionRAII& other) = delete; DBConnectionRAII& operator=(const DBConnectionRAII& other) = delete; // Allow moving by adding the appropriate members DBConnectionRAII(DBConnectionRAII&& other) noexcept : m_connection{ std::exchange(other.m_connection, nullptr) } { } DBConnectionRAII& operator=(DBConnectionRAII&& other) noexcept { // You could consider move-and-swap here, but it's so trivial (especially now) // that we decided against it here. m_connection = std::exchange(other.m_connection, nullptr); return *this; } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_CONNECTION*() const noexcept { return m_connection; } private: DB_CONNECTION* m_connection; }; /* RAII object that takes a DB_QUERY_RESULT and ensures it is freed */ class DBQueryResultRAII { public: DBQueryResultRAII(DB_QUERY_RESULT* result = nullptr) noexcept : m_result{ result } { } ~DBQueryResultRAII() // implicitly noexcept { if (m_result) { db_free_result(m_result); } } // Prevent copying by deleting both copy members DBQueryResultRAII(const DBQueryResultRAII& other) = delete; DBQueryResultRAII& operator=(const DBQueryResultRAII& other) = delete; // Allow moving by adding the appropriate members DBQueryResultRAII(DBQueryResultRAII&& other) noexcept : m_result{ std::exchange(other.m_result, nullptr) } { } DBQueryResultRAII& operator=(DBQueryResultRAII&& other) noexcept { // You could consider move-and-swap here, but it's so trivial (especially now) // that we decided against it here. m_result = std::exchange(other.m_result, nullptr); return *this; } // Implicit type conversion to the underlying resource handle. // Note: many RAII types will use a get() function instead // (unique_ptr / shared_ptr, for instance, do this) operator DB_QUERY_RESULT* () const noexcept { return m_result; } private: DB_QUERY_RESULT* m_result; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 18/Soln18_05/Soln18_05.cpp ================================================ // Exercise 18-5 // Only difference is the use of std::exchange() in the RAII classes #include #include #include "DB.h" #include "DBException.h" #include "Customer.h" #include "DB_RAII.h" void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream int main() { DBConnectionRAII connection{ db_connect() }; DBConnectionRAII moved_connection{ std::move(connection) }; try { DBQueryResultRAII result{ db_query(moved_connection, "SELECT * FROM CUSTOMER_TABEL") }; if (!result) { throw DatabaseException{"Query failed"}; } DBQueryResultRAII moved_result; moved_result = std::move(result); std::vector customers{ readCustomers(moved_result) }; if (customers.empty()) { std::cerr << "No customers found?" << std::endl; return 2; } for (auto& customer : customers) { print(std::cout, customer); } } catch (std::exception& caught) { std::cerr << caught.what() << std::endl; return 1; } } std::vector readCustomers(DB_QUERY_RESULT* result) { // Sanity check // (if the number of fields does not match 5, // the code below would crash!) verifyCustomerFields(result); std::vector customers; auto row{ db_fetch_row(result) }; while (row) { customers.push_back(Customer{ row[0], // Surname row[1], // Name row[2], // Street std::stoi(row[3]), // Street number row[4] // City }); row = db_fetch_row(result); } return customers; } void verifyCustomerFields(DB_QUERY_RESULT* result) { int numFields{ db_num_fields(result) }; if (numFields < 0) { throw DatabaseException{"db_num_fields() failed"}; } if (numFields != 5) { throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; } } void print(std::ostream& stream, const Customer& customer) { stream << customer.toString() << std::endl; if (std::cout.fail()) { std::cout.clear(); throw std::runtime_error("Failed to output customer"); } } ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_01/Soln19_01.cpp ================================================ // Exercise 19-1. // A lambda expression returning the number of vector elements that begin with a given letter. #include #include #include #include int main() { std::vector words{"apple", "pear", "plum", "orange", "peach", "grape", "greengage"}; std::cout << "Words are:\n"; for (const auto& word : words) std::cout << std::format("{:10}", word); std::cout << std::endl; const auto count { [&words](char letter) { size_t n {}; for (auto& word : words) if (letter == word[0]) ++n; return n; } }; char ch {'p'}; std::cout << std::format("There are {} words that begin with {}.\n", count(ch), ch); ch = 'g'; std::cout << std::format("There are {} words that begin with {}.\n", count(ch), ch); } ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_02/Soln19_02.cpp ================================================ // Exercise 19-2. // Sorting in various ways using higher-order functions, functors, and lambda expressions. #include #include #include #include // For std::tolower() #include // For std::abs() #include "Sort.h" // Output vector elements template void list(const std::vector& values, size_t width = 5) { for (auto value : values) std::cout << std::format("{:{}}", value, width); std::cout << std::endl; } int main() { std::vector numbers{ -2, 4, -5, 6, 10, -40, 56, 4, 67, 45 }; std::cout << "\nIntegers to be sorted:\n"; list(numbers); sort(numbers, std::greater<>{}); std::cout << "\nSorted integers:\n"; list(numbers); std::cout << "\nCharacters to be sorted:\n"; std::vector letters{ 'C', 'd', 'a', 'z', 't', 'S', 'p', 'm', 'D', 'f' }; list(letters, 2); sort(letters, [](char x, char y) { return std::tolower(x) < std::tolower(y); }); std::cout << "\nSorted characters:\n"; list(letters, 2); std::cout << "\nFloating-point values to be sorted:\n"; std::vector values{ -2.5, 1.4, -2.55, 6.3, 10.1, -40.5, 56.0, 4.7, 67.3, 45.0 }; list(values, 10); sort(values, [](double x, double y) { return std::abs(x) < std::abs(y); }); std::cout << "\nSorted floaating-point values:\n"; list(values, 10); } ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_02/Sort.h ================================================ // Sort.h #ifndef SORT_H #define SORT_H #include // for size_t #include // for std::swap() #include // The primary sort() template with two parameters calls // this internal sort() template with four parameters template void sort(std::vector& data, Compare compare, size_t start, size_t end); // Sort all vector elements template void sort(std::vector& data, Compare compare) { if (!data.empty()) sort(data, compare, 0, data.size() - 1); } // Swap two vector elements template void swap(std::vector& data, size_t first, size_t second) { std::swap(data[first], data[second]); } // Sort a range of vector elements template void sort(std::vector& data, Compare compare, size_t start, size_t end) { // Start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle value to partition set swap(data, start, (start + end) / 2); // Swap middle value with start // Check data against chosen value size_t current{ start }; // The index of the last element less than the chosen element (after partitioning) for (size_t i{ start + 1 }; i <= end; ++i) { if (compare(data[i], data[start])) // Is value less than chosen word? swap(data, ++current, i); // Yes, so swap to the left } swap(data, start, current); // Swap the chosen value with last in if (current > start) sort(data, compare, start, current - 1); // Sort left subset if exists if (end > current + 1) sort(data, compare, current + 1, end); // Sort right subset if exists } #endif // SORT_H ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_03/Soln19_03.cpp ================================================ // Exercise 19-3. Comparing sorting algorithms #include #include #include // For std::log2() #include // For random number generation #include // For std::bind() and #include // For std::ranges::sort() (bonus algorithm) #include "Sort.h" /* If you look carefully, you will notice that our implementation of quicksort deviates from the theoretical expectations. The larger the input size, the more it deviates (as shown by the ratios in our output). Better implementations of quicksort are no doubt possible: on random data an optimal quicksort algorithm should perform, on average, at a ratio 1.39 of the log-linear best case performance. But better algorithms exist as well. As a reference, we also compared with the Standard Library's sort() algorithm you'll learn about in Chapter 20. This algorithm should perform very close to the theoretical optimum. */ // Function to generate a random integer 1 to 100 (both inclusive) // Caution: unlike uniform_real_distribution, uniform_int_distribution // generates numbers in a closed interval [min, max]. auto createUniformPseudoRandomNumberGenerator() { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ 1u, 100u }; // Generate in [0, 100] interval return std::bind(distribution, generator); //... and in the darkness bind them! } auto generateRandomNumbers(unsigned number) { static auto random{ createUniformPseudoRandomNumberGenerator() }; std::vector random_numbers; for (unsigned i{}; i < number; ++i) random_numbers.push_back(random()); return random_numbers; } int main() { unsigned count {}; const auto counting_less{ [&count](int x, int y) { ++count; return x < y; } }; for (auto size : { 500u, 1'000u, 2'000u, 4'000u }) { const auto numbers{ generateRandomNumbers(size) }; count = 0; // Reset the count auto copy{ numbers }; // Ensure both sorrting algorithms work on exact same random sequence quicksort(copy, counting_less); const auto quicksort_count{ count }; count = 0; // Repeat for bubble sort algorithm copy = numbers; bubbleSort(copy, counting_less); const auto bubble_sort_count{ count }; // Not requested, but it is interesting (see earlier) // to also compare with the sorting algorithm of the C++ Standard Library count = 0; // Repeat once more for std::ranges::sort() (see Chapter 20) copy = numbers; std::ranges::sort(copy, counting_less); const auto std_sort_count{ count }; const auto quick_sort_theory = static_cast(size * std::log2(size)); const auto bubble_sort_theory = size * size; const auto std_sort_theory = static_cast(size * std::log2(size)); std::cout << std::format( "Number of comparisons for {} elements:\n" " - quicksort: {} (n*log(n): {}; ratio: {:.2})\n" " - bubble sort: {} (n*n: {}; ratio: {:.2})\n" " - Standard Library sort: {} (n*log(n): {}; ratio: {:.2})\n", size, quicksort_count, quick_sort_theory, static_cast(quick_sort_theory) / quicksort_count, bubble_sort_count, bubble_sort_theory, static_cast(bubble_sort_theory) / bubble_sort_count, std_sort_count, std_sort_theory, static_cast(std_sort_theory) / std_sort_count ) << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_03/Sort.h ================================================ // Sort.h #ifndef SORT_H #define SORT_H #include // for size_t #include // for std::swap() #include // The two algorithms that we are comparing: template void quicksort(std::vector& values, Compare compare); template void bubbleSort(std::vector& data, Compare compare); // Utility to swap two vector elements template void swap(std::vector& data, size_t first, size_t second) { std::swap(data[first], data[second]); } // The primary quicksort() template with two parameter calls the quicksort() template with four parameters // Sort a range of vector elements template void quicksort(std::vector& data, Compare compare, size_t start, size_t end) { // Start index must be less than end index for 2 or more elements if (!(start < end)) return; // Choose middle value to partition set swap(data, start, (start + end) / 2); // Swap middle value with start // Check data against chosen value size_t current{ start }; // The index of the last element less than the chosen element (after partitioning) for (size_t i{ start + 1 }; i <= end; ++i) { if (compare(data[i], data[start])) // Is value less than chosen word? swap(data, ++current, i); // Yes, so swap to the left } swap(data, start, current); // Swap the chosen value with last in if (current > start) quicksort(data, compare, start, current - 1); // Sort left subset if exists if (end > current + 1) quicksort(data, compare, current + 1, end); // Sort right subset if exists } // Sort all vector elements using quicksort template void quicksort(std::vector& data, Compare compare) { if (!data.empty()) quicksort(data, compare, 0, data.size() - 1); } template void bubbleSort(std::vector& data, Compare compare) { if (data.empty()) return; while (true) { bool swapped{ false }; // Becomes true when not all values are in order for (size_t i {}; i < data.size() - 1; ++i) { if (compare(data[i+1], data[i])) // Out of order so swap them { swap(data, i, i+1); swapped = true; } } if (!swapped) // If there were no swaps break; // ...they are in order... } // ...otherwise, go round again. } #endif // SORT_H ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_04/Collect.h ================================================ #ifndef COLLECT_H #define COLLECT_H #include template std::vector collect(const std::vector& values, Predicate predicate) { std::vector result; for (auto& value : values) if (predicate(value)) result.push_back(value); return result; } #endif // COLLECT_H ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_04/Soln19_04.cpp ================================================ // Exercise 19-4. // Collecting values using higher-order functions, functors, and lambda expressions. /* There are several ways to check for palindromes. One of the more elegant solutions probably though is the recursive one shown here. While recursive lambdas are possible, more or less, recursion is far easier with a regular function. Note that to collect upper case letters, there's no need for a lambda either. Just use a pointer to the std::isupper() function as the callback! */ #include #include #include #include // For std::isupper() #include #include "Collect.h" // Output vector elements template void list(const std::vector& values, size_t width = 5) { for (auto value : values) std::cout << std::format("{:{}}", value, width); std::cout << std::endl; } bool is_palindrome(std::string_view s) { return s.length() == 0 || (s.front() == s.back() && is_palindrome(s.substr(1, s.length() - 2))); } int main() { const std::vector numbers{ -2, 4, -5, 6, 10, -40, 56, 4, 67, 45 }; std::cout << "\nAll numbers:\n"; list(numbers); int threshold {}; std::cout << "\nPlease enter a threshold: "; std::cin >> threshold; const auto greater{ collect(numbers, [threshold](int i) { return i > threshold; }) }; std::cout << "Numbers greater than " << threshold << ":\n"; list(greater); const std::vector letters{ 'C', 'd', 'a', 'z', 't', 'S', 'p', 'm', 'D', 'f' }; std::cout << "\nAll characters:\n"; list(letters, 2); //const auto capitals{ collect(letters, std::isupper) }; // <-- This should but does not work with all compilers const auto capitals{ collect(letters, isupper) }; std::cout << "\nCapital letters:\n"; list(capitals, 2); const std::vector strings{ "palindrome", "racecar", "rubarb", "noon", "kayak", "ananas", "madam", "backwards" }; std::cout << "\nAll strings:\n"; list(strings, 12); const auto palindromes{ collect(strings, is_palindrome) }; std::cout << "\nPalindromes:\n"; list(palindromes, 10); } ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_05/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.m_length, box.m_width, box.m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_05/DeliveryTruck.cpp ================================================ #include "DeliveryTruck.h" DeliveryTruck::DeliveryTruck(Truckload aTruckload) : m_truckload{ std::move(aTruckload) } // Do not copy! {} void DeliveryTruck::deliverBox(SharedBox box) { m_truckload.removeBox(box); // Notify all interested parties (aka "observers") that the Box was delivered for (auto& callback : m_callbacks) callback(box); } void DeliveryTruck::registerOnDelivered(Callback callback) { m_callbacks.push_back(std::move(callback)); // Do not copy! } ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_05/DeliveryTruck.h ================================================ #ifndef DELIVERY_TRUCK_H #define DELIVERY_TRUCK_H #include "Truckload.h" #include // For std::function<> #include // For std::vector<> class DeliveryTruck { public: using Callback = std::function; // Type alias for the type of the delivery callback functions DeliveryTruck(Truckload truckload); // Create a delivery truck (pass-by-value to allow a Truckload to be either copied or moved!) void deliverBox(SharedBox box); void registerOnDelivered(Callback callback); private: Truckload m_truckload; std::vector m_callbacks; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_05/RandomBoxes.h ================================================ #ifndef RANDOM_BOXES_H #define RANDOM_BOXES_H #include "Box.h" #include // For random number generation #include // For std::bind() #include // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max inline auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } inline Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } inline auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } #endif ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_05/Soln19_05.cpp ================================================ // Exercise 19-5. // Using callback functions to implement the so-called observer pattern, // where a callback function is called whenever a certain event occurs // (in this case: when a Box is delivered). #include #include "DeliveryTruck.h" #include "RandomBoxes.h" void logDelivary(SharedBox box) { std::cout << "The box " << *box << " was delivered. On time, as always!" << std::endl; } int main() { const size_t boxCount {20}; // Number of Box object to be created // Create boxCount Box objects Truckload load; for (size_t i {}; i < boxCount; ++i) load.addBox(randomSharedBox()); DeliveryTruck truck{ load }; // Copy the load, because we still need it below. // Note that all Boxes are shared, so the they themselves are not copied. // Register two callback functions: truck.registerOnDelivered(logDelivary); unsigned count {}; truck.registerOnDelivered([&count](SharedBox) { ++count; }); // Deliver some boxes: for (size_t i : { 5u, 8u, 11u }) truck.deliverBox(load[i]); std::cout << count << " boxes were delivered. On time, as always!" << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_05/Truckload.cpp ================================================ #include "Truckload.h" #include #include // For standard exception type std::out_of_range #include // For std::string and std::to_string() // Definition of the nested class member // Since this member is private, // its definition can be moved to the source file. class Truckload::Package { public: SharedBox m_box; // Pointer to the Box object contained in this Package Package* m_next; // Pointer to the next Package in the list Package(SharedBox box) : m_box{ box }, m_next{ nullptr } {} // Constructor ~Package() { delete m_next; } // Destructor }; // Constructor - one Box (moved to source file to gain access to definition of Package) Truckload::Truckload(SharedBox box) { m_head = m_tail = new Package{ box }; } // Constructor - vector of Boxes Truckload::Truckload(const std::vector& boxes) { for (const auto& box : boxes) { addBox(box); } } // Copy constructor Truckload::Truckload(const Truckload& src) { for (Package* package{ src.m_head }; package; package = package->m_next) { addBox(package->m_box); } } Truckload& Truckload::operator=(const Truckload& other) { if (&other != this) // Do not forget: avoid issues with self-assignment! { delete m_head; // Delete all current packages m_head = m_tail = nullptr; // Reset both pointers // Same as copy constructor // (see Chapter 17 for the copy-and-swap idiom that allows you to avoid // duplicating logic like this...) for (Package* package{ other.m_head }; package; package = package->m_next) { addBox(package->m_box); } } return *this; } // Destructor: clean up the list Truckload::~Truckload() { delete m_head; } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_head }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_head; return m_current? m_current->m_box : nullptr; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (!m_current) // If there's no current... return getFirstBox(); // ...return the 1st Box m_current = m_current->m_next; // Move to the next package return m_current? m_current->m_box : nullptr; // Return its box (or nullptr...). } void Truckload::addBox(SharedBox box) { auto package{ new Package{box} }; // Create a new Package if (m_tail) // Check list is not empty m_tail->m_next = package; // Append the new object to the tail else // List is empty m_head = package; // so new object is the head m_tail = package; // Either way: the latest object is the (new) tail } bool Truckload::removeBox(SharedBox boxToRemove) { Package* previous {nullptr}; // no previous yet Package* current {m_head}; // initialize current to the head of the list while (current) { if (current->m_box == boxToRemove) // We found the Box! { // If there is a previous Package make it point to the next one (Figure 12.10) if (previous) previous->m_next = current->m_next; // Update pointers in member variables where required: if (current == m_head) m_head = current->m_next; if (current == m_tail) m_tail = previous; current->m_next = nullptr; // Disconnect the current Package from the list delete current; // and delete it return true; // Return true: we found and removed the box } // Move both pointers along (mind the order!) previous = current; // - first current becomes the new previous current = current->m_next; // - then move current along to the next Package } return false; // Return false: boxToRemove was not found } SharedBox& Truckload::operator[](size_t index) const { size_t count{}; // Package count for (Package* package{ m_head }; package; package = package->m_next) { if (count++ == index) // Up to index yet? return package->m_box; // If so return the pointer to Box } throw std::out_of_range{ "Index too large: " + std::to_string(index) }; } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Exercises/NoModules/Chapter 19/Soln19_05/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(const std::vector& boxes); // Constructor - vector of Boxes Truckload(const Truckload& src); // Copy constructor Truckload& operator=(const Truckload& other); // Copy assignment operator ~Truckload(); // Destructor class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index) const; // Overloaded subscript operator private: class Package; Package* m_head {}; // First in the list Package* m_tail {}; // Last in the list }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: Package* m_head; // The head of the linked list (needed for getFirstBox()) Package* m_current; // The package whose Box was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(Package* head) : m_head{ head }, m_current{ nullptr } {} }; std::ostream& operator<<(std::ostream& stream, const Truckload& load); #endif ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_01/Box.h ================================================ #ifndef BOX_H #define BOX_H #include #include class Box { public: Box() = default; Box(double length, double width, double height) : m_length{length}, m_width{width}, m_height{height} {}; double volume() const { return m_length * m_width * m_height; } friend std::ostream& operator<<(std::ostream& out, const Box& box) { return out << std::format("Box({:.1f}, {:.1f}, {:.1f})", box.m_length, box.m_width, box.m_height); } private: double m_length {1.0}; double m_width {1.0}; double m_height {1.0}; }; #endif ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_01/RandomBoxes.h ================================================ #ifndef RANDOM_BOXES_H #define RANDOM_BOXES_H #include "Box.h" #include // For random number generation #include // For std::bind() #include // For std::make_shared<>() and std::shared_ptr<> // Creates a pseudorandom number generator (PRNG) for random doubles between 0 and max inline auto createUniformPseudoRandomNumberGenerator(double max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_real_distribution distribution{ 0.0, max }; // Generate in [0, max) interval return std::bind(distribution, generator); //... and in the darkness bind them! } inline Box randomBox() { const int dimLimit{ 100 }; // Upper limit on Box dimensions static auto random{ createUniformPseudoRandomNumberGenerator(dimLimit) }; return Box{ random(), random(), random() }; } inline auto randomSharedBox() { return std::make_shared(randomBox()); // Uses copy constructor } #endif ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_01/Soln20_01.cpp ================================================ // Exercise 20-1 Rework the Truckload class using std::vector<> #include #include #include "Truckload.h" #include "RandomBoxes.h" /* In this solution we preserved the interface of the nested Iterator class. An alternative, of course, would be to define Iterator as an alias for std::vector::iterator, and introduce begin() and end() members. But then all code using the iterators would have to be updated as well. */ int main() { const double dimLimit{ 99.0 }; // Upper limit on Box dimensions Truckload load; const size_t boxCount{ 20 }; // Number of Box object to be created // Create boxCount Box objects for (size_t i{}; i < boxCount; ++i) load.addBox(randomSharedBox()); std::cout << "The boxes in the Truckload are:\n"; std::cout << load << std::endl; Truckload moveConstructedLoad{ std::move(load) }; std::cout << "The boxes in the move constructed Truckload are:\n"; std::cout << moveConstructedLoad << std::endl; Truckload moveAssignedLoad; moveAssignedLoad = std::move(moveConstructedLoad); std::cout << "The boxes in the move assigned Truckload are:\n"; std::cout << moveAssignedLoad << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_01/Truckload.cpp ================================================ #include "Truckload.h" #include #include // Constructor - one Box Truckload::Truckload(SharedBox box) : m_boxes{ 1, box } { } // Constructor - vector of Boxes Truckload::Truckload(std::vector boxes) : m_boxes{ std::move(boxes) } { } // Swap assignment operator (noexcept) void Truckload::swap(Truckload& other) noexcept { m_boxes.swap(other.m_boxes); } // Optional yet conventional non-member function (forwards to member function) void swap(Truckload& one, Truckload& other) noexcept { one.swap(other); } Truckload::Iterator Truckload::getIterator() const { return Iterator{ m_boxes }; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getFirstBox() { // Return m_head's box (or nullptr if the list is empty) m_current = m_boxes->begin(); return m_current != m_boxes->end() ? *m_current : SharedBox{}; } // Only thing we changed was adding "Iterator::" to the member's qualification SharedBox Truckload::Iterator::getNextBox() { if (m_current == m_boxes->end()) // If there's no current... return getFirstBox(); // ...return the 1st Box ++m_current; // Move to the next package return m_current != m_boxes->end() ? *m_current : SharedBox{}; } void Truckload::addBox(SharedBox box) { m_boxes.push_back(std::move(box)); } bool Truckload::removeBox(SharedBox boxToRemove) { // Unlike the original implementation, // std::erase() removes all occurrences of boxToRemove. // If that is not acceptable, you can use find() as shown below... return std::erase(m_boxes, boxToRemove) > 0; // Remove only the first occurrence of boxToRemove /* if (auto found{ std::ranges::find(m_boxes, boxToRemove) }; found != end(m_boxes)) { m_boxes.erase(found); return true; } else { return false; } */ } SharedBox& Truckload::operator[](size_t index) { // Original implementation performed bounds checking, so use at() instead of [] return m_boxes.at(index); } SharedBox Truckload::operator[](size_t index) const { // Original implementation performed bounds checking, so use at() instead of [] return m_boxes.at(index); } std::ostream& operator<<(std::ostream& stream, const Truckload& load) { size_t count{}; auto iterator{ load.getIterator() }; for (auto box{ iterator.getFirstBox() }; box; box = iterator.getNextBox()) { std::cout << *box << ' '; if (!(++count % 4)) std::cout << std::endl; } if (count % 4) std::cout << std::endl; return stream; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_01/Truckload.h ================================================ #ifndef TRUCKLOAD_H #define TRUCKLOAD_H #include "Box.h" #include #include #include using SharedBox = std::shared_ptr; class Truckload { public: Truckload() = default; // Default constructor - empty truckload Truckload(SharedBox box); // Constructor - one Box Truckload(std::vector boxes); // Constructor - vector of Boxes (accept by value!) void swap(Truckload& other) noexcept; class Iterator; // Declaration of a public nested class, Truckload::Iterator Iterator getIterator() const; void addBox(SharedBox box); // Add a new SharedBox bool removeBox(SharedBox box); // Remove a Box from the Truckload SharedBox& operator[](size_t index); // Overloaded subscript operator (can no longer be const!) SharedBox operator[](size_t index) const; // Overloaded subscript operator (new const version returns by value) private: std::vector m_boxes; }; // Out-of-class definition of the nested Iterator class // (class itself is part of the public interface, so belongs in the header) // Note that this is effectively a const iterator... class Truckload::Iterator { public: SharedBox getFirstBox(); // Get the first Box SharedBox getNextBox(); // Get the next Box private: const std::vector* m_boxes; // Pointer to the underlying vector std::vector::const_iterator m_current; // Iterator pointing to the Box that was last retrieved friend class Truckload; // Only a Truckload can create an Iterator explicit Iterator(const std::vector& boxes) : m_boxes{ &boxes }, m_current{ boxes.end() } {} }; std::ostream& operator<<(std::ostream& stream, const Truckload& load); // Optional yet conventional non-member function (forwards to member function) void swap(Truckload& one, Truckload& other) noexcept; #endif ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_02/Soln20_02.cpp ================================================ // Exercise 20-2 // Replace a custom stack container by the standard stack container adapter #include #include #include /* Two challenges: - std::stack<>::pop() is a void function. To access the top of the stack, you use std::stack<>::top(). - std::stack is not allowed */ int main() { std::string words[]{ "The", "quick", "brown", "fox", "jumps" }; std::stack wordStack; // A stack of strings for (const auto& word : words) wordStack.push(word); std::stack newStack{ wordStack }; // Create a copy of the stack // Display the words in reverse order while (!newStack.empty()) { std::cout << newStack.top() << ' '; newStack.pop(); } std::cout << std::endl; // Reverse wordStack onto newStack while (!wordStack.empty()) { newStack.push(wordStack.top()); wordStack.pop(); } // Display the words in original order while (!newStack.empty()) { std::cout << newStack.top() << ' '; newStack.pop(); } std::cout << std::endl; std::cout << std::endl << "Enter a line of text:" << std::endl; std::string text; std::getline(std::cin, text); // Read a line into the string object std::stack characters; // A stack for characters for (size_t i{}; i < text.length(); ++i) characters.push(text[i]); // Push the string characters onto the stack std::cout << std::endl; while (!characters.empty()) { std::cout << characters.top(); // Pop the characters off the stack characters.pop(); } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_03/Soln20_03.cpp ================================================ // Exercise 20-3 Replacing custom container types with standard ones /* The following replacements were made compared to Soln17_06.cpp: - LinkedList --> std::vector (not std::list<>, because vector<> should be your go-to container; there's rarely a good reason to use linked lists) - SparseArray --> std::map (no need to use a size_t as the key!) Notice how much more elegant inserting and retrieving values from the map is. All loops at the end of the solution can now also be replaced with elegant range-based for loops. At the bottom, there's two alternative loops: one close to the original one, and one even more elegant one that takes advantage of the fact that the keys in the map are already sorted. Soln20_03A contains alternative solutions based on std::multimap<> */ #include #include #include #include #include #include int main() { std::string text; // Stores input prose or poem std::cout << "Enter a poem or prose over one or more lines.\n" << "Terminate the input with #:\n"; getline(std::cin, text, '#'); std::map> lists; // Extract words and store in the appropriate list const std::string_view separators {" \n\t,.\"?!;:"}; // Separators between words size_t start {}; // Start of a word size_t end {}; // separator position after a word while (std::string::npos != (start = text.find_first_not_of(separators, start))) { end = text.find_first_of(separators, start+1); const auto word{ text.substr(start, end - start) }; const auto letter{ static_cast(std::toupper(word[0])) }; lists[letter].push_back(word); start = end; } // List the words in order 5 to a line const size_t perline {5}; /* Option 1: use a loop similar to the original one */ const std::string_view letters{ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" }; for (char letter : letters) { if (!lists.contains(letter)) continue; size_t count {}; // Word counter for (const auto& word : lists[letter]) { if (count++ % perline == 0 && count != 1) std::cout << std::endl; std::cout << word << ' '; } std::cout << std::endl; } std::cout << std::endl; /* Option 2: take advantage of the fact that the keys are already sorted in the map */ for (const auto& [letter, list] : lists) { size_t count{}; // Word counter for (const auto& word : list) { if (count++ % perline == 0 && count != 1) std::cout << std::endl; std::cout << word << ' '; } std::cout << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_03A/Soln20_03A.cpp ================================================ // Exercise 20-3 Replacing custom container types with standard ones /* The following replacements were made compared to Soln17_06.cpp: - SparseArray> --> std::multimap This means that compared to Soln20_03, we replaced std::map> with the more compact (and efficient) std::multimap. This data structure was less discussed in the main text (as it is less used in practice), but is actually more appropriate here. At the bottom, there's two alternative loops: one close to the original one, and one that takes advantage of the fact that the keys in the multimap are sorted. */ #include #include #include #include #include #include int main() { std::string text; // Stores input prose or poem std::cout << "Enter a poem or prose over one or more lines.\n" << "Terminate the input with #:\n"; getline(std::cin, text, '#'); std::multimap words; const std::string_view letters{ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" }; // Extract words and store in the appropriate list // A list in the SparseArray is selected by the index in letters of the first letter in a word. const std::string_view separators{ " \n\t,.\"?!;:" }; // Separators between words size_t start{}; // Start of a word size_t end{}; // separator position after a word while (std::string::npos != (start = text.find_first_not_of(separators, start))) { end = text.find_first_of(separators, start + 1); const auto word{ text.substr(start, end - start) }; const auto letter{ static_cast(std::toupper(word[0])) }; words.insert({ letter, word }); start = end; } // List the words in order 5 to a line const size_t perline{ 5 }; /* Option 1: use a loop similar to the original one */ for (char letter : letters) { if (!words.contains(letter)) continue; size_t count{}; // Word counter // Retrieve the range of all words that begin with letter const auto [begin, end] { words.equal_range(letter) }; for (auto iter{ begin }; iter != end; ++iter) { if (count++ % perline == 0 && count != 1) std::cout << std::endl; std::cout << iter->second << ' '; } std::cout << std::endl; } std::cout << std::endl; /* Option 2: take advantage of the fact that the keys are already sorted in the multimap */ size_t count{}; // Word counter char previous_letter{}; for (const auto& [letter, word] : words) { // Add extra enter after each new letter (but not the first time) if (count && letter != previous_letter) { std::cout << std::endl; count = 0; } if (count++ % perline == 0 && count != 1) std::cout << std::endl; std::cout << word << ' '; previous_letter = letter; } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_04/Soln20_04.cpp ================================================ // Removing all elements that satisfy a certain condition using std::partition() / stable_partition(). /* Notes: - Normally one would use std::remove() (as this is slightly faster and more to the point), though std::partition() can be used for this as well. - You may notice that std::partition() reorders the elements that it keeps. It is implementation-defined whether this partition() preserves the original order or not. If you want to make sure the original order of the numbers is preserved, you need to use std::stable_partition() */ #include #include #include #include std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); void removeEvenNumbers(std::vector& numbers) { const auto first_even_number { std::stable_partition(begin(numbers), end(numbers), [](auto num) { return num % 2 == 1; }) }; /* ^^^^^^^^^^^^^^^^ if you use partition(), the order of the odd elements may become scrambled */ numbers.erase(first_even_number, end(numbers)); } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers; for (int i{ 1 }; i <= N; ++i) numbers.push_back(i); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_05/Soln20_05.cpp ================================================ // Exercise 20-5 Create a generic average() algorithm using std::accumulate() #include #include #include // for std::pair<> (only required for Solution 2 below) #include #include // Solution 1: simply use accumulate to sum, and determine the count using std::distance() // (the latter is more general than using iterator arithmetic, end - begin, // which only works for random-access iterators) template std::optional average(IterType begin, IterType end) { const auto count{ std::distance(begin, end) }; const auto sum{ std::accumulate(begin, end, T{}) }; return count ? std::optional{ sum / count } : std::nullopt; } // Solution 2: accumulate a pair<> that counts both the number of elements and the sum //template //std::optional average(IterType begin, IterType end) //{ // const auto accumulated { // std::accumulate(begin, end, std::make_pair(0u, T{}), [](const auto& accumulated, const auto& element) // { // return std::make_pair(accumulated.first + 1, accumulated.second + element); // }) // }; // // return accumulated.first ? std::optional{ accumulated.second / accumulated.first } : std::nullopt; //} int main() { std::vector numbers{ 1, 2, 4, 8, 16, 32, 64, 128, 256 }; std::cout << *average(begin(numbers), end(numbers)) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_06/Soln20_06.cpp ================================================ // Removing all elements that satisfy a certain condition // while iterating over a container #include #include #include #include // for std::remove_if() #include // for std::iota() /* Note: std::ranges::iota() does not exist. No doubt because the module defines a std::ranges::views::iota range factory. Maybe you can alter this solution to use that view instead to fill the vector? (Note: in the next exercise you are asked to not use a vector at all, but operate directly with views...) */ std::vector fillVector_1toN(size_t N); // Fill a vector with 1, 2, ..., N void printVector(std::string_view message, const std::vector& numbers); template void removeEvenNumbers(Auto& numbers) /* Using more elegant std::erase_if() */ { std::erase_if(numbers, [](auto number) { return number % 2 == 0; }); } int main() { const size_t num_numbers{ 20 }; auto numbers{ fillVector_1toN(num_numbers) }; printVector("The original set of numbers", numbers); removeEvenNumbers(numbers); printVector("The numbers that were kept", numbers); } std::vector fillVector_1toN(size_t N) { std::vector numbers(N); std::iota(begin(numbers), end(numbers), 1); return numbers; } void printVector(std::string_view message, const std::vector& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_07/Soln20_07.cpp ================================================ // Removing all elements that satisfy a certain condition // while iterating over a container #include #include #include #include using namespace std::ranges::views; template void printVector(std::string_view message, Auto& numbers) { std::cout << message << ": "; for (int number : numbers) std::cout << number << ' '; std::cout << std::endl; } int main() { const size_t num_numbers{ 20 }; auto numbers{ iota(1, 20) }; printVector("The original set of numbers", numbers); auto odd_numbers{ numbers | filter([](int i) { return i % 2 != 0; }) }; printVector("The numbers that were kept", odd_numbers); } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_08/Soln20_08.cpp ================================================ // Exercise 20-8 Apply remove-erase idiom to remove duplicate elements #include #include // For random number generation #include // For std::bind() and std::ref() #include #include // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_numbers{ 25'000 }; std::vector numbers(num_numbers); // Why not use an algorithm as well to generate the random numbers!! // (Note: technically generate() is allowed to copy a function object // as often as it wants. std::ref() ensures that the algorithm // at all times operates on a reference to our random-number generator) auto generator{ createUniformPseudoRandomNumberGenerator(0, 10'000) }; std::ranges::generate(numbers, std::ref(generator)); // Algorithm number two std::ranges::sort(numbers); // And number three; this time combined with the remove-erase idiom (or unique-erase, if you will) /* Option 1: iterator-based */ numbers.erase(std::unique(begin(numbers), end(numbers)), end(numbers)); /* Option 2: range-based (sadly requires two statements...) */ //const auto [to_erase_begin, to_erase_end] { std::ranges::unique(numbers) }; //numbers.erase(to_erase_begin, to_erase_end); std::cout << "Number of unique numbers: " << numbers.size() << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_09/Soln20_09.cpp ================================================ // Exercise 20-9 Parallel version of 20-8 #include #include // For random number generation #include // For std::bind() and std::ref() #include #include #include // Creates a preudo-random number generator (PRNG) that generates unsigned integers // in a closed interval [min, max]. // Caution: unlike std::uniform_real_distribution (see Chapter 12), // uniform_int_distribution generates values in a *closed* interval. auto createUniformPseudoRandomNumberGenerator(unsigned min, unsigned max) { std::random_device seeder; // True random number generator to obtain a seed (slow) std::default_random_engine generator{ seeder() }; // Efficient pseudo-random generator std::uniform_int_distribution distribution{ min, max }; // Generate in [min, max] return std::bind(distribution, generator); //... and in the darkness bind them! } int main() { const size_t num_numbers{ 25'000 }; std::vector numbers(num_numbers); /* Caution: a pseudo-random number generator cannot safely be accessed concurrently! */ auto generator{ createUniformPseudoRandomNumberGenerator(0, 10'000) }; std::ranges::generate(numbers, std::ref(generator)); // Algorithm number two std::sort(std::execution::par, begin(numbers), end(numbers)); // And number three; this time combined with the remove-erase idiom (or unique-erase, if you will) numbers.erase(std::unique(std::execution::par, begin(numbers), end(numbers)), end(numbers)); std::cout << "Number of unique numbers: " << numbers.size() << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_10/Soln20_10.cpp ================================================ // Exercise 20-10: exercising projection with range-based algorithms #include #include #include #include int main() { std::vector names{"Frodo Baggins", "Gandalf the Gray", "Aragon", "Samwise Gamgee", "Peregrin Took", "Meriadoc Brandybuck", "Gimli", "Legolas Greenleaf", "Boromir"}; // Sort the names lexicographically std::ranges::sort(names); std::cout << "Names sorted lexicographically:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl << std::endl; // Sort the names by length /* Option 1: projection using member function pointer */ std::ranges::sort(names, std::less<>{}, &std::string::length); /* Option 2: projection using lambda expression */ // std::ranges::sort(names, std::less<>{}, [](const auto& name) { return name.length(); }); std::cout << "Names sorted by length:" << std::endl; for (const auto& name : names) std::cout << name << ", "; std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_11/Soln20_11.cpp ================================================ // Exercise 20-11: exercising range factories and range adaptors #include #include #include #include using namespace std::ranges::views; bool isPrime(unsigned number) { // Of course we use an algorithm and a range factory here as well! // Caution: mind the corner cases where number is 0, 1, or 2! return number >= 2 && std::ranges::none_of( iota(2u, static_cast(std::sqrt(number) + 1)), [number](unsigned divisor) { return number % divisor == 0; } ); } int main() { const unsigned num_primes{ 100 }; const unsigned per_line{ 10 }; unsigned count{}; for (auto prime : iota(2) | filter(&isPrime) | take(num_primes) | reverse) { std::cout << prime << ' '; if (++count % per_line == 0) std::cout << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_12/Soln20_12.cpp ================================================ // Exercise 20-12: exercising new range adaptors #include #include #include #include using namespace std::ranges::views; bool isPrime(unsigned number) { // Of course we use an algorithm and a range factory here as well! // Caution: mind the corner cases where number is 0, 1, or 2! return number >= 2 && std::ranges::none_of( iota(2u, static_cast(std::sqrt(number) + 1)), [number](unsigned divisor) { return number % divisor == 0; } ); } int main() { const unsigned max{ 1'000 }; const unsigned per_line{ 10 }; // As you no doubt deduced, drop_while() is not really applicable here, // but take_while() fits nicely! unsigned count{}; for (auto prime : iota(2u) | filter(&isPrime) | take_while([max](unsigned prime) { return prime < max; }) | reverse) { std::cout << prime << ' '; if (++count % per_line == 0) std::cout << std::endl; } } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_13/Soln20_13.cpp ================================================ // Exercise 20-13: more fun with algorithms and ranges #include #include #include #include #include #include #include #include using namespace std::ranges::views; // Type aliases using Words = std::vector; using WordCounts = std::map; // Function prototypes Words extractWords(std::string_view text, std::string_view separators = " ,.!?\"\n"); WordCounts countWords(const Words& words); void showWordCounts(const WordCounts& wordCounts); /* Below we list a number of possible solutions. We're sure there are plenty more variations possible... */ size_t maxWordLength(const WordCounts& wordCounts) { // Filter out words that occur only once + transform to word lengths using lambda expression auto frequentWordLengths { wordCounts | filter([](const auto wordCount) { return wordCount.second >= 2; }) | transform([](const auto wordCount) { return wordCount.first.length(); }) }; return std::ranges::empty(frequentWordLengths) ? 0 : *std::ranges::max_element(frequentWordLengths); } size_t maxWordLength_1(const WordCounts& wordCounts) { // Filter out words that occur only once, transform twice in a row with member pointers. auto frequentWordLengths { wordCounts | filter([](const auto wordCount) { return wordCount.second >= 2; }) | transform(&WordCounts::value_type::first) // Or, without alias: transform(&std::pair::first) | transform(&std::string_view::length) }; return std::ranges::empty(frequentWordLengths) ? 0 : *std::ranges::max_element(frequentWordLengths); } size_t maxWordLength_2(const WordCounts& wordCounts) { // Filter out words that occur only once, transform to words, project to lengths. auto frequentWords { wordCounts | filter([](const auto wordCount) { return wordCount.second >= 2; }) | transform([](const auto wordCount) { return wordCount.first; }) }; return std::ranges::empty(frequentWords) ? 0 : (*std::ranges::max_element(frequentWords, std::less<>{}, &std::string_view::length)).length(); } size_t maxWordLength_3(const WordCounts& wordCounts) { // Filter out words that occur only once auto frequentWordCounts { wordCounts | filter([](const auto wordCount) { return wordCount.second >= 2; }) }; // Project to obtain the words (the first element of the pairs in the map), // and compare their lengths using a lambda expression return std::ranges::empty(frequentWordCounts) ? 0 : std::ranges::max_element( frequentWordCounts, [](auto word1, auto word2) { return word1.length() < word2.length(); }, &WordCounts::value_type::first // Or, without alias: std::pair::first )->first.size(); } int main() { std::string text; // The string to count words in // Read a string from the keyboard std::cout << "Enter a string terminated by *:" << std::endl; getline(std::cin, text, '*'); const Words words{ extractWords(text) }; if (words.empty()) { std::cout << "No words in text." << std::endl; return 0; } const WordCounts wordCounts{ countWords(words) }; showWordCounts(wordCounts); } Words extractWords(std::string_view text, std::string_view separators) { Words words; size_t start{ text.find_first_not_of(separators) }; // Start 1st word size_t end{}; // Index for the end of a word while (start != std::string_view::npos) { end = text.find_first_of(separators, start + 1); // Find end separator if (end == std::string_view::npos) // End of text? end = text.length(); // Yes, so set to last+1 words.push_back(text.substr(start, end - start)); start = text.find_first_not_of(separators, end + 1); // Find next word } return words; } WordCounts countWords(const Words& words) { WordCounts result; for (auto& word : words) ++result[word]; return result; } void showWordCounts(const WordCounts& wordCounts) { const size_t field_width{ maxWordLength(wordCounts) + 1}; const size_t words_per_line{5}; size_t words_in_line{}; // Number of words in the current line char previous_initial{}; for (const auto& [word, count] : wordCounts) { if (count < 2) continue; // Skip words that appear only once // Output newline when initial letter changes or after 5 per line if ( (previous_initial && word[0] != previous_initial) || words_in_line++ == words_per_line) { words_in_line = 0; std::cout << std::endl; } // Output "word (count)", where word has a dynamic field width std::cout << std::format("{:>{}} ({:2})", word, field_width, count); previous_initial = word[0]; } std::cout << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 20/Soln20_14/Soln20_14.cpp ================================================ // Exercise 20-14: the hard nut #include #include #include #include #include using namespace std::ranges::views; bool isPrime(unsigned number) { return number >= 2 && std::ranges::none_of( iota(2u, static_cast(std::sqrt(number) + 1)), [number](unsigned divisor) { return number % divisor == 0; } ); } int main() { // This first version is one that compiled (with some help) on at least one compiler. // It uses one thing you have not encountered yet: std::ranges::views::common. // This range adaptor transforms a given range into a view where begin() and end() // return values of the same type, something which in general is not true for ranges. // Legacy algorithms, such as the iterator-pair-based std::copy() algorithm, // often expect iterator pairs to have the same type, though. auto view { std::ranges::istream_view(std::cin) | take_while([](int i) { return i >= 0; }) | transform([](int i) { return static_cast(i); }) | filter(isPrime) | transform([](unsigned i) { return "Yes, that is a prime!"; }) | common }; std::copy(view.begin(), view.end(), std::ostream_iterator(std::cout, "\n")); /* // Same as the previous one, but then without the legacy iterator-pair-based copy() // (and thus without the common range adaptor). // At the time we tried this, this did not compile yet in any compiler. auto view { std::ranges::istream_view(std::cin) | take_while([](int i) { return i >= 0; }) | transform([](int i) { return static_cast(i); }) | filter(isPrime) | transform([](unsigned i) { return "Yes, that is a prime!"; }) }; std::ranges::copy(view, std::ostream_iterator(std::cout, "\n")); */ /* // A variation that uses std::ranges::for_each(). // But, like the exercise says, this is cheating though, // as for_each() is just a poorly disguised range-based for loop, // and loops were not allowed! std::ranges::for_each( std::ranges::istream_view(std::cin) | take_while([](int i) { return i >= 0; }) | transform([](int i) { return static_cast(i); }) | filter(isPrime) | transform([](unsigned i) { return "Yes, that is a prime!"; }) , [](const auto& s) { std::cout << s << std::endl; } ); */ /* // A "clever" version where we print during a filter step auto view { std::ranges::istream_view(std::cin) | take_while([](int i) { return i >= 0; }) | transform([](int i) { return static_cast(i); }) | filter(isPrime) | filter([](unsigned i) { std::cout << "Yes, that is a prime!\n"; return false; }) }; view.begin(); // Try to find the begin of the view // Since the last step filters out all elements, though, // this will never find a begin, and eventually return view.end()... */ } ================================================ FILE: Exercises/NoModules/Chapter 21/Soln21_01/Soln21_01.cpp ================================================ // Exercise 21-1 Understanding compound and simple requirements #include // For the std::same_as<> and std::convertible_to<> concepts #include // For std::ranges::range<> concept #include // For the std::remove_cv<> type trait #include #include #include template concept BidirectionalIterator = true; // Feel free to further work out all requirements... // A compound requirement "{ expr } -> concept;" is equivalent to // the simple requirement "expr;", // combined with the nested requirement "requires concept;" template concept RandomAccessIterator = BidirectionalIterator && requires(const Iter i, const Iter j, Iter k, const int n) { { i - n }; { i + n }; { n + i }; requires std::same_as; requires std::same_as; requires std::same_as; { k += n }; { k -= n }; requires std::same_as; requires std::same_as; i[n]; requires std::same_as; i < j; i > j; i <= j; i >= j; requires std::convertible_to && std::convertible_to j), bool> && std::convertible_to && std::convertible_to= j), bool>; }; // To rewrite a compound requirement with noexcept you should know // that noexcept(expr) is a valid constant expression as well... template concept NoExceptDestructible = requires (T& value) { value.~T(); noexcept(value.~T()); }; template concept Character = std::same_as, char> || std::same_as, char8_t> || std::same_as, char16_t> || std::same_as, char32_t> || std::same_as, wchar_t>; // More of the same... template concept String = std::ranges::range && requires(S & s, const S & cs) { typename S::value_type; requires Character; cs.length(); requires std::integral; s[0]; cs[0]; requires std::same_as && std::convertible_to; s.data(); cs.data(); requires std::same_as && std::same_as; // ... }; static_assert(NoExceptDestructible); static_assert(NoExceptDestructible); static_assert(String); static_assert(!String>); static_assert(Character); static_assert(Character); static_assert(RandomAccessIterator::iterator>); static_assert(!RandomAccessIterator::iterator>); static_assert(RandomAccessIterator); int main() { } ================================================ FILE: Exercises/NoModules/Chapter 21/Soln21_02/Soln21_02.cpp ================================================ // Exercise 21-2 Writing a proper, modern algorithm is hard... // It involves adding support for projection, member pointers, // and iterator pairs where the end iterator (or, sentinel) // is of a different type than the begin iterator, // all with proper type constraints. #include #include // Standard concepts #include // Range factories and adaptors #include // For the std::identity and std::ranges::less functor classes, and std::invoke() #include // Iterator-based concepts, std::sentinel_for, and std::projected #include using namespace std::ranges::views; // The original function template which requires begin and end to be of the same type // Similar to the original iterator-pair based algorithms of , // only with type constraints. template Comparison> Iterator original_find_optimum(Iterator begin, Iterator end, Comparison compare) { if (begin == end) return end; Iterator optimum{ begin }; for (auto iter{ ++begin }; iter != end; ++iter) { if (compare(*iter, *optimum)) optimum = iter; } return optimum; } // Our improved function template supporting projection and differently-typed sentinels. // Appropriate default values were added as well. template Sentinel, typename Projection = std::identity, std::indirect_strict_weak_order> Comparison = std::ranges::less> Iterator find_optimum(Iterator begin, Sentinel end, Comparison compare = {}, Projection projection = {}) { if (begin == end) return begin; // Do not return end as before... Iterator optimum{ begin }; for (auto iter{ ++begin }; iter != end; ++iter) { // Note 1: std::invoke() is required to support comparison and projection through member pointers // Note 2: this re-invokes projection(*optimum) for each comparison, same as std::max_element() if (std::invoke(compare, std::invoke(projection, *iter), std::invoke(projection, *optimum))) optimum = iter; } return optimum; } /* A simple Box class to exercise our member-based comparison and projection facilities. It lacks an operator<(), so with classical algorithms you'd need lambdas to compare these Boxes... */ class Box { public: Box() : Box{ 1, 1, 1 } {}; Box(double length, double width, double height) : m_length{ length }, m_width{ width }, m_height{ height } {} double volume() const { return m_length * m_width * m_height; } double isSmallerThan(const Box& other) const { return volume() < other.volume(); } private: double m_length, m_width, m_height; }; int main() { std::vector boxes{ {2.0, 2.0, 3.0}, {1.0, 3.0, 2.0}, {1.0, 2.0, 1.0}, {2.0, 3.0, 3.0} }; // Step 1: create a view where begin() and end() are different. // The result of the take_while() range adapter has this property // (or, at least, it should have as per its specification in the Standard). auto first_boxes{ boxes | take_while([](const Box& box) { return box.volume() < 15; }) }; /* This therefore generally does not compile... */ //std::cout << "Volume of smallest box: " // << original_find_optimum(first_boxes.begin(), first_boxes.end(), // [](const Box& box1, const Box& box2) { return box1.isSmallerThan(box2); })->volume() // << std::endl; // Side-note: you can use the std::ranges::views::common to turn a range // where begin and end have a different type into a view where they do. auto common_boxes{ first_boxes | common }; std::cout << "Volume of smallest box: " << original_find_optimum(common_boxes.begin(), common_boxes.end(), [](const Box& box1, const Box& box2) { return box1.isSmallerThan(box2); })->volume() << std::endl; // Step 2: Show off our shiny new, modernised version of find_optimum() // First off: no need for the common adapter! std::cout << "Volume of largest box: " << find_optimum(first_boxes.begin(), first_boxes.end(), [](const Box& box1, const Box& box2) { return !box1.isSmallerThan(box2); })->volume() << std::endl; // Other cool features of our modern find_optimum() version include projection // and the support for member function pointers. So no more lambdas required here! // Use a member-function pointer for the comparison function std::cout << "Volume of smallest box: " << find_optimum(first_boxes.begin(), first_boxes.end(), &Box::isSmallerThan)->volume() << std::endl; // Use a member-function pointer for the new projection functionality std::cout << "Volume of largest box: " << find_optimum(first_boxes.begin(), first_boxes.end(), std::greater<>{}, &Box::volume)->volume() << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 21/Soln21_03/Soln21_03.cpp ================================================ // Exercise 21-3 #include #include #include #include #include /* Not requested, but just for fun, here is a concept that prescribes a complete set of operations one would require to take an average */ template concept Averagable = requires (const T x, const T y, T z, const int i) { { x + y } -> std::same_as; { x - y } -> std::same_as; { z += y } -> std::same_as; { z -= y } -> std::same_as; { x / i } -> std::same_as; { x * i } -> std::same_as; { z /= i } -> std::same_as; { z *= i } -> std::same_as; }; // By default, take the N / 2'th element // (Note: this template covers both the case if T would not be Averageble, // and the case where N % 2 == 1). template auto& medianOfSorted(std::span span) { static_assert(N != 0, "The median of an empty span is not defined"); return span[N / 2]; } template requires (N % 2 == 0) auto medianOfSorted(std::span span) { static_assert(N != 0, "The median of an empty span is not defined"); return (span[N / 2 - 1] + span[N / 2]) / 2; } int main() { std::array values_odd{ 1, 2, 3, 4, 5, 6, 7 }; std::cout << medianOfSorted(std::span{ values_odd }) << std::endl; std::array values_even{ 1., 2., 3., 4., 5., 6. }; std::cout << medianOfSorted(std::span{ values_even }) << std::endl; std::string strings_odd[] { "1", "2", "3", "4", "5" }; std::cout << medianOfSorted(std::span{ strings_odd }) << std::endl; std::string strings_even[] { "1", "2", "3", "4", "5", "6" }; std::cout << medianOfSorted(std::span{ strings_even }) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 21/Soln21_04/Soln21_04.cpp ================================================ // Exercise 21-4 Generalising medianOfSorted() #include #include #include #include #include #include #include #include #include #include /* Apologies. When we wrote the exercise, we misread what a std::sized_range() was. std::sized_range() is no concept for statically fixed-size ranges, but for ranges for which std::ranges::size() may be invoked *at runtime*. So this solution presents the next best thing: a medianOfSorted() that works for any range (not just fixed-size ranges), with appropriate type constraints. Key point is that underlying, range-based algorithms still very much work with iterators. Most requirements use Standard Library concepts in the std::ranges namespace, and the function implementations use Standard Library functions in that same namespace. */ /* Not requested, but just for fun, here is a concept that prescribes a complete set of operations one would require to take an average */ template concept Averagable = requires (const T x, const T y, T z, const int i) { { x + y } -> std::same_as; { x - y } -> std::same_as; { z += y } -> std::same_as; { z -= y } -> std::same_as; { x / i } -> std::same_as; { x * i } -> std::same_as; { z /= i } -> std::same_as; { z *= i } -> std::same_as; }; // By default, take the N/2'th element template // Requires being able to determine the range's size using std::ranges::size() requires std::ranges::input_range // Requires being able to dereference an iterator over the range. auto& medianOfSorted(Range&& range) { auto iter{ std::ranges::begin(range) }; // Supported for every range std::ranges::advance(iter, std::ranges::size(range) / 2); // Advances in constant time for random-access ranges, linearly otherwise. return *iter; // Only possible with input iterators... } // When we can take an average, check the size // (dynamically, not statically as was the original intent) // to decide whether we need to do so... template requires std::ranges::forward_range // More strict than before: we need the multi-pass guarantee && Averagable> auto medianOfSorted(Range&& range) { const auto N{ std::ranges::size(range) }; auto iter{ std::ranges::begin(range) }; if (N % 2 == 0) { std::ranges::advance(iter, N / 2 - 1); const auto& value1{ *iter }; // Reference remains valid because iter is a forward iterator! std::ranges::advance(iter, 1); return (value1 + *iter) / 2; } else { std::ranges::advance(iter, N / 2); return *iter; } } int main() { std::array values_odd{ 1, 2, 3, 4, 5, 6, 7 }; std::cout << medianOfSorted(values_odd) << std::endl; std::array values_even{ 1., 2., 3., 4., 5., 6. }; std::cout << medianOfSorted(values_even) << std::endl; std::string strings_odd[] { "1", "2", "3", "4", "5" }; std::cout << medianOfSorted(strings_odd) << std::endl; std::string strings_even[] { "1", "2", "3", "4", "5", "6" }; std::cout << medianOfSorted(std::span{ strings_even }) << std::endl; std::vector dynamically_sized{ 1.f, 2.f, 3.f, 4.f }; std::cout << medianOfSorted(dynamically_sized) << std::endl; std::list non_random_access{ 4.f, 3.f, 2.f, 1.f, 0.f }; std::cout << medianOfSorted(non_random_access) << std::endl; std::forward_list non_sized_ranged{ 123, 456, 789 }; //std::cout << medianOfSorted(non_sized_ranged) << std::endl; /* Error: not sized! */ } ================================================ FILE: Exercises/NoModules/Chapter 21/Soln21_05/Soln21_05.cpp ================================================ // Exercise 21-5 Recreating std::advance() using constraint-based specialisation #include #include #include #include #include template auto my_advance(Iter iter, std::iter_difference_t n) { return iter + n; } template auto my_advance(Iter iter, std::iter_difference_t n) { while (n > 0) { ++iter; --n; } while (n < 0) { --iter; ++n; } return iter; } template auto my_advance(Iter iter, std::iter_difference_t n) { while (n > 0) { ++iter; --n; } return iter; } int main() { // Random-access iterators: std::vector v{ 1, 2, 3, 4, 5 }; std::cout << *my_advance(v.begin(), 3) << std::endl; // Bidirectional iterators: std::list l{ 1, 2, 3, 4, 5 }; std::cout << *my_advance(l.end(), -3) << std::endl; // Forward iterators: std::forward_list f{ 1, 2, 3, 4, 5 }; std::cout << *my_advance(f.begin(), 3) << std::endl; } ================================================ FILE: Exercises/NoModules/Chapter 21/Soln21_06/Array.h ================================================ #ifndef ARRAY_H #define ARRAY_H #include // For standard exception types #include // For std::to_string() #include // For std::as_const() #include /* // Original template based on type traits and some mild template meta-programming template std::conditional_t, T&&, const T&> move_assign_if_noexcept(T& x) noexcept { return std::move(x); } */ // A straightforward concept with a single compound requirement template concept NoThrowMoveAssignable = requires (T left, T right) { { left = std::move(right) } noexcept -> std::same_as; }; // By default: return lvalue reference (will result in x being copied) // (Note: this default move_assign_if_noexcept() template in itself is noexcept, // the copy assignment that generally follows is not...) auto& move_assign_if_noexcept(auto& x) noexcept { return x; } // Specialization that moves if possible without throwing // Uses abbreviated template syntax with a type constraint on auto) auto&& move_assign_if_noexcept(NoThrowMoveAssignable auto& x) noexcept { return std::move(x); } template class Array { public: Array(); // Default constructor explicit Array(size_t size); // Constructor ~Array(); // Destructor Array(const Array& array); // Copy constructor Array(Array&& array) noexcept; // Move constructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs) noexcept; // Move assignment operator void swap(Array& other) noexcept; // Swap member function T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return m_size; } // Accessor for m_size void push_back(T element); // Copy or move element to the back of the array private: T* m_elements; // Array of type T size_t m_size; // Number of array elements }; // Forwarding default constructor template template Array::Array() : Array{0} {} // Constructor template template Array::Array(size_t size) : m_elements {new T[size] {}}, m_size {size} {} // Copy constructor template template Array::Array(const Array& array) : Array{array.m_size} { std::cout << "Array of " << m_size << " elements copied" << std::endl; for (size_t i {}; i < m_size; ++i) m_elements[i] = array.m_elements[i]; } // Move constructor template template Array::Array(Array&& moved) noexcept : m_size{moved.m_size}, m_elements{moved.m_elements} { std::cout << "Array of " << m_size << " elements moved" << std::endl; moved.m_elements = nullptr; // Otherwise destructor of moved would delete[] m_elements! } // Destructor template template Array::~Array() { delete[] m_elements; } // const subscript operator template template const T& Array::operator[](size_t index) const { if (index >= m_size) throw std::out_of_range {"Index too large: " + std::to_string(index)}; return m_elements[index]; } // Non-const subscript operator template in terms of const one // Uses the 'const-and-back-again' idiom template T& Array::operator[](size_t index) { return const_cast(std::as_const(*this)[index]); } // Template for exception-safe copy assignment operators // (expressed in terms of copy constructor and swap member) template Array& Array::operator=(const Array& rhs) { Array copy{ rhs }; // Copy... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; } // Move assignment operator template template Array& Array::operator=(Array&& rhs) noexcept { std::cout << "Array of " << rhs.m_size << " elements moved (assignment)" << std::endl; if (&rhs != this) // prevent trouble with self-assignments { delete[] m_elements; // delete[] all existing elements m_elements = rhs.m_elements; // copy the elements pointer and the size m_size = rhs.m_size; rhs.m_elements = nullptr; // make sure rhs does not delete[] m_elements } return *this; // return lhs } // Swap member function template template void Array::swap(Array& other) noexcept { std::swap(m_elements, other.m_elements); // Swap two pointers std::swap(m_size, other.m_size); // Swap the sizes } // Swap non-member function template (optional) template void swap(Array& one, Array& other) noexcept { one.swap(other); // Forward to public member function } // push_back() overload for lvalue references template void Array::push_back(T element) // Pass by value (copy of lvalue, or moved rvalue!) { Array newArray{m_size + 1}; // Allocate a larger Array<> for (size_t i {}; i < m_size; ++i) // Move existing elements (copy if not noexcept)... newArray[i] = move_assign_if_noexcept(m_elements[i]); newArray[m_size] = move_assign_if_noexcept(element); // Move (or copy) the new one... swap(newArray); // ... and swap! } #endif ================================================ FILE: Exercises/NoModules/Chapter 21/Soln21_06/Soln21_06.cpp ================================================ // Exercise 21-6 Simplifying move_assign_if_noexcept using concepts (see Array.h) #include "Array.h" #include // Construct an Array<> of a given size, filled with some arbitrary string data Array buildStringArray(const size_t size) { Array result{ size }; for (size_t i {}; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array> array_of_arrays; Array array{ buildStringArray(1'000) }; array_of_arrays.push_back(array); // Push an lvalue array.push_back("One more for good measure"); std::cout << std::endl; array_of_arrays.push_back(std::move(array)); // Push an rvalue } ================================================ FILE: Exercises/README.md ================================================ # Exercise Solutions This directory contains the solutions to all exercises of [*Beginning C++20*](https://www.apress.com/9781484258835) by Ivor Horton and Peter Van Weert (Apress, 2020). The [Modules](Modules) directory contains solutions that use C++20 modules. If your compiler does not support C++20 modules yet, though (which at the time of writing is likely), you may be better off looking at the solutions in the [NoModules](NoModules) directory instead. Consult the [Workarounds](../Workarounds) directory on how to work around compilation issues with other C++20 language and library features that your compiler may not (fully) [support](https://en.cppreference.com/w/cpp/compiler_support) yet. ================================================ FILE: LICENSE.txt ================================================ Freeware License, some rights reserved Copyright (c) 2020 Ivor Horton and Peter Van Weert Permission is hereby granted, free of charge, to anyone obtaining a copy of this software and associated documentation files (the "Software"), to work with the Software within the limits of freeware distribution and fair use. This includes the rights to use, copy, and modify the Software for personal use. Users are also allowed and encouraged to submit corrections and modifications to the Software for the benefit of other users. It is not allowed to reuse, modify, or redistribute the Software for commercial use in any way, or for a user’s educational materials such as books or blog articles without prior permission from the copyright holder. The above copyright notice and this permission notice need to 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 OR APRESS 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: README.md ================================================ # Apress Source Code This repository accompanies [*Beginning C++20*](https://www.apress.com/9781484258835) by Ivor Horton and Peter Van Weert (Apress, 2020). [comment]: #cover ![Cover image](9781484258835.jpg) Download the files as a zip using the green button, or clone the repository to your machine using Git. To make sure you also clone the 3rd party submodules in the [Workarounds](Workarounds) directory, we recommend using the following Git command: git clone --recursive https://github.com/Apress/beginning-cpp20.git ## C++20 Compiler Support At the time the book was completed, not a single compiler [supported](https://en.cppreference.com/w/cpp/compiler_support) all C++20 features yet. We recommend you consult the [Workarounds](Workarounds) section on how to work around any compilation issues you may experience. ## Contributions See the file [Contributing.md](Contributing.md) for more information on how you can contribute to this repository. ================================================ FILE: Workarounds/README.md ================================================ # Workarounds At the time the book was completed, not a single compiler [supported](https://en.cppreference.com/w/cpp/compiler_support) all C++20 features yet. Features that were used in the book, and for which you therefore need a workaround to compile and run the example / exercise code include - [Modules](#modules) - [std::format()](#format) - [Abbreviated Function Template Syntax](#abbreviated) - [Ranges](#ranges) ## Modules Most source code, most notably that in the second half of the book, uses C++20's modules. Unfortunately, [support](https://en.cppreference.com/w/cpp/compiler_support) for this feature may still be lacking in your compiler / build system. For the smaller examples it is just a matter of replacing the `import` declarations with `#include` directives (see [Appendix A](../Appendix.pdf)), but for examples comprising multiple files working around these limitations can be more work. For all examples and exercises that are not explicitly about modules, we therefore made an equivalent version available that does not use modules. You can find these in the `NoModules` directories of this source repository. ## std::format() It's not until recently that some major compilers started adding [support](https://en.cppreference.com/w/cpp/compiler_support) for the C++20 `` module. This module provides safe, elegant, and efficient text formatting, primeraly in the form of the `std::format()` function, and is heavily used throughout the C++20 edition of the book. If your compiler is too old to [support](https://en.cppreference.com/w/cpp/compiler_support) `` natively, we recommend the [`{fmt}`](https://fmt.dev/) library as a workaround. It is a free and open source implementation of a superset of the now standardised `` module. Steps: 1. Download the `fmt` source code - If you downloaded the book's source code as a zip file, you can download that of `fmt` as well from https://github.com/fmtlib/fmt, and extract it in a `fmt` subdirectory of this `Workarounds` directory - If you cloned using Git, but without specifying `--recursive`, you can execute the following Git commands in the root directory of your local clone: git submodule init git submodule update 2. Add the `Workarounds` directory to the additional include paths of your compiler - For clang and gcc, you do this using the `-I` / `--include-directory` command line flags - For Visual Studio, you right-click your project, select Properties, and add the `Workarounds` directory under Configuration Properties ► C/C++ ► General ► Additional Include Directories ► Edit... as illustrated in the following image 3. If all went well, `#include ` will then use the [`format`](format) header in this directory, which injects `{fmt}`'s functionality into the `std` namespace (technically not allowed, we know...), and all `std::format()` statements will work as expected. Note: this workaround works for `#include ` directives (see [Appendix A](../Appendix.pdf)); but at the time of writing we weren't having much luck getting this to work with C++20's `import` declarations. If you're experiencing similar problems, we therefore recommend you mostly use the `NoModules` versions of the source code. If you do want to experiment with modules and `std::format()` is getting in the way of that, you can, for instance, add the following to your source files namespace std { export std::string format(auto&&...) { return {}; } } Your formatted output will not work, but then at least you can try your luck getting the remainder of your modules to compile. ## Abbreviated Function Template Syntax If your compiler does not [support](https://en.cppreference.com/w/cpp/compiler_support) C++20's abbreviated function template syntax yet, the solution is somewhat obvious: use the non-abbreviated syntax. That is, instead of namespace math { auto square(const auto& x) { return x * x; } } you use namespace math { template auto square(const T& x) { return x * x; } } (`auto` return type deduction should work with any recent compiler.) ## Ranges Similar to ``, it took some time for all Standard Library implementations to add [support](https://en.cppreference.com/w/cpp/compiler_support) for C++20 ranges (and if they had support, not all range algorithms were working as they should). If you experience issues, you can try the excellent [range-v3](https://github.com/ericniebler/range-v3) library of Eric Niebler instead. We added the [header file](ranges) we used for this to this directory. The steps you need to get this to work are similar to those we detailed for [`std::format()`](#format), except that library needs to be compiled as well, and the corresponding binaries added as input to your linker. You can consult the [range-v3](https://github.com/ericniebler/range-v3#supported-compilers) on how to build this library, and your compiler's documentation on how to link with the resulting binaries. ================================================ FILE: Workarounds/format ================================================ // Compatibility include for compilers that do not support the module yet // (falls back to the reference implementation from https://fmt.dev) // See Appendix A for the preprocessing directives and macros we use here. #ifdef __cpp_lib_format #error Remove Workarounds/format from your include directories (use Standard Library header instead) #else #define FMT_HEADER_ONLY #include "fmt/include/fmt/format.h" // Technically you are not allowed to add to the std namespace, // but necessity knows no law... namespace std { using namespace fmt; } #endif ================================================ FILE: Workarounds/ranges ================================================ // Compatibility include for compilers that do not support the module yet // (falls back to the reference implementation from https://github.com/ericniebler/range-v3) // See Appendix A for the preprocessing directives and macros we use here. #ifdef __cpp_lib_ranges #error Remove/rename this compatibility header (use Standard Library header instead) #else #include "range-v3/include/range/v3/all.hpp" // Technically you are not allowed to add to the std namespace, // but necessity knows no law... namespace std::ranges { using namespace ::ranges::cpp20; using ::ranges::empty; using ::ranges::begin; using ::ranges::end; using ::ranges::size; template auto istream_view(basic_istream& s) { return basic_istream_view(s); } } namespace std::views { using namespace ::ranges::cpp20::views; } #endif ================================================ FILE: errata.md ================================================ # Errata for *Beginning C++20* On **page 21** [technical accuracy]: > lowercase letters have the sixth bit as 0, and uppercase letters have the sixth bit as 1 should be > lowercase letters have the sixth bit as 1, and uppercase letters have the sixth bit as 0 *** On **page 53** [typographic errors]: In the description of underflow for unsigned integers, the numbers are missing superscript needed to show exponentiation. Also, the result of 232 - 2 is incorrect; the correct result is 4294967294. | Original | Corrected | | ------------------------------------------ | ------------------------------------------------------| | -1 becomes 232 - 1 or 4294967295 | -1 becomes 232 - 1 or 4294967295 | | -2 becomes 232 - 2 or 4294967293 | -2 becomes 232 - 1 or 4294967294 | | -10 indeed becomes 232 - 10, or 4294967286 | -10 indeed becomes 232 - 10, or 4294967286 | *** On **page 81** [wrong bitwise xor result]: > font ^ bold 0000 0110 0010 1100 should be > font ^ bold 0000 0110 0**1**10 1100 The 7th bit of the bitwise exclusive or must be **1**, not 0. *** On **page 100** [wrong sign]: > std::cout << std::format("The value of the expression {} == {} is {}\n", first, second, first < second); should be > std::cout << std::format("The value of the expression {} == {} is {}\n", first, second, first **==** second); Instead of the expression first < second it must be **first == second** for the 3rd bracket. *** On **page 277** in **figure 8-1** [missing start value for result]: > double result; should be > double result { **1.0** }; Result must start with **1.0**, otherwise the power function will not work properly. *** On **page 323** [wrong comment]: > // Yes, so set to end of text should be > // **No** , so set to end of text This if refers to when you did not find a separator, hence **no** is needed, not yes. *** On **page xx** [Summary of error]: Details of error here. Highlight key pieces in **bold**.