1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243
|
:last-update-label!:
:icons: font
:prewrap!:
:docinfo: shared
:stylesheet: zajo-dark.css
:source-highlighter: rouge
ifdef::backend-pdf[]
= LEAF
endif::[]
ifndef::backend-pdf[]
= LEAFpass:[<div style="z-index: 3; bottom:-16px; right:4px; position:fixed"><input width="32" height="32" type="image" alt="Skin" src="./skin.png" onclick="this.blur();switch_style();return false;"/></div>]
endif::[]
Lightweight Error Augmentation Framework written in {CPP}11 | Emil Dotchevski
ifndef::backend-pdf[]
:toc: left
:toclevels: 3
:toc-title:
[.text-right]
https://github.com/boostorg/leaf[GitHub] | https://boostorg.github.io/leaf/leaf.pdf[PDF]
endif::[]
[abstract]
== Abstract
Boost LEAF is a lightweight error handling library for {CPP}11. Features:
====
* Portable single-header format, no dependencies.
* Tiny code size when configured for embedded development.
* No dynamic memory allocations, even with very large payloads.
* Deterministic unbiased efficiency on the "happy" path and the "sad" path.
* Error objects are handled in constant time, independent of call stack depth.
* Can be used with or without exception handling.
====
ifndef::backend-pdf[]
[grid=none, frame=none]
|====
| <<tutorial>> \| <<synopsis>> \| https://github.com/boostorg/leaf/blob/master/doc/whitepaper.md[Whitepaper] \| https://github.com/boostorg/leaf/blob/master/benchmark/benchmark.md[Benchmark] >| Reference: <<functions,Functions>> \| <<types,Types>> \| <<predicates,Predicates>> \| <<traits,Traits>> \| <<macros,Macros>>
|====
endif::[]
[[support]]
== Support
* https://Cpplang.slack.com[cpplang on Slack] (use the `#boost` channel)
* https://lists.boost.org/mailman/listinfo.cgi/boost-users[Boost Users Mailing List]
* https://lists.boost.org/mailman/listinfo.cgi/boost[Boost Developers Mailing List]
* https://github.com/boostorg/leaf/issues[Report issues] on GitHub
[[distribution]]
== Distribution
LEAF is distributed under the http://www.boost.org/LICENSE_1_0.txt[Boost Software License, Version 1.0].
There are three distribution channels:
* LEAF is included in official https://www.boost.org/[Boost] releases (starting with Boost 1.75), and therefore available via most package managers.
* The source code is hosted on https://github.com/boostorg/leaf[GitHub].
* For maximum portability, the latest LEAF release is also available in single-header format: simply download link:https://raw.githubusercontent.com/boostorg/leaf/gh-pages/leaf.hpp[leaf.hpp] (direct download link).
NOTE: LEAF does not depend on Boost or other libraries.
[[tutorial]]
== Tutorial
What is a failure? It is simply the inability of a function to return a valid result, instead producing an error object describing the reason for the failure.
A typical design is to return a variant type, e.g. `result<T, E>`. Internally, such variant types must store a discriminant (in this case a boolean) to indicate whether the object holds a `T` or an `E`.
The design of LEAF is informed by the observation that the immediate caller must have access to the discriminant in order to determine the availability of a valid `T`, but otherwise it rarely needs to access the `E`. The error object is only needed once an error handling scope is reached.
Therefore what would have been a `result<T, E>` becomes `result<T>`, which stores the discriminant and (optionally) a `T`, while the `E` is communicated directly to the error handling scope where it is needed.
The benefit of this decomposition is that `result<T>` becomes extremely lightweight, as it is not coupled with error types; further, error objects are communicated in constant time (independent of the call stack depth). Even very large objects are handled efficiently without dynamic memory allocation.
=== Reporting Errors
A function that reports an error is pretty straight-forward:
[source,c++]
----
enum class err1 { e1, e2, e3 };
leaf::result<T> f()
{
....
if( error_detected )
return leaf::new_error( err1::e1 ); // Pass an error object of any type
// Produce and return a T.
}
----
[.text-right]
<<result>> | <<new_error>>
'''
[[checking_for_errors]]
=== Checking for Errors
Checking for errors communicated by a `leaf::result<T>` works as expected:
[source,c++]
----
leaf::result<U> g()
{
leaf::result<T> r = f();
if( !r )
return r.error();
T const & v = r.value();
// Use v to produce a valid U
}
----
[.text-right]
<<result>>
TIP: The the result of `r.error()` is compatible with any instance of the `leaf::result` template. In the example above, note that `g` returns a `leaf::result<U>`, while `r` is of type `leaf::result<T>`.
The boilerplate `if` statement can be avoided using `BOOST_LEAF_AUTO`:
[source,c++]
----
leaf::result<U> g()
{
BOOST_LEAF_AUTO(v, f()); // Bail out on error
// Use v to produce a valid U
}
----
[.text-right]
<<BOOST_LEAF_AUTO>>
`BOOST_LEAF_AUTO` can not be used with `void` results; in that case, to avoid the boilerplate `if` statement, use `BOOST_LEAF_CHECK`:
[source,c++]
----
leaf::result<void> f();
leaf::result<int> g()
{
BOOST_LEAF_CHECK(f()); // Bail out on error
return 42;
}
----
[.text-right]
<<BOOST_LEAF_CHECK>>
On implementations that define `pass:[__GNUC__]` (e.g. GCC/clang), the `BOOST_LEAF_CHECK` macro definition takes advantage of https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html[GNU C statement expressions]. In this case, in addition to its portable usage with `result<void>`, `BOOST_LEAF_CHECK` can be used in expressions with non-`void` result types:
[source,c++]
----
leaf::result<int> f();
float g(int x);
leaf::result<float> t()
{
return g( BOOST_LEAF_CHECK(f()) );
}
----
The following is the portable alternative:
[source,c++]
----
leaf::result<float> t()
{
BOOST_LEAF_AUTO(x, f());
return g(x);
}
----
'''
[[tutorial-error_handling]]
=== Error Handling
Error handling scopes must use a special syntax to indicate that they need to access error objects. The following excerpt attempts several operations and handles errors of type `err1`:
[source,c++]
----
leaf::result<U> r = leaf::try_handle_some(
[]() -> leaf::result<U>
{
BOOST_LEAF_AUTO(v1, f1());
BOOST_LEAF_AUTO(v2, f2());
return g(v1, v2);
},
[]( err1 e ) -> leaf::result<U>
{
if( e == err1::e1 )
.... // Handle err1::e1
else
.... // Handle any other err1 value
} );
----
[.text-right]
<<try_handle_some>> | <<result>> | <<BOOST_LEAF_AUTO>>
The first lambda passed to `try_handle_some` is executed first; it attempts to produce a `result<U>`, but it may fail.
The second lambda is an error handler: it will be called iff the first lambda fails and an error object of type `err1` was communicated to LEAF. That object is stored on the stack, local to the `try_handle_some` function (LEAF knows to allocate this storage because we gave it an error handler that takes an `err1`). Error handlers passed to `leaf::try_handle_some` can return a valid `leaf::result<U>` but are allowed to fail.
It is possible for an error handler to specify that it can only deal with some values of a given error type:
[source,c++]
----
leaf::result<U> r = leaf::try_handle_some(
[]() -> leaf::result<U>
{
BOOST_LEAF_AUTO(v1, f1());
BOOST_LEAF_AUTO(v2, f2());
return g(v1. v2);
},
[]( leaf::match<err1, err1::e1, err1::e3> ) -> leaf::result<U>
{
// Handle err::e1 or err1::e3
},
[]( err1 e ) -> leaf::result<U>
{
// Handle any other err1 value
} );
----
[.text-right]
<<try_handle_some>> | <<result>> | <<BOOST_LEAF_AUTO>> | <<match>>
LEAF considers the provided error handlers in order, and calls the first one for which it can supply arguments, based on the error objects currently being communicated. Above:
* The first error handler uses the predicate `leaf::match` to specify that it should only be considered if an error object of type `err1` is available, and its value is either `err1::e1` or `err1::e3`.
* Otherwise the second error handler will be called if an error object of type `err1` is available, regardless of its value.
* Otherwise `leaf::try_handle_some` fails.
It is possible for an error handler to conditionally leave the current failure unhandled:
[source,c++]
----
leaf::result<U> r = leaf::try_handle_some(
[]() -> leaf::result<U>
{
BOOST_LEAF_AUTO(v1, f1());
BOOST_LEAF_AUTO(v2, f2());
return g(v1. v2);
},
[]( err1 e, leaf::error_info const & ei ) -> leaf::result<U>
{
if( <<condition>> )
return valid_U;
else
return ei.error();
} );
----
[.text-right]
<<try_handle_some>> | <<result>> | <<BOOST_LEAF_AUTO>> | <<error_info>>
Any error handler can take an argument of type `leaf::error_info const &` to get access to generic information about the error being handled; in this case we use the `error` member function, which returns the unique <<error_id>> of the current error; we use it to initialize the returned `leaf::result`, effectively propagating the current error out of `try_handle_some`.
TIP: If we wanted to signal a new error (rather than propagating the current error), in the `return` statement we would invoke the `leaf::new_error` function.
If we want to ensure that all possible failures are handled, we use `leaf::try_handle_all` instead of `leaf::try_handle_some`:
[source,c++]
----
U r = leaf::try_handle_all(
[]() -> leaf::result<U>
{
BOOST_LEAF_AUTO(v1, f1());
BOOST_LEAF_AUTO(v2, f2());
return g(v1. v2);
},
[]( leaf::match<err1, err1::e1> ) -> U
{
// Handle err::e1
},
[]( err1 e ) -> U
{
// Handle any other err1 value
},
[]() -> U
{
// Handle any other failure
} );
----
[.text-right]
<<try_handle_all>>
The `leaf::try_handle_all` function enforces at compile time that at least one of the supplied error handlers takes no arguments (and therefore is able to handle any failure). In addition, all error handlers are forced to return a valid `U`, rather than a `leaf::result<U>`, so that `leaf::try_handle_all` is guaranteed to succeed, always.
'''
=== Working with Different Error Types
It is of course possible to provide different handlers for different error types:
[source,c++]
----
enum class err1 { e1, e2, e3 };
enum class err2 { e1, e2 };
....
leaf::result<U> r = leaf::try_handle_some(
[]() -> leaf::result<U>
{
BOOST_LEAF_AUTO(v1, f1());
BOOST_LEAF_AUTO(v2, f2());
return g(v1, v2);
},
[]( err1 e ) -> leaf::result<U>
{
// Handle errors of type `err1`.
},
[]( err2 e ) -> leaf::result<U>
{
// Handle errors of type `err2`.
} );
----
[.text-right]
<<try_handle_some>> | <<result>> | <<BOOST_LEAF_AUTO>>
Recall that error handlers are always considered in order:
* The first error handler will be used if an error object of type `err1` is available;
* otherwise, the second error handler will be used if an error object of type `err2` is available;
* otherwise, `leaf::try_handle_some` fails.
'''
=== Working with Multiple Error Objects
The `leaf::new_error` function can be invoked with multiple error objects, for example to communicate an error code and the relevant file name:
[source,c++]
----
enum class io_error { open_error, read_error, write_error };
struct e_file_name { std::string value; }
leaf::result<File> open_file( char const * name )
{
....
if( open_failed )
return leaf::new_error(io_error::open_error, e_file_name {name});
....
}
----
[.text-right]
<<result>> | <<new_error>>
Similarly, error handlers may take multiple error objects as arguments:
[source,c++]
----
leaf::result<U> r = leaf::try_handle_some(
[]() -> leaf::result<U>
{
BOOST_LEAF_AUTO(f, open_file(fn));
....
},
[]( io_error ec, e_file_name fn ) -> leaf::result<U>
{
// Handle I/O errors when a file name is available.
},
[]( io_error ec ) -> leaf::result<U>
{
// Handle I/O errors when no file name is available.
} );
----
[.text-right]
<<try_handle_some>> | <<result>> | <<BOOST_LEAF_AUTO>>
Once again, error handlers are considered in order:
* The first error handler will be used if an error object of type `io_error` _and_ and error_object of type `e_file_name` are available;
* otherwise, the second error handler will be used if an error object of type `io_error` is avaliable;
* otherwise, `leaf_try_handle_some` fails.
An alternative way to write the above is to provide a single error handler that takes the `e_file_name` argument as a pointer:
[source,c++]
----
leaf::result<U> r = leaf::try_handle_some(
[]() -> leaf::result<U>
{
BOOST_LEAF_AUTO(f, open_file(fn));
....
},
[]( io_error ec, e_file_name const * fn ) -> leaf::result<U>
{
if( fn )
.... // Handle I/O errors when a file name is available.
else
.... // Handle I/O errors when no file name is available.
} );
----
[.text-right]
<<try_handle_some>> | <<result>> | <<BOOST_LEAF_AUTO>>
An error handler is never dropped for lack of error objects of types which the handler takes as pointers; in this case LEAF simply passes `0` for these arguments.
TIP: Error handlers can take arguments by value, by (`const`) reference or as a (`const`) pointer. It the latter case, changes to the error object state will be propagated up the call stack if the failure is not handled.
[[tutorial-augmenting_errors]]
=== Augmenting Errors
Let's say we have a function `parse_line` which could fail due to an `io_error` or a `parse_error`:
[source,c++]
----
enum class io_error { open_error, read_error, write_error };
enum class parse_error { bad_syntax, bad_range };
leaf::result<int> parse_line( FILE * f );
----
The `leaf::on_error` function can be used to automatically associate additional error objects with any failure that is "in flight":
[source,c++]
----
struct e_line { int value; };
leaf::result<void> process_file( FILE * f )
{
for( int current_line = 1; current_line != 10; ++current_line )
{
auto load = leaf::on_error( e_line {current_line} );
BOOST_LEAF_AUTO(v, parse_line(f));
// use v
}
}
----
[.text-right]
<<on_error>> | <<BOOST_LEAF_AUTO>>
Because `process_file` does not handle errors, it remains neutral to failures, except to attach the `current_line` if something goes wrong. The object returned by `on_error` holds a copy of the `current_line` wrapped in `struct e_line`. If `parse_line` succeeds, the `e_line` object is simply discarded; but if it fails, the `e_line` object will be automatically "attached" to the failure.
Such failures can then be handled like so:
[source,c++]
----
leaf::result<void> r = leaf::try_handle_some(
[&]() -> leaf::result<void>
{
BOOST_LEAF_CHECK( process_file(f) );
},
[]( parse_error e, e_line current_line )
{
std::cerr << "Parse error at line " << current_line.value << std::endl;
},
[]( io_error e, e_line current_line )
{
std::cerr << "I/O error at line " << current_line.value << std::endl;
},
[]( io_error e )
{
std::cerr << "I/O error" << std::endl;
} );
----
[.text-right]
<<try_handle_some>> | <<BOOST_LEAF_CHECK>>
The following is equivalent, and perhaps simpler:
[source,c++]
----
leaf::result<void> r = leaf::try_handle_some(
[]() -> leaf::result<void>
{
BOOST_LEAF_CHECK( process_file(f) );
},
[]( parse_error e, e_line current_line )
{
std::cerr << "Parse error at line " << current_line.value << std::endl;
},
[]( io_error e, e_line const * current_line )
{
std::cerr << "Parse error";
if( current_line )
std::cerr << " at line " << current_line->value;
std::cerr << std::endl;
} );
----
'''
[[tutorial-exception_handling]]
=== Exception Handling
What happens if an operation throws an exception? Not to worry, both `try_handle_some` and `try_handle_all` catch exceptions and are able to pass them to any compatible error handler:
[source,c++]
----
leaf::result<void> r = leaf::try_handle_some(
[]() -> leaf::result<void>
{
BOOST_LEAF_CHECK( process_file(f) );
},
[]( std::bad_alloc const & )
{
std::cerr << "Out of memory!" << std::endl;
},
[]( parse_error e, e_line l )
{
std::cerr << "Parse error at line " << l.value << std::endl;
},
[]( io_error e, e_line const * l )
{
std::cerr << "Parse error";
if( l )
std::cerr << " at line " << l.value;
std::cerr << std::endl;
} );
----
[.text-right]
<<try_handle_some>> | <<result>> | <<BOOST_LEAF_CHECK>>
Above, we have simply added an error handler that takes a `std::bad_alloc`, and everything "just works" as expected: LEAF will dispatch error handlers correctly no matter if failures are communicated via `leaf::result` or by an exception.
Of course, if we use exception handling exclusively, we do not need `leaf::result` at all. In this case we use `leaf::try_catch`:
[source,c++]
----
leaf::try_catch(
[]
{
process_file(f);
},
[]( std::bad_alloc const & )
{
std::cerr << "Out of memory!" << std::endl;
},
[]( parse_error e, e_line l )
{
std::cerr << "Parse error at line " << l.value << std::endl;
},
[]( io_error e, e_line const * l )
{
std::cerr << "Parse error";
if( l )
std::cerr << " at line " << l.value;
std::cerr << std::endl;
} );
----
[.text-right]
<<try_catch>>
Remarkably, we did not have to change the error handlers! But how does this work? What kind of exceptions does `process_file` throw?
LEAF enables a novel technique of exception handling, which does not use an exception type hierarchy to classify failures and does not carry data in exception objects. Recall that when failures are communicated via `leaf::result`, we call `leaf::new_error` in a `return` statement, passing any number of error objects which are sent directly to the correct error handling scope:
[source,c++]
----
enum class err1 { e1, e2, e3 };
enum class err2 { e1, e2 };
....
leaf::result<T> f()
{
....
if( error_detected )
return leaf::new_error(err1::e1, err2::e2);
// Produce and return a T.
}
----
[.text-right]
<<result>> | <<new_error>>
When using exception handling this becomes:
[source,c++]
----
enum class err1 { e1, e2, e3 };
enum class err2 { e1, e2 };
T f()
{
if( error_detected )
leaf::throw_exception(err1::e1, err2::e2);
// Produce and return a T.
}
----
[.text-right]
<<throw_exception>>
The `leaf::throw_exception` function handles the passed error objects just like `leaf::new_error` does, and then throws an object of a type that derives from `std::exception`. Using this technique, the exception type is not important: `leaf::try_catch` catches all exceptions, then goes through the usual LEAF error handler selection procedure.
If instead we want to use the legacy convention of throwing different types to indicate different failures, we simply pass an exception object (that is, an object of a type that derives from `std::exception`) as the first argument to `leaf::throw_exception`:
[source,c++]
----
leaf::throw_exception(std::runtime_error("Error!"), err1::e1, err2::e2);
----
In this case the returned object will be of type that derives from `std::runtime_error`, rather than from `std::exception`.
Finally, `leaf::on_error` "just works" as well. Here is our `process_file` function rewritten to work with exceptions, rather than return a `leaf::result` (see <<tutorial-augmenting_errors>>):
[source,c++]
----
int parse_line( FILE * f ); // Throws
struct e_line { int value; };
void process_file( FILE * f )
{
for( int current_line = 1; current_line != 10; ++current_line )
{
auto load = leaf::on_error( e_line {current_line} );
int v = parse_line(f);
// use v
}
}
----
[.text-right]
<<on_error>>
'''
=== Using External `result` Types
Static type checking creates difficulties in error handling interoperability in any non-trivial project. Using exception handling alleviates this problem somewhat because in that case error types are not burned into function signatures, so errors easily punch through multiple layers of APIs; but this doesn't help {CPP} in general because the community is fractured on the issue of exception handling. That debate notwithstanding, the reality is that {CPP} programs need to handle errors communicated through multiple layers of APIs via a plethora of error codes, `result` types and exceptions.
LEAF enables application developers to shake error objects out of each individual library's `result` type and send them to error handling scopes verbatim. Here is an example:
[source,c++]
----
lib1::result<int, lib1::error_code> foo();
lib2::result<int, lib2::error_code> bar();
int g( int a, int b );
leaf::result<int> f()
{
auto a = foo();
if( !a )
return leaf::new_error( a.error() );
auto b = bar();
if( !b )
return leaf::new_error( b.error() );
return g( a.value(), b.value() );
}
----
[.text-right]
<<result>> | <<new_error>>
Later we simply call `leaf::try_handle_some` passing an error handler for each type:
[source,c++]
----
leaf::result<int> r = leaf::try_handle_some(
[]() -> leaf::result<int>
{
return f();
},
[]( lib1::error_code ec ) -> leaf::result<int>
{
// Handle lib1::error_code
},
[]( lib2::error_code ec ) -> leaf::result<int>
{
// Handle lib2::error_code
} );
}
----
[.text-right]
<<try_handle_some>> | <<result>>
A possible complication is that we might not have the option to return `leaf::result<int>` from `f`: a third party API may impose a specific signature on it, forcing it to return a library-specific `result` type. This would be the case when `f` is intended to be used as a callback:
[source,c++]
----
void register_callback( std::function<lib3::result<int>()> const & callback );
----
Can we use LEAF in this case? Actually we can, as long as `lib3::result` is able to communicate a `std::error_code`. We just have to let LEAF know, by specializing the `is_result_type` template:
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
struct is_result_type<lib3::result<T>>: std::true_type;
} }
----
[.text-right]
<<is_result_type>>
With this in place, `f` works as before, even though `lib3::result` isn't capable of transporting `lib1` errors or `lib2` errors:
[source,c++]
----
lib1::result<int, lib1::error_type> foo();
lib2::result<int, lib2::error_type> bar();
int g( int a, int b );
lib3::result<int> f()
{
auto a = foo();
if( !a )
return leaf::new_error( a.error() );
auto b = bar();
if( !b )
return leaf::new_error( b.error() );
return g( a.value(), b.value() );
}
----
[.text-right]
<<new_error>>
The object returned by `leaf::new_error` converts implicitly to `std::error_code`, using a LEAF-specific `error_category`, which makes `lib3::result` compatible with `leaf::try_handle_some` (and with `leaf::try_handle_all`):
[source,c++]
----
lib3::result<int> r = leaf::try_handle_some(
[]() -> lib3::result<int>
{
return f();
},
[]( lib1::error_code ec ) -> lib3::result<int>
{
// Handle lib1::error_code
},
[]( lib2::error_code ec ) -> lib3::result<int>
{
// Handle lib2::error_code
} );
}
----
[.text-right]
<<try_handle_some>>
'''
[[tutorial-model]]
=== Error Communication Model
==== `noexcept` API
The following figure illustrates how error objects are transported when using LEAF without exception handling:
.LEAF noexcept Error Communication Model
image::LEAF-1.png[]
The arrows pointing down indicate the call stack order for the functions `f1` through `f5`: higher level functions calling lower level functions.
Note the call to `on_error` in `f3`: it caches the passed error objects of types `E1` and `E3` in the returned object `load`, where they stay ready to be communicated in case any function downstream from `f3` reports an error. Presumably these objects are relevant to any such failure, but are conveniently accessible only in this scope.
_Figure 1_ depicts the condition where `f5` has detected an error. It calls `leaf::new_error` to create a new, unique `error_id`. The passed error object of type `E2` is immediately loaded in the first active `context` object that provides static storage for it, found in any calling scope (in this case `f1`), and is associated with the newly-generated `error_id` (solid arrow);
The `error_id` itself is returned to the immediate caller `f4`, usually stored in a `result<T>` object `r`. That object takes the path shown by dashed arrows, as each error neutral function, unable to handle the failure, forwards it to its immediate caller in the returned value -- until an error handling scope is reached.
When the destructor of the `load` object in `f3` executes, it detects that `new_error` was invoked after its initialization, loads the cached objects of types `E1` and `E3` in the first active `context` object that provides static storage for them, found in any calling scope (in this case `f1`), and associates them with the last generated `error_id` (solid arrow).
When the error handling scope `f1` is reached, it probes `ctx` for any error objects associated with the `error_id` it received from `f2`, and processes a list of user-provided error handlers, in order, until it finds a handler with arguments that can be supplied using the available (in `ctx`) error objects. That handler is called to deal with the failure.
==== Exception Handling API
The following figure illustrates the slightly different error communication model used when errors are reported by throwing exceptions:
.LEAF Error Communication Model Using Exception Handling
image::LEAF-2.png[]
The main difference is that the call to `new_error` is implicit in the call to the function template `leaf::throw_exception`, which in this case takes an exception object of type `Ex`, and throws an exception object of unspecified type that derives publicly from `Ex`.
[[tutorial-interoperability]]
==== Interoperability
Ideally, when an error is detected, a program using LEAF would always call <<new_error>>, ensuring that each encountered failure is definitely assigned a unique <<error_id>>, which then is reliably delivered, by an exception or by a `result<T>` object, to the appropriate error handling scope.
Alas, this is not always possible.
For example, the error may need to be communicated through uncooperative 3rd-party interfaces. To facilitate this transmission, a error ID may be encoded in a `std::error_code`. As long as a 3rd-party interface is able to transport a `std::error_code`, it should be compatible with LEAF.
Further, it is sometimes necessary to communicate errors through an interface that does not even use `std::error_code`. An example of this is when an external lower-level library throws an exception, which is unlikely to be able to carry an `error_id`.
To support this tricky use case, LEAF provides the function <<current_error>>, which returns the error ID returned by the most recent call (from this thread) to <<new_error>>. One possible approach to solving the problem is to use the following logic (implemented by the <<error_monitor>> type):
. Before calling the uncooperative API, call <<current_error>> and cache the returned value.
. Call the API, then call `current_error` again:
.. If this returns the same value as before, pass the error objects to `new_error` to associate them with a new `error_id`;
.. else, associate the error objects with the `error_id` value returned by the second call to `current_error`.
Note that if the above logic is nested (e.g. one function calling another), `new_error` will be called only by the inner-most function, because that call guarantees that all calling functions will hit the `else` branch.
For a detailed tutorial see <<tutorial-on_error_in_c_callbacks>>.
TIP: To avoid ambiguities, whenever possible, use the <<throw_exception>> function template to throw exceptions, to ensure that the exception object transports a unique `error_id`; better yet, use the <<BOOST_LEAF_THROW_EXCEPTION>> macro, which in addition will capture `pass:[__FILE__]` and `pass:[__LINE__]`.
'''
[[tutorial-loading]]
=== Loading of Error Objects
To load an error object is to move it into an active <<context>>, usually local to a <<try_handle_some>>, a <<try_handle_all>> or a <<try_catch>> scope in the calling thread, where it becomes uniquely associated with a specific <<error_id>> -- or discarded if storage is not available.
Various LEAF functions take a list of error objects to load. As an example, if a function `copy_file` that takes the name of the input file and the name of the output file as its arguments detects a failure, it could communicate an error code `ec`, plus the two relevant file names using <<new_error>>:
[source,c++]
----
return leaf::new_error(ec, e_input_name{n1}, e_output_name{n2});
----
Alternatively, error objects may be loaded using a `result<T>` that is already communicating an error. This way they become associated with that error, rather than with a new error:
[source,c++]
----
leaf::result<int> f() noexcept;
leaf::result<void> g( char const * fn ) noexcept
{
if( leaf::result<int> r = f() )
{ <1>
....;
return { };
}
else
{
return r.load( e_file_name{fn} ); <2>
}
}
----
[.text-right]
<<result>> | <<result::load>>
<1> Success! Use `r.value()`.
<2> `f()` has failed; here we associate an additional `e_file_name` with the error. However, this association occurs iff in the call stack leading to `g` there are error handlers that take an `e_file_name` argument. Otherwise, the object passed to `load` is discarded. In other words, the passed objects are loaded iff the program actually uses them to handle errors.
Besides error objects, `load` can take function arguments:
* If we pass a function that takes no arguments, it is invoked, and the returned error object is loaded.
+
Consider that if we pass to `load` an error object that is not needed by any error handler, it will be discarded. If the object is expensive to compute, it would be better if the computation can be skipped as well. Passing a function with no arguments to `load` is an excellent way to achieve this behavior:
+
[source,c++]
----
struct info { .... };
info compute_info() noexcept;
leaf::result<void> operation( char const * file_name ) noexcept
{
if( leaf::result<int> r = try_something() )
{ <1>
....
return { };
}
else
{
return r.load( <2>
[&]
{
return compute_info();
} );
}
}
----
[.text-right]
<<result>> | <<result::load>>
+
<1> Success! Use `r.value()`.
<2> `try_something` has failed; `compute_info` will only be called if an error handler exists which takes a `info` argument.
+
* If we pass a function that takes a single argument of type `E &`, LEAF calls the function with the object of type `E` currently loaded in an active `context`, associated with the error. If no such object is available, a new one is default-initialized and then passed to the function.
+
For example, if an operation that involves many different files fails, a program may provide for collecting all relevant file names in a `e_relevant_file_names` object:
+
[source,c++]
----
struct e_relevant_file_names
{
std::vector<std::string> value;
};
leaf::result<void> operation( char const * file_name ) noexcept
{
if( leaf::result<int> r = try_something() )
{ <1>
....
return { };
}
else
{
return r.load( <2>
[&](e_relevant_file_names & e)
{
e.value.push_back(file_name);
} );
}
}
----
[.text-right]
<<result>> | <<result::load>>
+
<1> Success! Use `r.value()`.
<2> `try_something` has failed -- add `file_name` to the `e_relevant_file_names` object, associated with the `error_id` communicated in `r`. Note, however, that the passed function will only be called iff in the call stack there are error handlers that take an `e_relevant_file_names` object.
'''
[[tutorial-on_error]]
=== Using `on_error`
It is not typical for an error reporting function to be able to supply all of the data needed by a suitable error handling function in order to recover from the failure. For example, a function that reports `FILE` failures may not have access to the file name, yet an error handling function needs it in order to print a useful error message.
Of course the file name is typically readily available in the call stack leading to the failed `FILE` operation. Below, while `parse_info` can't report the file name, `parse_file` can and does:
[source,c++]
----
leaf::result<info> parse_info( FILE * f ) noexcept; <1>
leaf::result<info> parse_file( char const * file_name ) noexcept
{
auto load = leaf::on_error(leaf::e_file_name{file_name}); <2>
if( FILE * f = fopen(file_name,"r") )
{
auto r = parse_info(f);
fclose(f);
return r;
}
else
return leaf::new_error( error_enum::file_open_error );
}
----
[.text-right]
<<result>> | <<on_error>> | <<new_error>>
<1> `parse_info` parses `f`, communicating errors using `result<info>`.
<2> Using `on_error` ensures that the file name is included with any error reported out of `parse_file`. All we need to do is hold on to the returned object `load`; when it expires, if an error is being reported, the passed `e_file_name` value will be automatically associated with it.
TIP: `on_error` -- like `load` -- can be passed any number of arguments.
When we invoke `on_error`, we can pass three kinds of arguments:
. Actual error objects (like in the example above);
. Functions that take no arguments and return an error object;
. Functions that take an error object by mutable reference.
If we want to use `on_error` to capture `errno`, we can't just pass <<e_errno>> to it, because at that time it hasn't been set (yet). Instead, we'd pass a function that returns it:
[source,c++]
----
void read_file(FILE * f) {
auto load = leaf::on_error([]{ return e_errno{errno}; });
....
size_t nr1=fread(buf1,1,count1,f);
if( ferror(f) )
leaf::throw_exception();
size_t nr2=fread(buf2,1,count2,f);
if( ferror(f) )
leaf::throw_exception();
size_t nr3=fread(buf3,1,count3,f);
if( ferror(f) )
leaf::throw_exception();
....
}
----
Above, if `throw_exception` is called, LEAF will invoke the function passed to `on_error` and associate the returned `e_errno` object with the exception.
The final argument type that can be passed to `on_error` is a function that takes a single mutable error object reference. In this case, `on_error` uses it similarly to how such functions are used by `load`; see <<tutorial-loading>>.
'''
[[tutorial-predicates]]
=== Using Predicates to Handle Errors
Usually, LEAF error handlers are selected based on the type of the arguments they take and the type of the available error objects. When an error handler takes a predicate type as an argument, the <<handler_selection_procedure,handler selection procedure>> is able to also take into account the _value_ of the available error objects.
Consider this error code enum:
[source,c++]
----
enum class my_error
{
e1=1,
e2,
e3
};
----
We could handle `my_error` errors like so:
[source,c++]
----
return leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[]( my_error e )
{ <1>
switch(e)
{
case my_error::e1:
....; <2>
break;
case my_error::e2:
case my_error::e3:
....; <3>
break;
default:
....; <4>
break;
} );
----
<1> This handler will be selected if we've got a `my_error` object.
<2> Handle `e1` errors.
<3> Handle `e2` and `e3` errors.
<4> Handle bad `my_error` values.
If `my_error` object is available, LEAF will call our error handler. If not, the failure will be forwarded to our caller.
This can be rewritten using the <<match>> predicate to organize the different cases in different error handlers. The following is equivalent:
[source,c++]
----
return leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[]( leaf::match<my_error, my_error::e1> m )
{ <1>
assert(m.matched == my_error::e1);
....;
},
[]( leaf::match<my_error, my_error::e2, my_error::e3> m )
{ <2>
assert(m.matched == my_error::e2 || m.matched == my_error::e3);
....;
},
[]( my_error e )
{ <3>
....;
} );
----
<1> We've got a `my_error` object that compares equal to `e1`.
<2> We`ve got a `my_error` object that compares equal to either `e2` or `e3`.
<3> Handle bad `my_error` values.
The first argument to the `match` template generally specifies the type `E` of the error object `e` that must be available for the error handler to be considered at all. Typically, the rest of the arguments are values. The error handler is dropped if `e` does not compare equal to any of them.
In particular, `match` works great with `std::error_code`. The following handler is designed to handle `ENOENT` errors:
[source,c++]
----
[]( leaf::match<std::error_code, std::errc::no_such_file_or_directory> )
{
}
----
This, however, requires {CPP}17 or newer, because it is impossible to infer the type of the error enum (in this case, `std::errc`) from the specified type `std::error_code`, and {CPP}11 does not allow `auto` template arguments. LEAF provides the following workaround, compatible with {CPP}11:
[source,c++]
----
[]( leaf::match<leaf::condition<std::errc>, std::errc::no_such_file_or_directory> )
{
}
----
In addition, it is possible to select a handler based on `std::error_category`. The following handler will match any `std::error_code` of the `std::generic_category` (requires {CPP}17 or newer):
[source,c++]
----
[]( std::error_code, leaf::category<std::errc>> )
{
}
----
TIP: See <<match>> for more examples.
The following predicates are available:
* <<match>>: as described above.
* <<match_value>>: where `match<E, V...>` compares the object `e` of type `E` with the values `V...`, `match_value<E, V...>` compare `e.value` with the values `V...`.
* <<match_member>>: similar to `match_value`, but takes a pointer to the data member to compare; that is, `match_member<&E::value, V...>` is equvialent to `match_value<E, V...>`. Note, however, that `match_member` requires {CPP}17 or newer, while `match_value` does not.
* `<<catch_,catch_>><Ex...>`: Similar to `match`, but checks whether the caught `std::exception` object can be `dynamic_cast` to any of the `Ex` types.
* <<if_not>> is a special predicate that takes any other predicate `Pred` and requires that an error object of type `E` is available and that `Pred` evaluates to `false`. For example, `if_not<match<E, V...>>` requires that an object `e` of type `E` is available, and that it does not compare equal to any of the specified `V...`.
Finally, the predicate system is easily extensible, see <<predicates,Predicates>>.
NOTE: See also <<tutorial-std_error_code>>.
'''
[[tutorial-binding_handlers]]
=== Binding Error Handlers in a `std::tuple`
Consider this snippet:
[source,c++]
----
leaf::try_handle_all(
[&]
{
return f(); // returns leaf::result<T>
},
[](my_error_enum x)
{
...
},
[](read_file_error_enum y, e_file_name const & fn)
{
...
},
[]
{
...
});
----
[.text-right]
<<try_handle_all>> | <<e_file_name>>
Looks pretty simple, but what if we need to attempt a different set of operations yet use the same handlers? We could repeat the same thing with a different function passed as `TryBlock` for `try_handle_all`:
[source,c++]
----
leaf::try_handle_all(
[&]
{
return g(); // returns leaf::result<T>
},
[](my_error_enum x)
{
...
},
[](read_file_error_enum y, e_file_name const & fn)
{
...
},
[]
{
...
});
----
That works, but it is better to bind our error handlers in a `std::tuple`:
[source,c++]
----
auto error_handlers = std::make_tuple(
[](my_error_enum x)
{
...
},
[](read_file_error_enum y, e_file_name const & fn)
{
...
},
[]
{
...
});
----
The `error_handlers` tuple can later be used with any error handling function:
[source,c++]
----
leaf::try_handle_all(
[&]
{
// Operations which may fail <1>
},
error_handlers );
leaf::try_handle_all(
[&]
{
// Different operations which may fail <2>
},
error_handlers ); <3>
----
[.text-right]
<<try_handle_all>> | <<error_info>>
<1> One set of operations which may fail...
<2> A different set of operations which may fail...
<3> ... both using the same `error_handlers`.
Error handling functions accept a `std::tuple` of error handlers in place of any error handler. The behavior is as if the tuple is unwrapped in-place.
'''
[[tutorial-async]]
=== Transporting Error Objects Between Threads
Error objects are stored on the stack in an instance of the <<context>> class template in the scope of e.g. <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> functions. When using concurrency, we need a mechanism to collect error objects in one thread, then use them to handle errors in another thread.
LEAF offers two interfaces for this purpose, one using `result<T>`, and another designed for programs that use exception handling.
[[tutorial-async_result]]
==== Using `result<T>`
Let's assume we have a `task` that we want to launch asynchronously, which produces a `task_result` but could also fail:
[source,c++]
----
leaf::result<task_result> task();
----
Because the task will run asynchronously, in case of a failure we need it to capture the relevant error objects but not handle errors. To this end, in the main thread we bind our error handlers in a `std::tuple`, which we will later use to handle errors from each completed asynchronous task (see <<tutorial-binding_handlers,tutorial>>):
[source,c++]
----
auto error_handlers = std::make_tuple(
[](E1 e1, E2 e2)
{
//Deal with E1, E2
....
return { };
},
[](E3 e3)
{
//Deal with E3
....
return { };
} );
----
Why did we start with this step? Because we need to create a <<context>> object to collect the error objects we need. We could just instantiate the `context` template with `E1`, `E2` and `E3`, but that would be prone to errors, since it could get out of sync with the handlers we use. Thankfully LEAF can deduce the types we need automatically, we just need to show it our `error_handlers`:
[source,c++]
----
std::shared_ptr<leaf::polymorphic_context> ctx = leaf::make_shared_context(error_handlers);
----
The `polymorphic_context` type is an abstract base class that has the same members as any instance of the `context` class template, allowing us to erase its exact type. In this case what we're holding in `ctx` is a `context<E1, E2, E3>`, where `E1`, `E2` and `E3` were deduced automatically from the `error_handlers` tuple we passed to `make_shared_context`.
We're now ready to launch our asynchronous task:
[source,c++]
----
std::future<leaf::result<task_result>> launch_task() noexcept
{
return std::async(
std::launch::async,
[&]
{
std::shared_ptr<leaf::polymorphic_context> ctx = leaf::make_shared_context(error_handlers);
return leaf::capture(ctx, &task);
} );
}
----
[.text-right]
<<result>> | <<make_shared_context>> | <<capture>>
That's it! Later when we `get` the `std::future`, we can process the returned `result<task_result>` in a call to <<try_handle_some>>, using the `error_handlers` tuple we created earlier:
[source,c++]
----
//std::future<leaf::result<task_result>> fut;
fut.wait();
return leaf::try_handle_some(
[&]() -> leaf::result<void>
{
BOOST_LEAF_AUTO(r, fut.get());
//Success!
return { }
},
error_handlers );
----
[.text-right]
<<try_handle_some>> | <<result>> | <<BOOST_LEAF_AUTO>>
The reason this works is that in case the `leaf::result<T>` communicates a failure, it is able to hold a `shared_ptr<polymorphic_context>` object. That is why earlier instead of calling `task()` directly, we called `leaf::capture`: it calls the passed function and, in case that fails, it stores the `shared_ptr<polymorphic_context>` we created in the returned `result<T>`, which now doesn't just communicate the fact that an error has occurred, but also holds the `context` object that `try_handle_some` needs in order to supply a suitable handler with arguments.
NOTE: Follow this link to see a complete example program: https://github.com/boostorg/leaf/blob/master/example/capture_in_result.cpp?ts=4[capture_in_result.cpp].
[[tutorial-async_eh]]
==== Using Exception Handling
Let's assume we have an asynchronous `task` which produces a `task_result` but could also throw:
[source,c++]
----
task_result task();
----
Just like we saw in <<tutorial-async_result>>, first we will bind our error handlers in a `std::tuple`:
[source,c++]
----
auto handle_errors = std::make_tuple(
[](E1 e1, E2 e2)
{
//Deal with E1, E2
....
return { };
},
[](E3 e3)
{
//Deal with E3
....
return { };
} );
----
Launching the task looks the same as before, except that we don't use `result<T>`:
[source,c++]
----
std::future<task_result> launch_task()
{
return std::async(
std::launch::async,
[&]
{
std::shared_ptr<leaf::polymorphic_context> ctx = leaf::make_shared_context(&handle_error);
return leaf::capture(ctx, &task);
} );
}
----
[.text-right]
<<make_shared_context>> | <<capture>>
That's it! Later when we `get` the `std::future`, we can process the returned `task_result` in a call to <<try_catch>>, using the `error_handlers` we saved earlier, as if it was generated locally:
[source,c++]
----
//std::future<task_result> fut;
fut.wait();
return leaf::try_catch(
[&]
{
task_result r = fut.get(); // Throws on error
//Success!
},
error_handlers );
----
[.text-right]
<<try_catch>>
This works similarly to using `result<T>`, except that the `std::shared_ptr<polymorphic_context>` is transported in an exception object (of unspecified type which <<try_catch>> recognizes and then automatically unwraps the original exception).
NOTE: Follow this link to see a complete example program: https://github.com/boostorg/leaf/blob/master/example/capture_in_exception.cpp?ts=4[capture_in_exception.cpp].
'''
[[tutorial-classification]]
=== Classification of Failures
It is common for an interface to define an `enum` that lists all possible error codes that the API reports. The benefit of this approach is that the list is complete and usually well documented:
[source,c++]
----
enum error_code
{
....
read_error,
size_error,
eof_error,
....
};
----
The disadvantage of such flat enums is that they do not support handling of a whole class of failures. Consider the following LEAF error handler:
[source,c++]
----
....
[](leaf::match<error_code, size_error, read_error, eof_error>, leaf::e_file_name const & fn)
{
std::cerr << "Failed to access " << fn.value << std::endl;
},
....
----
[.text-right]
<<match>> | <<e_file_name>>
It will get called if the value of the `error_code` enum communicated with the failure is one of `size_error`, `read_error` or `eof_error`. In short, the idea is to handle any input error.
But what if later we add support for detecting and reporting a new type of input error, e.g. `permissions_error`? It is easy to add that to our `error_code` enum; but now our input error handler won't recognize this new input error -- and we have a bug.
If we can use exceptions, the situation is better because exception types can be organized in a hierarchy in order to classify failures:
[source,c++]
----
struct input_error: std::exception { };
struct read_error: input_error { };
struct size_error: input_error { };
struct eof_error: input_error { };
----
In terms of LEAF, our input error exception handler now looks like this:
[source,c++]
----
[](input_error &, leaf::e_file_name const & fn)
{
std::cerr << "Failed to access " << fn.value << std::endl;
},
----
This is future-proof, but still not ideal, because it is not possible to refine the classification of the failure after the exception object has been thrown.
LEAF supports a novel style of error handling where the classification of failures does not use error code values or exception type hierarchies. Instead of our `error_code` enum, we could define:
[source,c++]
----
....
struct input_error { };
struct read_error { };
struct size_error { };
struct eof_error { };
....
----
With this in place, we could define a function `file_read`:
[source,c++]
----
leaf::result<void> file_read( FILE & f, void * buf, int size )
{
int n = fread(buf, 1, size, &f);
if( ferror(&f) )
return leaf::new_error(input_error{}, read_error{}, leaf::e_errno{errno}); <1>
if( n!=size )
return leaf::new_error(input_error{}, eof_error{}); <2>
return { };
}
----
[.text-right]
<<result>> | <<new_error>> | <<e_errno>>
<1> This error is classified as `input_error` and `read_error`.
<2> This error is classified as `input_error` and `eof_error`.
Or, even better:
[source,c++]
----
leaf::result<void> file_read( FILE & f, void * buf, int size )
{
auto load = leaf::on_error(input_error{}); <1>
int n = fread(buf, 1, size, &f);
if( ferror(&f) )
return leaf::new_error(read_error{}, leaf::e_errno{errno}); <2>
if( n!=size )
return leaf::new_error(eof_error{}); <3>
return { };
}
----
[.text-right]
<<result>> | <<on_error>> | <<new_error>> | <<e_errno>>
<1> Any error escaping this scope will be classified as `input_error`
<2> In addition, this error is classified as `read_error`.
<3> In addition, this error is classified as `eof_error`.
This technique works just as well if we choose to use exception handling, we just call `leaf::throw_exception` instead of `leaf::new_error`:
[source,c++]
----
void file_read( FILE & f, void * buf, int size )
{
auto load = leaf::on_error(input_error{});
int n = fread(buf, 1, size, &f);
if( ferror(&f) )
leaf::throw_exception(read_error{}, leaf::e_errno{errno});
if( n!=size )
leaf::throw_exception(eof_error{});
}
----
[.text-right]
<<on_error>> | <<throw_exception>> | <<e_errno>>
NOTE: If the type of the first argument passed to `leaf::throw_exception` derives from `std::exception`, it will be used to initialize the thrown exception object. Here this is not the case, so the function returns a default-initialized `std::exception` object, while the first (and any other) argument is associated with the failure.
Now we can write a future-proof handler for any `input_error`:
[source,c++]
----
....
[](input_error, leaf::e_file_name const & fn)
{
std::cerr << "Failed to access " << fn.value << std::endl;
},
....
----
Remarkably, because the classification of the failure does not depend on error codes or on exception types, this error handler can be used with `try_catch` if we use exception handling, or with `try_handle_some`/`try_handle_all` if we do not.
'''
[[tutorial-exception_to_result]]
=== Converting Exceptions to `result<T>`
It is sometimes necessary to catch exceptions thrown by a lower-level library function, and report the error through different means, to a higher-level library which may not use exception handling.
TIP: Error handlers that take arguments of types that derive from `std::exception` work correctly -- regardless of whether the error object itself is thrown as an exception, or <<tutorial-loading,loaded>> into a <<context>>. The technique described here is only needed when the exception must be communicated through functions which are not exception-safe, or are compiled with exception handling disabled.
Suppose we have an exception type hierarchy and a function `compute_answer_throws`:
[source,c++]
----
class error_base: public std::exception { };
class error_a: public error_base { };
class error_b: public error_base { };
class error_c: public error_base { };
int compute_answer_throws()
{
switch( rand()%4 )
{
default: return 42;
case 1: throw error_a();
case 2: throw error_b();
case 3: throw error_c();
}
}
----
We can write a simple wrapper using `exception_to_result`, which calls `compute_answer_throws` and switches to `result<int>` for error handling:
[source,c++]
----
leaf::result<int> compute_answer() noexcept
{
return leaf::exception_to_result<error_a, error_b>(
[]
{
return compute_answer_throws();
} );
}
----
[.text-right]
<<result>> | <<exception_to_result>>
The `exception_to_result` template takes any number of exception types. All exception types thrown by the passed function are caught, and an attempt is made to convert the exception object to each of the specified types. Each successfully-converted slice of the caught exception object, as well as the return value of `std::current_exception`, are copied and <<tutorial-loading,loaded>>, and in the end the exception is converted to a `<<result,result>><T>` object.
(In our example, `error_a` and `error_b` slices as communicated as error objects, but `error_c` exceptions will still be captured by `std::exception_ptr`).
Here is a simple function which prints successfully computed answers, forwarding any error (originally reported by throwing an exception) to its caller:
[source,c++]
----
leaf::result<void> print_answer() noexcept
{
BOOST_LEAF_AUTO(answer, compute_answer());
std::cout << "Answer: " << answer << std::endl;
return { };
}
----
[.text-right]
<<result>> | <<BOOST_LEAF_AUTO>>
Finally, here is a scope that handles the errors -- it will work correctly regardless of whether `error_a` and `error_b` objects are thrown as exceptions or not.
[source,c++]
----
leaf::try_handle_all(
[]() -> leaf::result<void>
{
BOOST_LEAF_CHECK(print_answer());
return { };
},
[](error_a const & e)
{
std::cerr << "Error A!" << std::endl;
},
[](error_b const & e)
{
std::cerr << "Error B!" << std::endl;
},
[]
{
std::cerr << "Unknown error!" << std::endl;
} );
----
[.text-right]
<<try_handle_all>> | <<result>> | <<BOOST_LEAF_CHECK>>
NOTE: The complete program illustrating this technique is available https://github.com/boostorg/leaf/blob/master/example/exception_to_result.cpp?ts=4[here].
'''
[[tutorial-on_error_in_c_callbacks]]
=== Using `error_monitor` to Report Arbitrary Errors from C-callbacks
Communicating information pertaining to a failure detected in a C callback is tricky, because C callbacks are limited to a specific static signature, which may not use {CPP} types.
LEAF makes this easy. As an example, we'll write a program that uses Lua and reports a failure from a {CPP} function registered as a C callback, called from a Lua program. The failure will be propagated from {CPP}, through the Lua interpreter (written in C), back to the {CPP} function which called it.
C/{CPP} functions designed to be invoked from a Lua program must use the following signature:
[source,c]
----
int do_work( lua_State * L ) ;
----
Arguments are passed on the Lua stack (which is accessible through `L`). Results too are pushed onto the Lua stack.
First, let's initialize the Lua interpreter and register a function, `do_work`, as a C callback available for Lua programs to call:
[source,c++]
----
std::shared_ptr<lua_State> init_lua_state() noexcept
{
std::shared_ptr<lua_State> L(lua_open(), &lua_close); //<1>
lua_register(&*L, "do_work", &do_work); //<2>
luaL_dostring(&*L, "\ //<3>
\n function call_do_work()\
\n return do_work()\
\n end");
return L;
}
----
<1> Create a new `lua_State`. We'll use `std::shared_ptr` for automatic cleanup.
<2> Register the `do_work` {CPP} function as a C callback, under the global name `do_work`. With this, calls from Lua programs to `do_work` will land in the `do_work` {CPP} function.
<3> Pass some Lua code as a `C` string literal to Lua. This creates a global Lua function called `call_do_work`, which we will later ask Lua to execute.
Next, let's define our `enum` used to communicate `do_work` failures:
[source,c++]
----
enum do_work_error_code
{
ec1=1,
ec2
};
----
We're now ready to define the `do_work` callback function:
[source,c++]
----
int do_work( lua_State * L ) noexcept
{
bool success = rand() % 2; <1>
if( success )
{
lua_pushnumber(L, 42); <2>
return 1;
}
else
{
(void) leaf::new_error(ec1); <3>
return luaL_error(L, "do_work_error"); <4>
}
}
----
[.text-right]
<<new_error>> | <<error_id::load>>
<1> "Sometimes" `do_work` fails.
<2> In case of success, push the result on the Lua stack, return back to Lua.
<3> Generate a new `error_id` and associate a `do_work_error_code` with it. Normally, we'd return this in a `leaf::result<T>`, but the `do_work` function signature (required by Lua) does not permit this.
<4> Tell the Lua interpreter to abort the Lua program.
Now we'll write the function that calls the Lua interpreter to execute the Lua function `call_do_work`, which in turn calls `do_work`. We'll return `<<result,result>><int>`, so that our caller can get the answer in case of success, or an error:
[source,c++]
----
leaf::result<int> call_lua( lua_State * L )
{
lua_getfield(L, LUA_GLOBALSINDEX, "call_do_work");
error_monitor cur_err;
if( int err = lua_pcall(L, 0, 1, 0) ) <1>
{
auto load = leaf::on_error(e_lua_error_message{lua_tostring(L,1)}); <2>
lua_pop(L,1);
return cur_err.assigned_error_id().load(e_lua_pcall_error{err}); <3>
}
else
{
int answer = lua_tonumber(L, -1); <4>
lua_pop(L, 1);
return answer;
}
}
----
[.text-right]
<<result>> | <<on_error>> | <<error_monitor>>
<1> Ask the Lua interpreter to call the global Lua function `call_do_work`.
<2> `on_error` works as usual.
<3> `load` will use the `error_id` generated in our Lua callback. This is the same `error_id` the `on_error` uses as well.
<4> Success! Just return the `int` answer.
Finally, here is the `main` function which exercises `call_lua`, each time handling any failure:
[source,c++]
----
int main() noexcept
{
std::shared_ptr<lua_State> L=init_lua_state();
for( int i=0; i!=10; ++i )
{
leaf::try_handle_all(
[&]() -> leaf::result<void>
{
BOOST_LEAF_AUTO(answer, call_lua(&*L));
std::cout << "do_work succeeded, answer=" << answer << '\n'; <1>
return { };
},
[](do_work_error_code e) <2>
{
std::cout << "Got do_work_error_code = " << e << "!\n";
},
[](e_lua_pcall_error const & err, e_lua_error_message const & msg) <3>
{
std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n";
},
[](leaf::error_info const & unmatched)
{
std::cerr <<
"Unknown failure detected" << std::endl <<
"Cryptic diagnostic information follows" << std::endl <<
unmatched;
} );
}
----
[.text-right]
<<try_handle_all>> | <<result>> | <<BOOST_LEAF_AUTO>> | <<error_info>>
<1> If the call to `call_lua` succeeded, just print the answer.
<2> Handle `do_work` failures.
<3> Handle all other `lua_pcall` failures.
NOTE: Follow this link to see the complete program: https://github.com/boostorg/leaf/blob/master/example/lua_callback_result.cpp?ts=4[lua_callback_result.cpp].
TIP: When using Lua with {CPP}, we need to protect the Lua interpreter from exceptions that may be thrown from {CPP} functions installed as `lua_CFunction` callbacks. Here is the program from this section rewritten to use a {CPP} exception to safely communicate errors out of the `do_work` function: https://github.com/boostorg/leaf/blob/master/example/lua_callback_eh.cpp?ts=4[lua_callback_eh.cpp].
''''
[[tutorial-diagnostic_information]]
=== Diagnostic Information
LEAF is able to automatically generate diagnostic messages that include information about all error objects available to error handlers:
[source,c++]
----
enum class error_code
{
read_error,
write_error
};
....
leaf::try_handle_all(
[]() -> leaf::result<void> <1>
{
...
return leaf::new_error( error_code::write_error, leaf::e_file_name{ "file.txt" } );
},
[]( leaf::match<error_code, error_code::read_error> ) <2>
{
std::cerr << "Read error!" << std::endl;
},
[]( leaf::verbose_diagnostic_info const & info ) <3>
{
std::cerr << "Unrecognized error detected, cryptic diagnostic information follows.\n" << info;
} );
----
<1> We handle all failures that occur in this try block.
<2> One or more error handlers that should handle all possible failures.
<3> The "catch all" error handler is required by `try_handle_all`. It will be called if LEAF is unable to use another error handler.
The `verbose_diagnostic_info` output for the snippet above tells us that we got an `error_code` with value `1` (`write_error`), and an object of type `e_file_name` with `"file.txt"` stored in its `.value`:
----
Unrecognized error detected, cryptic diagnostic information follows.
leaf::verbose_diagnostic_info for Error ID = 1:
[with Name = error_code]: 1
Unhandled error objects:
[with Name = boost::leaf::e_file_name]: file.txt
----
To print each error object, LEAF attempts to bind an unqualified call to `operator<<`, passing a `std::ostream` and the error object. If that fails, it will also attempt to bind `operator<<` that takes the `.value` of the error type. If that also does not compile, the error object value will not appear in diagnostic messages, though LEAF will still print its type.
Even with error types that define a printable `.value`, the user may still want to overload `operator<<` for the enclosing `struct`, e.g.:
[source,c++]
----
struct e_errno
{
int value;
friend std::ostream & operator<<( std::ostream & os, e_errno const & e )
{
return os << "errno = " << e.value << ", \"" << strerror(e.value) << '"';
}
};
----
The `e_errno` type above is designed to hold `errno` values. The defined `operator<<` overload will automatically include the output from `strerror` when `e_errno` values are printed (LEAF defines `e_errno` in `<boost/leaf/common.hpp>`, together with other commonly-used error types).
Using `verbose_diagnostic_info` comes at a cost. Normally, when the program attempts to communicate error objects of types which are not used in any error handling scope in the current call stack, they are discarded, which saves cycles. However, if an error handler is provided that takes `verbose_diagnostic_info` argument, before such objects are discarded, they are printed and appended to a `std::string` (this is the case with `e_file_name` in our example above). Such objects appear under `Unhandled error objects` in the output from `verbose_diagnostic_info`.
If handling `verbose_diagnostic_info` is considered too costly, use `diagnostic_info` instead:
[source,c++]
----
leaf::try_handle_all(
[]() -> leaf::result<void>
{
...
return leaf::new_error( error_code::write_error, leaf::e_file_name{ "file.txt" } );
},
[]( leaf::match<error_code, error_code::read_error> )
{
std::cerr << "Read error!" << std::endl;
},
[]( leaf::diagnostic_info const & info )
{
std::cerr << "Unrecognized error detected, cryptic diagnostic information follows.\n" << info;
} );
----
In this case, the output may look like this:
----
Unrecognized error detected, cryptic diagnostic information follows.
leaf::diagnostic_info for Error ID = 1:
[with Name = error_code]: 1
Detected 1 attempt to communicate an unexpected error object of type [with Name = boost::leaf::e_file_name]
----
Notice how the diagnostic information for `e_file_name` changed: LEAF no longer prints it before discarding it, and so `diagnostic_info` can only inform about the type of the discarded object, but not its value.
TIP: The automatically-generated diagnostic messages are developer-friendly, but not user-friendly. Therefore, `operator<<` overloads for error types should only print technical information in English, and should not attempt to localize strings or to format a user-friendly message; this should be done in error handling functions specifically designed for that purpose.
'''
[[tutorial-std_error_code]]
=== Working with `std::error_code`, `std::error_condition`
==== Introduction
The relationship between `std::error_code` and `std::error_condition` is not easily understood from reading the standard specifications. This section explains how they're supposed to be used, and how LEAF interacts with them.
The idea behind `std::error_code` is to encode both an integer value representing an error code, as well as the domain of that value. The domain is represented by a `std::error_category` [underline]#reference#. Conceptually, a `std::error_code` is like a `pair<std::error_category const &, int>`.
Let's say we have this `enum`:
[source,c++]
----
enum class libfoo_error
{
e1 = 1,
e2,
e3
};
----
We want to be able to transport `libfoo_error` values in `std::error_code` objects. This erases their static type, which enables them to travel freely across API boundaries. To this end, we must define a `std::error_category` that represents our `libfoo_error` type:
[source,c++]
----
std::error_category const & libfoo_error_category()
{
struct category: std::error_category
{
char const * name() const noexcept override
{
return "libfoo";
}
std::string message(int code) const override
{
switch( libfoo_error(code) )
{
case libfoo_error::e1: return "e1";
case libfoo_error::e2: return "e2";
case libfoo_error::e3: return "e3";
default: return "error";
}
}
};
static category c;
return c;
}
----
We also need to inform the standard library that `libfoo_error` is compatible with `std::error_code`, and provide a factory function which can be used to make `std::error_code` objects out of `libfoo_error` values:
[source,c++]
----
namespace std
{
template <>
struct is_error_code_enum<libfoo_error>: std::true_type
{
};
}
std::error_code make_error_code(libfoo_error e)
{
return std::error_code(int(e), libfoo_error_category());
}
----
With this in place, if we receive a `std::error_code`, we can easily check if it represents some of the `libfoo_error` values we're interested in:
[source,c++]
----
std::error_code f();
....
auto ec = f();
if( ec == libfoo_error::e1 || ec == libfoo_error::e2 )
{
// We got either a libfoo_error::e1 or a libfoo_error::e2
}
----
This works because the standard library detects that `std::is_error_code_enum<libfoo_error>::value` is `true`, and then uses `make_error_code` to create a `std::error_code` object it actually uses to compare to `ec`.
So far so good, but remember, the standard library defines another type also, `std::error_condition`. The first confusing thing is that in terms of its physical representation, `std::error_condition` is identical to `std::error_code`; that is, it is also like a pair of `std::error_category` reference and an `int`. Why do we need two different types which use identical physical representation?
The key to answering this question is to understand that `std::error_code` objects are designed to be returned from functions to indicate failures. In contrast, `std::error_condition` objects are [underline]#never# supposed to be communicated; their purpose is to interpret the `std::error_code` values being communicated. The idea is that in a given program there may be multiple different "physical" (maybe platform-specific) `std::error_code` values which all indicate the same "logical" `std::error_condition`.
This leads us to the second confusing thing about `std::error_condition`: it uses the same `std::error_category` type, but for a completely different purpose: to specify what `std::error_code` values are equivalent to what `std::error_condition` values.
Let's say that in addition to `libfoo`, our program uses another library, `libbar`, which communicates failures in terms of `std::error_code` with a different error category. Perhaps `libbar_error` looks like this:
[source,c++]
----
enum class libbar_error
{
e1 = 1,
e2,
e3,
e4
};
// Boilerplate omitted:
// - libbar_error_category()
// - specialization of std::is_error_code_enum
// - make_error_code factory function for libbar_error.
----
We can now use `std::error_condition` to define the _logical_ error conditions represented by the `std::error_code` values communicated by `libfoo` and `libbar`:
[source,c++]
----
enum class my_error_condition <1>
{
c1 = 1,
c2
};
std::error_category const & libfoo_error_category() <2>
{
struct category: std::error_category
{
char const * name() const noexcept override
{
return "my_error_condition";
}
std::string message(int cond) const override
{
switch( my_error_condition(code) )
{
case my_error_condition::c1: return "c1";
case my_error_condition::c2: return "c2";
default: return "error";
}
}
bool equivalent(std::error_code const & code, int cond) const noexcept
{
switch( my_error_condition(cond) )
{
case my_error_condition::c1: <3>
return
code == libfoo_error::e1 ||
code == libbar_error::e3 ||
code == libbar_error::e4;
case my_error_condition::c2: <4>
return
code == libfoo_error::e2 ||
code == libbar_error::e1 ||
code == libbar_error::e2;
default:
return false;
}
}
};
static category c;
return c;
}
namespace std
{
template <> <5>
class is_error_condition_enum<my_error_condition>: std::true_type
{
};
}
std::error_condition make_error_condition(my_error_condition e) <6>
{
return std::error_condition(int(e), my_error_condition_error_category());
}
----
<1> Enumeration of the two logical error conditions, `c1` and `c2`.
<2> Define the `std::error_category` for `std::error_condition` objects that represent a `my_error_condition`.
<3> Here we specify that any of `libfoo:error::e1`, `libbar_error::e3` and `libbar_error::e4` are logically equivalent to `my_error_condition::c1`, and that...
<4> ...any of `libfoo:error::e2`, `libbar_error::e1` and `libbar_error::e2` are logically equivalent to `my_error_condition::c2`.
<5> This specialization tells the standard library that the `my_error_condition` enum is designed to be used with `std::error_condition`.
<6> The factory function to make `std::error_condition` objects out of `my_error_condition` values.
Phew!
Now, if we have a `std::error_code` object `ec`, we can easily check if it is equivalent to `my_error_condition::c1` like so:
[source,c++]
----
if( ec == my_error_condition::c1 )
{
// We have a c1 in our hands
}
----
Again, remember that beyond defining the `std::error_category` for `std::error_condition` objects initialized with a `my_error_condition` value, we don't need to interact with the actual `std::error_condition` instances: they're created when needed to compare to a `std::error_code`, and that's pretty much all they're good for.
==== Support in LEAF
The `match` predicate can be used as an argument to a LEAF error handler to match a `std::error_code` with a given error condition. For example, to handle `my_error_condition::c1` (see above), we could use:
[source,c++]
----
leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[]( leaf::match<std::error_code, my_error_condition::c1> m )
{
assert(m.matched == my_error_condition::c1);
....
} );
----
See <<match>> for more examples.
'''
[[tutorial-boost_exception_integration]]
=== Boost Exception Integration
Instead of the https://www.boost.org/doc/libs/release/libs/exception/doc/get_error_info.html[`boost::get_error_info`] API defined by Boost Exception, it is possible to use LEAF error handlers directly. Consider the following use of `boost::get_error_info`:
[source,c++]
----
typedef boost::error_info<struct my_info_, int> my_info;
void f(); // Throws using boost::throw_exception
void g()
{
try
{
f();
},
catch( boost::exception & e )
{
if( int const * x = boost::get_error_info<my_info>(e) )
std::cerr << "Got my_info with value = " << *x;
} );
}
----
We can rewrite `g` to access `my_info` using LEAF:
[source,c++]
----
#include <boost/leaf/handle_errors.hpp>
void g()
{
leaf::try_catch(
[]
{
f();
},
[]( my_info x )
{
std::cerr << "Got my_info with value = " << x.value();
} );
}
----
[.text-right]
<<try_catch>>
Taking `my_info` means that the handler will only be selected if the caught exception object carries `my_info` (which LEAF accesses via `boost::get_error_info`).
The use of <<match>> is also supported:
[source,c++]
----
void g()
{
leaf::try_catch(
[]
{
f();
},
[]( leaf::match_value<my_info, 42> )
{
std::cerr << "Got my_info with value = 42";
} );
}
----
Above, the handler will be selected if the caught exception object carries `my_info` with `.value()` equal to 42.
[[example]]
== Examples
See https://github.com/boostorg/leaf/tree/master/example[github].
[[synopsis]]
== Synopsis
This section lists each public header file in LEAF, documenting the definitions it provides.
LEAF headers are designed to minimize coupling:
* Headers needed to report or forward but not handle errors are lighter than headers providing error handling functionality.
* Headers that provide exception handling or throwing functionality are separate from headers that provide error handling or reporting but do not use exceptions.
A standalone single-header option is available; please see <<distribution>>.
'''
[[synopsis-reporting]]
=== Error Reporting
[[error.hpp]]
==== `error.hpp`
====
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
class error_id
{
public:
error_id() noexcept;
template <class Enum>
error_id( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
error_id( std::error_code const & ec ) noexcept;
int value() const noexcept;
explicit operator bool() const noexcept;
std::error_code to_error_code() const noexept;
friend bool operator==( error_id a, error_id b ) noexcept;
friend bool operator!=( error_id a, error_id b ) noexcept;
friend bool operator<( error_id a, error_id b ) noexcept;
template <class... Item>
error_id load( Item && ... item ) const noexcept;
friend std::ostream & operator<<( std::ostream & os, error_id x );
};
bool is_error_id( std::error_code const & ec ) noexcept;
template <class... Item>
error_id new_error( Item && ... item ) noexcept;
error_id current_error() noexcept;
//////////////////////////////////////////
class polymorphic_context
{
protected:
polymorphic_context() noexcept = default;
~polymorphic_context() noexcept = default;
public:
virtual void activate() noexcept = 0;
virtual void deactivate() noexcept = 0;
virtual bool is_active() const noexcept = 0;
virtual void propagate( error_id ) noexcept = 0;
virtual void print( std::ostream & ) const = 0;
};
//////////////////////////////////////////
template <class Ctx>
class context_activator
{
context_activator( context_activator const & ) = delete;
context_activator & operator=( context_activator const & ) = delete;
public:
explicit context_activator( Ctx & ctx ) noexcept;
context_activator( context_activator && ) noexcept;
~context_activator() noexcept;
};
template <class Ctx>
context_activator<Ctx> activate_context( Ctx & ctx ) noexcept;
template <class R>
struct is_result_type: std::false_type
{
};
template <class R>
struct is_result_type<R const>: is_result_type<R>
{
};
} }
#define BOOST_LEAF_ASSIGN(v, r)\
auto && <<temp>> = r;\
if( !<<temp>> )\
return <<temp>>.error();\
v = std::forward<decltype(<<temp>>)>(<<temp>>).value()
#define BOOST_LEAF_AUTO(v, r)\
BOOST_LEAF_ASSIGN(auto v, r)
#define BOOST_LEAF_CHECK(r)\
auto && <<temp>> = r;\
if( <<temp>> )\
;\
else\
return <<temp>>.error()
#define BOOST_LEAF_NEW_ERROR <<exact-definition-unspecified>>
----
[.text-right]
Reference: <<error_id>> | <<is_error_id>> | <<new_error>> | <<current_error>> | <<polymorphic_context>> | <<context_activator>> | <<activate_context>> | <<is_result_type>> | <<BOOST_LEAF_ASSIGN>> | <<BOOST_LEAF_AUTO>> | <<BOOST_LEAF_CHECK>> | <<BOOST_LEAF_NEW_ERROR>>
====
[[common.hpp]]
==== `common.hpp`
====
.#include <boost/leaf/common.hpp>
[source,c++]
----
namespace boost { namespace leaf {
struct e_api_function { char const * value; };
struct e_file_name { std::string value; };
struct e_type_info_name { char const * value; };
struct e_at_line { int value; };
struct e_errno
{
int value;
explicit e_errno(int value=errno);
friend std::ostream & operator<<(std::ostream &, e_errno const &);
};
namespace windows
{
struct e_LastError
{
unsigned value;
explicit e_LastError(unsigned value);
#if BOOST_LEAF_CFG_WIN32
e_LastError();
friend std::ostream & operator<<(std::ostream &, e_LastError const &);
#endif
};
}
} }
----
[.text-right]
Reference: <<e_api_function>> | <<e_file_name>> | <<e_at_line>> | <<e_type_info_name>> | <<e_source_location>> | <<e_errno>> | <<e_LastError>>
====
[[result.hpp]]
==== `result.hpp`
====
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
class result
{
public:
result() noexcept;
result( T && v ) noexcept;
result( T const & v );
template <class U>
result( U && u, <<enabled_if_T_can_be_inited_with_U>> );
result( error_id err ) noexcept;
result( std::shared_ptr<polymorphic_context> && ctx ) noexcept;
template <class Enum>
result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
result( std::error_code const & ec ) noexcept;
result( result && r ) noexcept;
template <class U>
result( result<U> && r ) noexcept;
result & operator=( result && r ) noexcept;
template <class U>
result & operator=( result<U> && r ) noexcept;
bool has_value() const noexcept;
bool has_error() const noexcept;
explicit operator bool() const noexcept;
T const & value() const;
T & value();
T const * operator->() const noexcept;
T * operator->() noexcept;
T const & operator*() const noexcept;
T & operator*() noexcept;
<<unspecified-type>> error() noexcept;
template <class... Item>
error_id load( Item && ... item ) noexcept;
};
template <>
class result<void>
{
public:
result() noexcept;
result( error_id err ) noexcept;
result( std::shared_ptr<polymorphic_context> && ctx ) noexcept;
template <class Enum>
result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
result( std::error_code const & ec ) noexcept;
result( result && r ) noexcept;
template <class U>
result( result<U> && r ) noexcept;
result & operator=( result && r ) noexcept;
template <class U>
result & operator=( result<U> && r ) noexcept;
explicit operator bool() const noexcept;
void value() const;
<<unspecified-type>> error() noexcept;
template <class... Item>
error_id load( Item && ... item ) noexcept;
};
struct bad_result: std::exception { };
template <class T>
struct is_result_type<result<T>>: std::true_type
{
};
} }
----
[.text-right]
Reference: <<result>> | <<is_result_type>>
====
[[on_error.hpp]]
==== `on_error.hpp`
====
[source,c++]
.#include <boost/leaf/on_error.hpp>
----
namespace boost { namespace leaf {
template <class... Item>
<<unspecified-type>> on_error( Item && ... e ) noexcept;
class error_monitor
{
public:
error_monitor() noexcept;
error_id check() const noexcept;
error_id assigned_error_id() const noexcept;
};
} }
----
[.text-right]
Reference: <<on_error>> | <<error_monitor>>
====
[[exception.hpp]]
==== `exception.hpp`
====
.#include <boost/leaf/exception.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class Ex, class... E> <1>
[[noreturn]] void throw_exception( Ex &&, E && ... );
template <class E1, class... E> <2>
[[noreturn]] void throw_exception( E1 &&, E && ... );
[[noreturn]] void throw_exception();
template <class Ex, class... E> <1>
[[noreturn]] void throw_exception( error_id id, Ex &&, E && ... );
template <class E1, class... E> <2>
[[noreturn]] void throw_exception( error_id id, E1 &&, E && ... );
[[noreturn]] void throw_exception( error_id id );
template <class... Ex, class F>
<<result<T>-deduced>> exception_to_result( F && f ) noexcept;
} }
#define BOOST_LEAF_THROW_EXCEPTION <<exact-definition-unspecified>>
----
[.text-right]
Reference: <<throw_exception>> | <<BOOST_LEAF_THROW_EXCEPTION>>
<1> Only enabled if std::is_base_of<std::exception, Ex>::value.
<2> Only enabled if !std::is_base_of<std::exception, E1>::value.
====
==== `capture.hpp`
====
[source,c++]
.#include <boost/leaf/capture.hpp>
----
namespace boost { namespace leaf {
template <class F, class... A>
decltype(std::declval<F>()(std::forward<A>(std::declval<A>())...))
capture(std::shared_ptr<polymorphic_context> && ctx, F && f, A... a);
} }
----
[.text-right]
Reference: <<capture>> | <<exception_to_result>>
====
'''
[[tutorial-handling]]
=== Error Handling
[[context.hpp]]
==== `context.hpp`
====
.#include <boost/leaf/context.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... E>
class context
{
context( context const & ) = delete;
context & operator=( context const & ) = delete;
public:
context() noexcept;
context( context && x ) noexcept;
~context() noexcept;
void activate() noexcept;
void deactivate() noexcept;
bool is_active() const noexcept;
void propagate( error_id ) noexcept;
void print( std::ostream & os ) const;
template <class R, class... H>
R handle_error( R &, H && ... ) const;
};
//////////////////////////////////////////
template <class... H>
using context_type_from_handlers = typename <<unspecified>>::type;
template <class... H>
BOOST_LEAF_CONSTEXPR context_type_from_handlers<H...> make_context() noexcept;
template <class... H>
BOOST_LEAF_CONSTEXPR context_type_from_handlers<H...> make_context( H && ... ) noexcept;
template <class... H>
context_ptr make_shared_context() noexcept;
template <class... H>
context_ptr make_shared_context( H && ... ) noexcept;
} }
----
[.text-right]
Reference: <<context>> | <<context_type_from_handlers>> | <<make_context>> | <<make_shared_context>>
====
[[handle_errors.hpp]]
==== `handle_errors.hpp`
====
.#include <boost/leaf/handle_errors.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class TryBlock, class... H>
typename std::decay<decltype(std::declval<TryBlock>()().value())>::type
try_handle_all( TryBlock && try_block, H && ... h );
template <class TryBlock, class... H>
typename std::decay<decltype(std::declval<TryBlock>()())>::type
try_handle_some( TryBlock && try_block, H && ... h );
template <class TryBlock, class... H>
typename std::decay<decltype(std::declval<TryBlock>()())>::type
try_catch( TryBlock && try_block, H && ... h );
//////////////////////////////////////////
class error_info
{
//No public constructors
public:
error_id error() const noexcept;
bool exception_caught() const noexcept;
std::exception const * exception() const noexcept;
friend std::ostream & operator<<( std::ostream & os, error_info const & x );
};
class diagnostic_info: public error_info
{
//No public constructors
friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x );
};
class verbose_diagnostic_info: public error_info
{
//No public constructors
friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x );
};
} }
----
[.text-right]
Reference: <<try_handle_all>> | <<try_handle_some>> | <<try_catch>> | <<error_info>> | <<diagnostic_info>> | <<verbose_diagnostic_info>>
====
[[handle_errors.hpp]]
==== `to_variant.hpp`
====
.#include <boost/leaf/to_variant.hpp>
[source,c++]
----
namespace boost { namespace leaf {
// Requires at least C++17
template <class... E, class TryBlock>
std::variant<
typename std::decay<decltype(std::declval<TryBlock>()().value())>::type
std::tuple<
std::optional<E>...>>
to_variant( TryBlock && try_block );
} }
----
[.text-right]
Reference: <<to_variant>>
====
[[pred.hpp]]
==== `pred.hpp`
====
.#include <boost/leaf/pred.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
struct is_predicate: std::false_type
{
};
template <class E, auto... V>
struct match
{
E matched;
// Other members not specified
};
template <class E, auto... V>
struct is_predicate<match<E, V...>>: std::true_type
{
};
template <class E, auto... V>
struct match_value
{
E matched;
// Other members not specified
};
template <class E, auto... V>
struct is_predicate<match_value<E, V...>>: std::true_type
{
};
template <auto, auto...>
struct match_member;
template <class E, class T, T E::* P, auto... V>
struct member<P, V...>
{
E matched;
// Other members not specified
};
template <auto P, auto... V>
struct is_predicate<match_member<P, V...>>: std::true_type
{
};
template <class... Ex>
struct catch_
{
std::exception const & matched;
// Other members not specified
};
template <class Ex>
struct catch_<Ex>
{
Ex const & matched;
// Other members not specified
};
template <class... Ex>
struct is_predicate<catch_<Ex...>>: std::true_type
{
};
template <class Pred>
struct if_not
{
E matched;
// Other members not specified
};
template <class Pred>
struct is_predicate<if_not<Pred>>: std::true_type
{
};
template <class ErrorCodeEnum>
bool category( std::error_code const & ec ) noexcept;
template <class Enum, class EnumType = Enum>
struct condition;
} }
----
[.text-right]
Reference: <<match>> | <<match_value>> | <<match_member>> | <<catch_>> | <<if_not>> | <<category,`category`>> | <<condition,`condition`>>
====
[[functions]]
== Reference: Functions
TIP: The contents of each Reference section are organized alphabetically.
'''
[[activate_context]]
=== `activate_context`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class Ctx>
context_activator<Ctx> activate_context( Ctx & ctx ) noexcept
{
return context_activator<Ctx>(ctx);
}
} }
----
[.text-right]
<<context_activator>>
.Example:
[source,c++]
----
leaf::context<E1, E2, E3> ctx;
{
auto active_context = activate_context(ctx); <1>
} <2>
----
<1> Activate `ctx`.
<2> Automatically deactivate `ctx`.
'''
[[capture]]
=== `capture`
.#include <boost/leaf/capture.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class F, class... A>
decltype(std::declval<F>()(std::forward<A>(std::declval<A>())...))
capture(std::shared_ptr<polymorphic_context> && ctx, F && f, A... a);
} }
----
[.text-right]
<<polymorphic_context>>
This function can be used to capture error objects stored in a <<context>> in one thread and transport them to a different thread for handling, either in a `<<result,result>><T>` object or in an exception.
Returns: :: The same type returned by `F`.
Effects: :: Uses an internal <<context_activator>> to <<context::activate>> `*ctx`, then invokes `std::forward<F>(f)(std::forward<A>(a)...)`. Then:
+
--
* If the returned value `r` is not a `result<T>` type (see <<is_result_type>>), it is forwarded to the caller.
* Otherwise:
** If `!r`, the return value of `capture` is initialized with `ctx`;
+
NOTE: An object of type `leaf::<<result,result>><T>` can be initialized with a `std::shared_ptr<leaf::polymorphic_context>`.
+
** otherwise, it is initialized with `r`.
--
+
In case `f` throws, `capture` catches the exception in a `std::exception_ptr`, and throws a different exception of unspecified type that transports both the `std::exception_ptr` as well as `ctx`. This exception type is recognized by <<try_catch>>, which automatically unpacks the original exception and propagates the contents of `*ctx` (presumably, in a different thread).
TIP: See also <<tutorial-async>> from the Tutorial.
'''
[[context_type_from_handlers]]
=== `context_type_from_handlers`
.#include <boost/leaf/context.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... H>
using context_type_from_handlers = typename <<unspecified>>::type;
} }
----
.Example:
[source,c++]
----
auto error_handlers = std::make_tuple(
[](e_this const & a, e_that const & b)
{
....
},
[](leaf::diagnostic_info const & info)
{
....
},
.... );
leaf::context_type_from_handlers<decltype(error_handlers)> ctx; <1>
----
<1> `ctx` will be of type `context<e_this, e_that>`, deduced automatically from the specified error handlers.
TIP: Alternatively, a suitable context may be created by calling <<make_context>>, or allocated dynamically by calling <<make_shared_context>>.
'''
[[current_error]]
=== `current_error`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
error_id current_error() noexcept;
} }
----
Returns: :: The `error_id` value returned the last time <<new_error>> was invoked from the calling thread.
TIP: See also <<on_error>>.
'''
[[exception_to_result]]
=== `exception_to_result`
[source,c++]
.#include <boost/leaf/exception.hpp>
----
namespace boost { namespace leaf {
template <class... Ex, class F>
<<result<T>-deduced>> exception_to_result( F && f ) noexcept;
} }
----
This function can be used to catch exceptions from a lower-level library and convert them to `<<result,result>><T>`.
Returns: :: Where `f` returns a type `T`, `exception_to_result` returns `leaf::result<T>`.
Effects: ::
. Catches all exceptions, then captures `std::current_exception` in a `std::exception_ptr` object, which is <<tutorial-loading,loaded>> with the returned `result<T>`.
. Attempts to convert the caught exception, using `dynamic_cast`, to each type `Ex~i~` in `Ex...`. If the cast to `Ex~i~` succeeds, the `Ex~i~` slice of the caught exception is loaded with the returned `result<T>`.
TIP: An error handler that takes an argument of an exception type (that is, of a type that derives from `std::exception`) will work correctly whether the object is thrown as an exception or communicated via <<new_error>> (or converted using `exception_to_result`).
.Example:
[source,c++]
----
int compute_answer_throws();
//Call compute_answer, convert exceptions to result<int>
leaf::result<int> compute_answer()
{
return leaf::exception_to_result<ex_type1, ex_type2>(compute_answer_throws());
}
----
At a later time we can invoke <<try_handle_some>> / <<try_handle_all>> as usual, passing handlers that take `ex_type1` or `ex_type2`, for example by reference:
[source,c++]
----
return leaf::try_handle_some(
[] -> leaf::result<void>
{
BOOST_LEAF_AUTO(answer, compute_answer());
//Use answer
....
return { };
},
[](ex_type1 & ex1)
{
//Handle ex_type1
....
return { };
},
[](ex_type2 & ex2)
{
//Handle ex_type2
....
return { };
},
[](std::exception_ptr const & p)
{
//Handle any other exception from compute_answer.
....
return { };
} );
----
[.text-right]
<<try_handle_some>> | <<result>> | <<BOOST_LEAF_AUTO>>
WARNING: When a handler takes an argument of an exception type (that is, a type that derives from `std::exception`), if the object is thrown, the argument will be matched dynamically (using `dynamic_cast`); otherwise (e.g. after being converted by `exception_to_result`) it will be matched based on its static type only (which is the same behavior used for types that do not derive from `std::exception`).
TIP: See also <<tutorial-exception_to_result>> from the tutorial.
'''
[[make_context]]
=== `make_context`
.#include <boost/leaf/context.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... H>
context_type_from_handlers<H...> make_context() noexcept
{
return { };
}
template <class... H>
context_type_from_handlers<H...> make_context( H && ... ) noexcept
{
return { };
}
} }
----
[.text-right]
<<context_type_from_handlers>>
.Example:
[source,c++]
----
auto ctx = leaf::make_context( <1>
[]( e_this ) { .... },
[]( e_that ) { .... } );
----
<1> `decltype(ctx)` is `leaf::context<e_this, e_that>`.
'''
[[make_shared_context]]
=== `make_shared_context`
.#include <boost/leaf/context.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... H>
context_ptr make_shared_context() noexcept
{
return std::make_shared<leaf_detail::polymorphic_context_impl<context_type_from_handlers<H...>>>();
}
template <class... H>
context_ptr make_shared_context( H && ... ) noexcept
{
return std::make_shared<leaf_detail::polymorphic_context_impl<context_type_from_handlers<H...>>>();
}
} }
----
[.text-right]
<<context_type_from_handlers>>
TIP: See also <<tutorial-async>> from the tutorial.
'''
[[new_error]]
=== `new_error`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... Item>
error_id new_error(Item && ... item) noexcept;
} }
----
Requires: :: Each of the `Item...` types must be no-throw movable.
Effects: :: As if:
+
[source,c++]
----
error_id id = <<generate-new-unique-id>>;
return id.load(std::forward<Item>(item)...);
----
Returns: :: A new `error_id` value, which is unique across the entire program.
Ensures: :: `id.value()!=0`, where `id` is the returned `error_id`.
NOTE: `new_error` discards error objects which are not used in any active error handling calling scope.
CAUTION: When loaded into a `context`, an error object of a type `E` will overwrite the previously loaded object of type `E`, if any.
'''
[[on_error]]
=== `on_error`
.#include <boost/leaf/on_error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... Item>
<<unspecified-type>> on_error(Item && ... item) noexcept;
} }
----
Requires: :: Each of the `Item...` types must be no-throw movable.
Effects: :: All `item...` objects are forwarded and stored, together with the value returned from `std::unhandled_exceptions`, into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed, if an error has occurred since `on_error` was invoked, LEAF will process the stored items to obtain error objects to be associated with the failure.
+
On error, LEAF first needs to deduce an `error_id` value `err` to associate error objects with. This is done using the following logic:
+
--
* If <<new_error>> was invoked (by the calling thread) since the object returned by `on_error` was created, `err` is initialized with the value returned by <<current_error>>;
* Otherwise, if `std::unhandled_exceptions` returns a greater value than it returned during initialization, `err` is initialized with the value returned by <<new_error>>;
* Otherwise, the stored `item...` objects are discarded and no further action is taken (no error has occurred).
--
+
Next, LEAF proceeds similarly to:
+
[source,c++]
----
err.load(std::forward<Item>(item)...);
----
+
The difference is that unlike <<error_id::load>>, `on_error` will not overwrite any error objects already associated with `err`.
TIP: See <<tutorial-on_error>> from the Tutorial.
'''
[[throw_exception]]
=== `throw_exception`
[source,c++]
.#include <boost/leaf/exception.hpp>
----
namespace boost { namespace leaf {
template <class Ex, class... E> <1>
[[noreturn]] void throw_exception( Ex && ex, E && ... e );
template <class E1, class... E> <2>
[[noreturn]] void throw_exception( E1 && e1, E && ... e );
[[noreturn]] void throw_exception(); <3>
template <class Ex, class... E> <4>
[[noreturn]] void throw_exception( error_id id, Ex && ex, E && ... e );
template <class E1, class... E> <5>
[[noreturn]] void throw_exception( error_id id, E1 && e1, E && ... e );
[[noreturn]] void throw_exception( error_id id ); <6>
} }
----
The `throw_exception` function is overloaded: it can be invoked with no arguments, or else there are several alternatives, selected using `std::enable_if` based on the type of the passed arguments. All overloads throw an exception:
<1> Selected if the first argument is not of type `error_id` and is an exception object, that is, iff `Ex` derives publicly from `std::exception`. In this case the thrown exception is of unspecified type which derives publicly from `Ex` *and* from class <<error_id>>, such that:
* its `Ex` subobject is initialized by `std::forward<Ex>(ex)`;
* its `error_id` subobject is initialized by `<<new_error,new_error>>(std::forward<E>(e)...`).
<2> Selected if the first argument is not of type `error_id` and is not an exception object. In this case the thrown exception is of unspecified type which derives publicly from `std::exception` *and* from class `error_id`, such that:
** its `std::exception` subobject is default-initialized;
** its `error_id` subobject is initialized by `<<new_error,new_error>>(std::forward<E1>(e1), std::forward<E>(e)...`).
<3> If the fuction is invoked without arguments, the thrown exception is of unspecified type which derives publicly from `std::exception` *and* from class `error_id`, such that:
** its `std::exception` subobject is default-initialized;
** its `error_id` subobject is initialized by `<<new_error,new_error>>()`.
<4> Selected if the first argument is of type `error_id` and the second argument is an exception object, that is, iff `Ex` derives publicly from `std::exception`. In this case the thrown exception is of unspecified type which derives publicly from `Ex` *and* from class <<error_id>>, such that:
** its `Ex` subobject is initialized by `std::forward<Ex>(ex)`;
** its `error_id` subobject is initialized by `id.<<error_id::load>>(std::forward<E>(e)...)`.
<5> Selected if the first argument is of type `error_id` and the second argument is not an exception object. In this case the thrown exception is of unspecified type which derives publicly from `std::exception` *and* from class `error_id`, such that:
** its `std::exception` subobject is default-initialized;
** its `error_id` subobject is initialized by `id.<<error_id::load>>(std::forward<E1>(e1), std::forward<E>(e)...`).
<6> If `exception` is invoked with just an `error_id` object, the thrown exception is of unspecified type which derives publicly from `std::exception` *and* from class `error_id`, such that:
** its `std::exception` subobject is default-initialized;
** its `error_id` subobject is initialized by copying from `id`.
NOTE: The first three overloads throw an exception object that is associated with a new `error_id`. The second three overloads throw an exception object that is associated with the specified `error_id`.
.Example 1:
[source,c++]
----
struct my_exception: std::exception { };
leaf::throw_exception(my_exception{}); <1>
----
<1> Throws an exception of a type that derives from `error_id` and from `my_exception` (because `my_exception` derives from `std::exception`).
.Example 2:
[source,c++]
----
enum class my_error { e1=1, e2, e3 }; <1>
leaf::throw_exception(my_error::e1);
----
<1> Throws an exception of a type that derives from `error_id` and from `std::exception` (because `my_error` does not derive from `std::exception`).
NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` with the returned object, use <<BOOST_LEAF_THROW_EXCEPTION>> instead of `leaf::throw_exception`.
'''
[[to_variant]]
=== `to_variant`
.#include <boost/leaf/to_variant.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... E, class TryBlock>
std::variant<
typename std::decay<decltype(std::declval<TryBlock>()().value())>::type
std::tuple<
std::optional<E>...>>
to_variant( TryBlock && try_block );
} }
----
Requires: ::
* This function is only available under {CPP}-17 or newer.
* The `try_block` function may not take any arguments.
* The type returned by the `try_block` function must be a `result<T>` type (see <<is_result_type>>). It is valid for the `try_block` to return `leaf::<<result,result>><T>`, however this is not a requirement.
The `to_variant` function uses <<try_handle_all>> internally to invoke the `try_block` and capture the result in a `std::variant`. On success, the variant contains the `T` object from the produced `result<T>`. Otherwise, the variant contains a `std::tuple` where each `std::optional` element contains an object of type `E~i~` from the user-supplied sequence `E...`, or is empty if the failure did not produce an error object of that type.
.Example:
[source,c++]
----
enum class E1 { e11, e12, e13 };
enum class E2 { e21, e22, e23 };
enum class E3 { e31, e32, e33 };
....
auto v = leaf::to_variant<E1, E2, E3>(
[]() -> leaf::result<int>
{
return leaf::new_error( E1::e12, E3::e33 );
} );
assert(v.index() == 1); <1>
auto t = std::get<1>(v); <2>
assert(std::get<0>(t).value() == E1::e12); <3>
assert(!std::get<1>(t).has_value()); <4>
assert(std::get<2>(t).value() == E3::e33); <3>
----
<1> We report a failure, so the variant must contain the error object tuple, rather than an `int`.
<2> Grab the error tuple.
<3> We communicated an `E1` and an `E3` error object...
<4> ...but not an `E2` error object.
'''
[[try_catch]]
=== `try_catch`
.#include <boost/leaf/handle_errors.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class TryBlock, class... H>
typename std::decay<decltype(std::declval<TryBlock>()())>::type
try_catch( TryBlock && try_block, H && ... h );
} }
----
The `try_catch` function works similarly to <<try_handle_some>>, except that it does not use or understand the semantics of `result<T>` types; instead:
* It assumes that the `try_block` throws to indicate a failure, in which case `try_catch` will attempt to find a suitable handler among `h...`;
* If a suitable handler isn't found, the original exception is re-thrown using `throw;`.
TIP: See <<tutorial-exception_handling>>.
'''
[[try_handle_all]]
=== `try_handle_all`
.#include <boost/leaf/handle_errors.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class TryBlock, class... H>
typename std::decay<decltype(std::declval<TryBlock>()().value())>::type
try_handle_all( TryBlock && try_block, H && ... h );
} }
----
The `try_handle_all` function works similarly to <<try_handle_some>>, except:
* In addition, it requires that at least one of `h...` can be used to handle any error (this requirement is enforced at compile time);
* If the `try_block` returns some `result<T>` type, it must be possible to initialize a value of type `T` with the value returned by each of `h...`, and
* Because it is required to handle all errors, `try_handle_all` unwraps the `result<T>` object `r` returned by the `try_block`, returning `r.value()` instead of `r`.
TIP: See <<tutorial-error_handling>>.
'''
[[try_handle_some]]
=== `try_handle_some`
.#include <boost/leaf/handle_errors.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class TryBlock, class... H>
typename std::decay<decltype(std::declval<TryBlock>()())>::type
try_handle_some( TryBlock && try_block, H && ... h );
} }
----
Requires: ::
* The `try_block` function may not take any arguments.
* The type `R` returned by the `try_block` function must be a `result<T>` type (see <<is_result_type>>). It is valid for the `try_block` to return `leaf::<<result,result>><T>`, however this is not a requirement.
* Each of the `h...` functions:
** must return a type that can be used to initialize an object of the type `R`; in case R is a `result<void>` (that is, in case of success it does not communicate a value), handlers that return `void` are permitted. If such a handler is selected, the `try_handle_some` return value is initialized by `{}`;
** may take any error objects, by value, by (`const`) reference, or as pointer (to `const`);
** may take arguments, by value, of any predicate type: <<catch_>>, <<match>>, <<match_value>>, <<match_member>>, <<if_not>>, or of any user-defined predicate type `Pred` for which `<<is_predicate,is_predicate>><Pred>::value` is `true`;
** may take an <<error_info>> argument by `const &`;
** may take a <<diagnostic_info>> argument by `const &`;
** may take a <<verbose_diagnostic_info>> argument by `const &`.
Effects: ::
* Creates a local `<<context,context>><E...>` object `ctx`, where the `E...` types are automatically deduced from the types of arguments taken by each of `h...`, which guarantees that `ctx` is able to store all of the types required to handle errors.
* Invokes the `try_block`:
** if the returned object `r` indicates success [.underline]#and# the `try_block` did not throw, `r` is forwarded to the caller.
** otherwise, LEAF considers each of the `h...` handlers, in order, until it finds one that it can supply with arguments using the error objects currently stored in `ctx`, associated with `r.error()`. The first such handler is invoked and its return value is used to initialize the return value of `try_handle_some`, which can indicate success if the handler was able to handle the error, or failure if it was not.
+
** if `try_handle_some` is unable to find a suitable handler, it returns `r`.
NOTE: `try_handle_some` is exception-neutral: it does not throw exceptions, however the `try_block` and any of `h...` are permitted to throw.
[[handler_selection_procedure]]
Handler Selection Procedure: ::
+
A handler `h` is suitable to handle the failure reported by `r` iff `try_handle_some` is able to produce values to pass as its arguments, using the error objects currently available in `ctx`, associated with the error ID obtained by calling `r.error()`. As soon as it is determined that an argument value can not be produced, the current handler is dropped and the selection process continues with the next handler, if any.
+
The return value of `r.error()` must be implicitly convertible to <<error_id>>. Naturally, the `leaf::result` template satisfies this requirement. If an external `result` type is used instead, usually `r.error()` would return a `std::error_code`, which is able to communicate LEAF error IDs; see <<tutorial-interoperability>>.
+
If `err` is the `error_id` obtained from `r.error()`, each argument `a~i~` taken by the handler currently under consideration is produced as follows:
+
* If `a~i~` is of type `A~i~`, `A~i~ const&` or `A~i~&`:
+
--
** If an error object of type `A~i~`, associated with `err`, is currently available in `ctx`, `a~i~` is initialized with a reference to that object; otherwise
** If `A~i~` derives from `std::exception`, and the `try_block` throws an object `ex` of type that derives from `std::exception`, LEAF obtains `A~i~* p = dynamic_cast<A~i~*>(&ex)`. The handler is dropped if `p` is null, otherwise `a~i~` is initialized with `*p`.
** Otherwise the handler is dropped.
--
+
.Example:
[source,c++]
----
....
auto r = leaf::try_handle_some(
[]() -> leaf::result<int>
{
return f();
},
[](leaf::e_file_name const & fn) <1>
{
std::cerr << "File Name: \"" << fn.value << '"' << std::endl; <2>
return 1;
} );
----
+
[.text-right]
<<result>> | <<e_file_name>>
+
<1> In case the `try_block` indicates a failure, this handler will be selected if `ctx` stores an `e_file_name` associated with the error. Because this is the only supplied handler, if an `e_file_name` is not available, `try_handle_some` will return the `leaf::result<int>` returned by `f`.
<2> Print the file name, handle the error.
+
* If `a~i~` is of type `A~i~` `const*` or `A~i~*`, `try_handle_some` is always able to produce it: first it attempts to produce it as if it is taken by reference; if that fails, rather than dropping the handler, `a~i~` is initialized with `0`.
+
.Example:
[source,c++]
----
....
try_handle_some(
[]() -> leaf::result<int>
{
return f();
},
[](leaf::e_file_name const * fn) <1>
{
if( fn ) <2>
std::cerr << "File Name: \"" << fn->value << '"' << std::endl;
return 1;
} );
}
----
+
[.text-right]
<<result>> | <<e_file_name>>
+
<1> This handler can be selected to handle any error, because it takes `e_file_name` as a `const *` (and nothing else).
<2> If an `e_file_name` is available with the current error, print it.
+
* If `a~i~` is of a predicate type `Pred` (for which `<<is_predicate,is_predicate>><Pred>::value` is `true`), `E` is deduced as `typename Pred::error_type`, and then:
** If `E` is not `void`, and an error object `e` of type `E`, associated with `err`, is not currently stored in `ctx`, the handler is dropped; otherwise the handler is dropped if the expression `Pred::evaluate(e)` returns `false`.
** if `E` is `void`, and a `std::exception` was not caught, the handler is dropped; otherwise the handler is dropped if the expression `Pred::evaluate(e)`, where `e` is of type `std::exception const &`, returns `false`.
** To invoke the handler, the `Pred` argument `a~i~` is initialized with `Pred{e}`.
+
NOTE: See also: <<predicates,Predicates>>.
+
* If `a~i~` is of type `error_info const &`, `try_handle_some` is always able to produce it.
+
.Example:
[source,c++]
----
....
try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[](leaf::error_info const & info) <1>
{
std::cerr << "leaf::error_info:" << std::endl << info; <2>
return info.error(); <3>
} );
----
+
[.text-right]
<<result>> | <<error_info>>
+
<1> This handler matches any error.
<2> Print error information.
<3> Return the original error, which will be returned out of `try_handle_some`.
+
* If `a~i~` is of type `diagnostic_info const &`, `try_handle_some` is always able to produce it.
+
.Example:
[source,c++]
----
....
try_handle_some(
[]
{
return f(); // throws
},
[](leaf::diagnostic_info const & info) <1>
{
std::cerr << "leaf::diagnostic_information:" << std::endl << info; <2>
return info.error(); <3>
} );
----
+
[.text-right]
<<result>> | <<diagnostic_info>>
+
<1> This handler matches any error.
<2> Print diagnostic information, including limited information about dropped error objects.
<3> Return the original error, which will be returned out of `try_handle_some`.
+
* If `a~i~` is of type `verbose_diagnostic_info const &`, `try_handle_some` is always able to produce it.
+
.Example:
[source,c++]
----
....
try_handle_some(
[]
{
return f(); // throws
},
[](leaf::verbose_diagnostic_info const & info) <1>
{
std::cerr << "leaf::verbose_diagnostic_information:" << std::endl << info; <2>
return info.error(); <3>
} );
----
+
[.text-right]
<<result>> | <<verbose_diagnostic_info>>
+
<1> This handler matches any error.
<2> Print verbose diagnostic information, including values of dropped error objects.
<3> Return the original error, which will be returned out of `try_handle_some`.
[[types]]
== Reference: Types
TIP: The contents of each Reference section are organized alphabetically.
'''
[[context]]
=== `context`
.#include <boost/leaf/context.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... E>
class context
{
context( context const & ) = delete;
context & operator=( context const & ) = delete;
public:
context() noexcept;
context( context && x ) noexcept;
~context() noexcept;
void activate() noexcept;
void deactivate() noexcept;
bool is_active() const noexcept;
void propagate( error_id ) noexcept;
void print( std::ostream & os ) const;
template <class R, class... H>
R handle_error( error_id, H && ... ) const;
};
template <class... H>
using context_type_from_handlers = typename <<unspecified>>::type;
} }
----
[.text-right]
<<context::context>> | <<context::activate>> | <<context::deactivate>> | <<context::is_active>> | <<context::propagate>> | <<context::print>> | <<context::handle_error>> | <<context_type_from_handlers>>
The `context` class template provides storage for each of the specified `E...` types. Typically, `context` objects are not used directly; they're created internally when the <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> functions are invoked, instantiated with types that are automatically deduced from the types of the arguments of the passed handlers.
Independently, users can create `context` objects if they need to capture error objects and then transport them, by moving the `context` object itself.
Even in that case it is recommended that users do not instantiate the `context` template by explicitly listing the `E...` types they want it to be able to store. Instead, use <<context_type_from_handlers>> or call the <<make_context>> function template, which deduce the correct `E...` types from a captured list of handler function objects.
To be able to load up error objects in a `context` object, it must be activated. Activating a `context` object `ctx` binds it to the calling thread, setting thread-local pointers of the stored `E...` types to point to the corresponding storage within `ctx`. It is possible, even likely, to have more than one active `context` in any given thread. In this case, activation/deactivation must happen in a LIFO manner. For this reason, it is best to use a <<context_activator>>, which relies on RAII to activate and deactivate a `context`.
When a `context` is deactivated, it detaches from the calling thread, restoring the thread-local pointers to their pre-`activate` values. Typically, at this point the stored error objects, if any, are either discarded (by default) or moved to corresponding storage in other `context` objects active in the calling thread (if available), by calling <<context::propagate>>.
While error handling typically uses <<try_handle_some>>, <<try_handle_all>> or <<try_catch>>, it is also possible to handle errors by calling the member function <<context::handle_error>>. It takes an <<error_id>>, and attempts to select an error handler based on the error objects stored in `*this`, associated with the passed `error_id`.
TIP: `context` objects can be moved, as long as they aren't active.
WARNING: Moving an active `context` results in undefined behavior.
'''
[[context::context]]
==== Constructors
.#include <boost/leaf/context.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... E>
context<E...>::context() noexcept;
template <class... E>
context<E...>::context( context && x ) noexcept;
} }
----
The default constructor initializes an empty `context` object: it provides storage for, but does not contain any error objects.
The move constructor moves the stored error objects from one `context` to the other.
WARNING: Moving an active `context` object results in undefined behavior.
'''
[[context::activate]]
==== `activate`
.#include <boost/leaf/context.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... E>
void context<E...>::activate() noexcept;
} }
----
Requires: :: `!<<context::is_active,is_active>>()`.
Effects: :: Associates `*this` with the calling thread.
Ensures: :: `<<context::is_active,is_active>>()`.
When a context is associated with a thread, thread-local pointers are set to point each `E...` type in its store, while the previous value of each such pointer is preserved in the `context` object, so that the effect of `activate` can be undone by calling `deactivate`.
When an error object is <<tutorial-loading,loaded>>, it is moved in the last activated (in the calling thread) `context` object that provides storage for its type (note that this may or may not be the last activated `context` object). If no such storage is available, the error object is discarded.
'''
[[context::deactivate]]
==== `deactivate`
.#include <boost/leaf/context.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... E>
void context<E...>::deactivate() noexcept;
} }
----
Requires: ::
* `<<context::is_active,is_active>>()`;
* `*this` must be the last activated `context` object in the calling thread.
Effects: :: Un-associates `*this` with the calling thread.
Ensures: :: `!<<context::is_active,is_active>>()`.
When a context is deactivated, the thread-local pointers that currently point to each individual error object storage in it are restored to their original value prior to calling <<context::activate>>.
'''
[[context::handle_error]]
==== `handle_error`
[source,c++]
.#include <boost/leaf/handle_errors.hpp>
----
namespace boost { namespace leaf {
template <class... E>
template <class R, class... H>
R context<E...>::handle_error( error_id err, H && ... h ) const;
} }
----
This function works similarly to <<try_handle_all>>, but rather than calling a `try_block` and obtaining the <<error_id>> from a returned `result` type, it matches error objects (stored in `*this`, associated with `err`) with a suitable error handler from the `h...` pack.
NOTE: The caller is required to specify the return type `R`. This is because in general the supplied handlers may return different types (which must all be convertible to `R`).
'''
[[context::is_active]]
==== `is_active`
[source,c++]
.#include <boost/leaf/context.hpp>
----
namespace boost { namespace leaf {
template <class... E>
bool context<E...>::is_active() const noexcept;
} }
----
Returns: :: `true` if the `*this` is active in any thread, `false` otherwise.
'''
[[context::print]]
==== `print`
.#include <boost/leaf/context.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... E>
void context<E...>::print( std::ostream & os ) const;
} }
----
Effects: :: Prints all error objects currently stored in `*this`, together with the unique error ID each individual error object is associated with.
'''
[[context::propagate]]
==== `propagate`
.#include <boost/leaf/context.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... E>
void context<E...>::propagate( error_id id ) noexcept;
} }
----
Requires: ::
`!<<context::is_active,is_active>>()`.
Effects: ::
Each stored error object of some type `E` is moved into another `context` object active in the call stack that provides storage for objects of type `E`, if any, or discarded. Target objects are not overwritten if they are associated with the specified `id`, except if `id.value() == 0`.
'''
[[context_activator]]
=== `context_activator`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class Ctx>
class context_activator
{
context_activator( context_activator const & ) = delete;
context_activator & operator=( context_activator const & ) = delete;
public:
explicit context_activator( Ctx & ctx ) noexcept;
context_activator( context_activator && ) noexcept;
~context_activator() noexcept;
};
} }
----
`context_activator` is a simple class that activates and deactivates a <<context>> using RAII:
If `<<context::is_active,ctx.is_active>>`() is `true` at the time the `context_activator` is initialized, the constructor and the destructor have no effects. Otherwise:
* The constructor stores a reference to `ctx` in `*this` and calls `<<context::activate,ctx.activate>>`().
* The destructor:
** Has no effects if `ctx.is_active()` is `false` (that is, it is valid to call <<context::deactivate>> manually, before the `context_activator` object expires);
** Otherwise, calls `<<context::deactivate,ctx.deactivate>>`().
For automatic deduction of `Ctx`, use <<activate_context>>.
'''
[[diagnostic_info]]
=== `diagnostic_info`
.#include <boost/leaf/handle_errors.hpp>
[source,c++]
----
namespace boost { namespace leaf {
class diagnostic_info: public error_info
{
//Constructors unspecified
friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x );
};
} }
----
Handlers passed to <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> may take an argument of type `diagnostic_info const &` if they need to print diagnostic information about the error.
The message printed by `operator<<` includes the message printed by `error_info`, followed by basic information about error objects that were communicated to LEAF (to be associated with the error) for which there was no storage available in any active <<context>> (these error objects were discarded by LEAF, because no handler needed them).
The additional information is limited to the type name of the first such error object, as well as their total count.
[NOTE]
--
The behavior of `diagnostic_info` (and <<verbose_diagnostic_info>>) is affected by the value of the macro `BOOST_LEAF_CFG_DIAGNOSTICS`:
* If it is 1 (the default), LEAF produces `diagnostic_info` but only if an active error handling context on the call stack takes an argument of type `diagnostic_info`;
* If it is 0, the `diagnostic_info` functionality is stubbed out even for error handling contexts that take an argument of type `diagnostic_info`. This could shave a few cycles off the error path in some programs (but it is probably not worth it).
--
'''
[[error_id]]
=== `error_id`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
class error_id
{
public:
error_id() noexcept;
template <class Enum>
result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
error_id( std::error_code const & ec ) noexcept;
int value() const noexcept;
explicit operator bool() const noexcept;
std::error_code to_error_code() const noexcept;
friend bool operator==( error_id a, error_id b ) noexcept;
friend bool operator!=( error_id a, error_id b ) noexcept;
friend bool operator<( error_id a, error_id b ) noexcept;
template <class... Item>
error_id load( Item && ... item ) const noexcept;
friend std::ostream & operator<<( std::ostream & os, error_id x );
};
bool is_error_id( std::error_code const & ec ) noexcept;
template <class... E>
error_id new_error( E && ... e ) noexcept;
error_id current_error() noexcept;
} }
----
[.text-right]
<<error_id::error_id>> | <<error_id::value>> | <<error_id::operator_bool>> | <<error_id::to_error_code>> | <<error_id::comparison_operators>> | <<error_id::load>> | <<is_error_id>> | <<new_error>> | <<current_error>>
Values of type `error_id` identify a specific occurrence of a failure across the entire program. They can be copied, moved, assigned to, and compared to other `error_id` objects. They're as efficient as an `int`.
'''
[[error_id::error_id]]
==== Constructors
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
error_id::error_id() noexcept = default;
template <class Enum>
error_id::error_id( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
error_id::error_id( std::error_code const & ec ) noexcept;
} }
----
A default-initialized `error_id` object does not represent a specific failure. It compares equal to any other default-initialized `error_id` object. All other `error_id` objects identify a specific occurrence of a failure.
CAUTION: When using an object of type `error_id` to initialize a `result<T>` object, it will be initialized in error state, even when passing a default-initialized `error_id` value.
Converting an `error_id` object to `std::error_code` uses an unspecified `std::error_category` which LEAF recognizes. This allows an `error_id` to be transported through interfaces that work with `std::error_code`. The `std::error_code` constructor allows the original `error_id` to be restored.
TIP: To check if a given `std::error_code` is actually carrying an `error_id`, use <<is_error_id>>.
Typically, users create new `error_id` objects by invoking <<new_error>>. The constructor that takes `std::error_code`, and the one that takes a type `Enum` for which `std::is_error_code_enum<Enum>::value` is `true`, have the following effects:
* If `ec.value()` is `0`, the effect is the same as using the default constructor.
* Otherwise, if `<<is_error_id,is_error_id>>(ec)` is `true`, the original `error_id` value is used to initialize `*this`;
* Otherwise, `*this` is initialized by the value returned by <<new_error>>, while `ec` is passed to `load`, which enables handlers used with `try_handle_some`, `try_handle_all` or `try_catch` to receive it as an argument of type `std::error_code`.
'''
[[is_error_id]]
==== `is_error_id`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
bool is_error_id( std::error_code const & ec ) noexcept;
} }
----
Returns: :: `true` if `ec` uses the LEAF-specific `std::error_category` that identifies it as carrying an error ID rather than another error code; otherwise returns `false`.
'''
[[error_id::load]]
==== `load`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... Item>
error_id error_id::load( Item && ... item ) const noexcept;
} }
----
Requires: :: Each of the `Item...` types must be no-throw movable.
Effects: ::
* If `value()==0`, all of `item...` are discarded and no further action is taken.
* Otherwise, what happens with each `item` depends on its type:
** If it is a function that takes a single argument of some type `E &`, that function is called with the object of type `E` currently associated with `*this`. If no such object exists, a default-initialized object is associated with `*this` and then passed to the function.
** If it is a function that takes no arguments, than function is called to obtain an error object, which is associated with `*this`.
** Otherwise, the `item` itself is assumed to be an error object, which is associated with `*this`.
Returns: :: `*this`.
NOTE: `load` discards error objects which are not used in any active error handling calling scope.
CAUTION: When loaded into a `context`, an error object of a type `E` will overwrite the previously loaded object of type `E`, if any.
See also: :: <<tutorial-loading>>.
'''
[[error_id::comparison_operators]]
==== `operator==`, `!=`, `<`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
friend bool operator==( error_id a, error_id b ) noexcept;
friend bool operator!=( error_id a, error_id b ) noexcept;
friend bool operator<( error_id a, error_id b ) noexcept;
} }
----
These functions have the usual semantics, comparing `a.value()` and `b.value()`.
NOTE: The exact strict weak ordering implemented by `operator<` is not specified. In particular, if for two `error_id` objects `a` and `b`, `a < b` is true, it does not follow that the failure identified by `a` ocurred earlier than the one identified by `b`.
'''
[[error_id::operator_bool]]
==== `operator bool`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
explicit error_id::operator bool() const noexcept;
} }
----
Effects: :: As if `return value()!=0`.
'''
[[error_id::to_error_code]]
==== `to_error_code`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
std::error_code error_id::to_error_code() const noexcept;
} }
----
Effects: :: Returns a `std::error_code` with the same `value()` as `*this`, using an unspecified `std::error_category`.
NOTE: The returned object can be used to initialize an `error_id`, in which case the original `error_id` value will be restored.
TIP: Use <<is_error_id>> to check if a given `std::error_code` carries an `error_id`.
'''
[[error_id::value]]
==== `value`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
int error_id::value() const noexcept;
} }
----
Effects: ::
* If `*this` was initialized using the default constructor, returns 0.
* Otherwise returns an `int` that is guaranteed to not be 0: a program-wide unique identifier of the failure.
'''
[[error_monitor]]
=== `error_monitor`
.#include <boost/leaf/on_error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
class error_monitor
{
public:
error_monitor() noexcept;
error_id check() const noexcept;
error_id assigned_error_id( E && ... e ) const noexcept;
};
} }
----
This class helps obtain an <<error_id>> to associate error objects with, when augmenting failures communicated using LEAF through uncooperative APIs that do not use LEAF to report errors (and therefore do not return an `error_id` on error).
The common usage of this class is as follows:
[source,c++]
----
error_code compute_value( int * out_value ) noexcept; <1>
leaf::error<int> augmenter() noexcept
{
leaf::error_monitor cur_err; <2>
int val;
auto ec = compute_value(&val);
if( failure(ec) )
return cur_err.assigned_error_id().load(e1, e2, ...); <3>
else
return val; <4>
}
----
<1> Uncooperative third-party API that does not use LEAF, but may result in calling a user callback that does use LEAF. In case our callback reports a failure, we'll augment it with error objects available in the calling scope, even though `compute_value` can not communicate an <<error_id>>.
<2> Initialize an `error_monitor` object.
<3> The call to `compute_value` has failed:
- If <<new_error>> was invoked (by the calling thread) after the `augment` object was initialized, `assigned_error_id` returns the last `error_id` returned by `new_error`. This would be the case if the failure originates in our callback (invoked internally by `compute_value`).
- Else, `assigned_error_id` invokes `new_error` and returns that `error_id`.
<4> The call was successful, return the computed value.
The `check` function works similarly, but instead of invoking `new_error` it returns a default-initialized `error_id`.
TIP: See <<tutorial-on_error_in_c_callbacks>>.
'''
[[e_api_function]]
=== `e_api_function`
.#include <boost/leaf/common.hpp>
[source,c++]
----
namespace boost { namespace leaf {
struct e_api_function {char const * value;};
} }
----
The `e_api_function` type is designed to capture the name of the API function that failed. For example, if you're reporting an error from `fread`, you could use `leaf::e_api_function {"fread"}`.
WARNING: The passed value is stored as a C string (`char const *`), so `value` should only be initialized with a string literal.
'''
[[e_at_line]]
=== `e_at_line`
.#include <boost/leaf/common.hpp>
[source,c++]
----
namespace boost { namespace leaf {
struct e_at_line { int value; };
} }
----
`e_at_line` can be used to communicate the line number when reporting errors (for example parse errors) about a text file.
'''
[[e_errno]]
=== `e_errno`
.#include <boost/leaf/common.hpp>
[source,c++]
----
namespace boost { namespace leaf {
struct e_errno
{
int value;
explicit e_errno(int value=errno);
friend std::ostream & operator<<( std::ostream & os, e_errno const & err );
};
} }
----
By default, the constructor initializes `value` with `errno`, but the caller can pass a specific error code instead. When printed in automatically-generated diagnostic messages, `e_errno` objects use `strerror` to convert the error code to string.
'''
[[e_file_name]]
=== `e_file_name`
.#include <boost/leaf/common.hpp>
[source,c++]
----
namespace boost { namespace leaf {
struct e_file_name { std::string value; };
} }
----
When a file operation fails, you could use `e_file_name` to store the name of the file.
TIP: It is probably better to define your own file name wrappers to avoid clashes if different modules all use `leaf::e_file_name`. It is best to use a descriptive name that clarifies what kind of file name it is (e.g. `e_source_file_name`, `e_destination_file_name`), or at least define `e_file_name` in a given module's namespace.
'''
[[e_LastError]]
=== `e_LastError`
.#include <boost/leaf/common.hpp>
[source,c++]
----
namespace boost { namespace leaf {
namespace windows
{
struct e_LastError
{
unsigned value;
explicit e_LastError(unsigned value);
#if BOOST_LEAF_CFG_WIN32
e_LastError();
friend std::ostream & operator<<(std::ostream &, e_LastError const &);
#endif
};
}
} }
----
`e_LastError` is designed to communicate `GetLastError()` values on Windows. The default constructor initializes `value` via `GetLastError()`. See <<configuration>>.
'''
[[e_source_location]]
=== `e_source_location`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
struct e_source_location
{
char const * file;
int line;
char const * function;
friend std::ostream & operator<<( std::ostream & os, e_source_location const & x );
};
} }
----
The <<BOOST_LEAF_NEW_ERROR>> and <<BOOST_LEAF_THROW_EXCEPTION>> macros capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` into a `e_source_location` object.
'''
[[e_type_info_name]]
=== `e_type_info_name`
.#include <boost/leaf/common.hpp>
[source,c++]
----
namespace boost { namespace leaf {
struct e_type_info_name { char const * value; };
} }
----
`e_type_info_name` is designed to store the return value of `std::type_info::name`.
'''
[[error_info]]
=== `error_info`
.#include <boost/leaf/handle_errors.hpp>
[source,c++]
----
namespace boost { namespace leaf {
class error_info
{
//Constructors unspecified
public:
error_id error() const noexcept;
bool exception_caught() const noexcept;
std::exception const * exception() const noexcept;
friend std::ostream & operator<<( std::ostream & os, error_info const & x );
};
} }
----
Handlers passed to error handling functions such as <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> may take an argument of type `error_info const &` to receive generic information about the error being handled.
The `error` member function returns the program-wide unique <<error_id>> of the error.
The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked to handle an exception, `false` otherwise.
If handling an exception, the `exception` member function returns a pointer to the `std::exception` subobject of the caught exception, or `0` if that exception could not be converted to `std::exception`.
WARNING: It is illegal to call the `exception` member function unless `exception_caught()` is `true`.
The `operator<<` overload prints diagnostic information about each error object currently stored in the <<context>> local to the <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> scope that invoked the handler, but only if it is associated with the <<error_id>> returned by `error()`.
'''
[[polymorphic_context]]
=== `polymorphic_context`
.#include <boost/leaf/error.hpp>
[source,c++]
----
namespace boost { namespace leaf {
class polymorphic_context
{
protected:
polymorphic_context() noexcept;
~polymorphic_context() noexcept;
public:
virtual void activate() noexcept = 0;
virtual void deactivate() noexcept = 0;
virtual bool is_active() const noexcept = 0;
virtual void propagate( error_id ) noexcept = 0;
virtual void print( std::ostream & ) const = 0;
};
} }
----
The `polymorphic_context` class is an abstract base type which can be used to erase the type of the exact instantiation of the <<context>> class template used. See <<make_shared_context>>.
'''
[[result]]
=== `result`
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
class result
{
public:
result() noexcept;
result( T && v ) noexcept;
result( T const & v );
template <class U>
result( U &&, <<enabled_if_T_can_be_inited_with_U>> );
result( error_id err ) noexcept;
result( std::shared_ptr<polymorphic_context> && ctx ) noexcept;
template <class Enum>
result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
result( std::error_code const & ec ) noexcept;
result( result && r ) noexcept;
template <class U>
result( result<U> && r ) noexcept;
result & operator=( result && r ) noexcept;
template <class U>
result & operator=( result<U> && r ) noexcept;
bool has_value() const noexcept;
bool has_error() const noexcept;
explicit operator bool() const noexcept;
T const & value() const;
T & value();
T const * operator->() const noexcept;
T * operator->() noexcept;
T const & operator*() const noexcept;
T & operator*() noexcept;
<<unspecified-type>> error() noexcept;
template <class... Item>
error_id load( Item && ... item ) noexcept;
};
template <>
class result<void>
{
public:
result() noexcept;
result( error_id err ) noexcept;
result( std::shared_ptr<polymorphic_context> && ctx ) noexcept;
template <class Enum>
result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
result( std::error_code const & ec ) noexcept;
result( result && r ) noexcept;
template <class U>
result( result<U> && r ) noexcept;
result & operator=( result && r ) noexcept;
template <class U>
result & operator=( result<U> && r ) noexcept;
bool has_value() const noexcept;
bool has_error() const noexcept;
explicit operator bool() const noexcept;
void value() const;
<<unspecified-type>> error() noexcept;
template <class... Item>
error_id load( Item && ... item ) noexcept;
};
struct bad_result: std::exception { };
} }
----
[.text-right]
<<result::result>> | <<result::operator_eq>> | <<result::has_value>> | <<result::has_error>> | <<result::operator_bool>> | <<result::value>> | <<result::operator_ptr>> | <<result::operator_deref>> | <<result::error>> | <<result::load>>
The `result<T>` type can be returned by functions which produce a value of type `T` but may fail doing so.
Requires: :: `T` must be movable, and its move constructor may not throw.
Invariant: :: A `result<T>` object is in one of three states:
* Value state, in which case it contains an object of type `T`, and <<result::value>>/<<result::operator_deref>>/<<result::operator_ptr>> can be used to access the contained value.
* Error state, in which case it contains an error ID, and calling <<result::value>> throws `leaf::bad_result`.
* Error capture state, which is the same as the Error state, but in addition to the error ID, it holds a `std::shared_ptr<<<polymorphic_context,polymorphic_context>>>`.
`result<T>` objects are nothrow-moveable but are not copyable.
'''
[[result::result]]
==== Constructors
--
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
result<T>::result() noexcept;
template <class T>
result<T>::result( T && v ) noexcept; <1>
template <class T>
result<T>::result( T const & v ); <1>
template <class U>
result<T>::result( U && u, <<enabled_if_T_can_be_inited_with_U>> ); <2>
template <class T>
result<T>::result( leaf::error_id err ) noexcept;
template <class T>
template <class Enum>
result<T>::result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
template <class T>
result<T>::result( std::error_code const & ec ) noexcept;
template <class T>
result<T>::result( std::shared_ptr<polymorphic_context> && ctx ) noexcept;
template <class T>
result<T>::result( result && ) noexcept;
template <class T>
template <class U>
result<T>::result( result<U> && ) noexcept;
} }
----
<1> Not available if `T` is `void`.
<2> Available if an object of type `T` can be initialized with `std::forward<U>(u)`. This is to enable e.g. `result<std::string>` to be initialized with a string literal.
--
Requires: :: `T` must be movable, and its move constructor may not throw; or `void`.
Effects: ::
Establishes the `result<T>` invariant:
+
--
* To get a `result<T>` in <<result,Value state>>, initialize it with an object of type `T` or use the default constructor.
* To get a `result<T>` in <<result,Error state>>, initialize it with:
** an <<error_id>> object.
+
CAUTION: Initializing a `result<T>` with a default-initialized `error_id` object (for which `.value()` returns `0`) will still result in <<result,Error state>>!
+
** a `std::error_code` object.
** an object of type `Enum` for which `std::is_error_code_enum<Enum>::value` is `true`.
* To get a `result<T>` in <<result,Error capture state>>, initialize it with a `std::shared_ptr<<<polymorphic_context,polymorphic_context>>>` (which can be obtained by calling e.g. <<make_shared_context>>).
--
+
When a `result` object is initialized with a `std::error_code` object, it is used to initialize an `error_id` object, then the behavior is the same as if initialized with `error_id`.
Throws: ::
* Initializing the `result<T>` in Value state may throw, depending on which constructor of `T` is invoked;
* Other constructors do not throw.
TIP: A `result` that is in value state converts to `true` in boolean contexts. A `result` that is not in value state converts to `false` in boolean contexts.
NOTE: `result<T>` objects are nothrow-moveable but are not copyable.
'''
[[result::error]]
==== `error`
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... E>
<<unspecified-type>> result<T>::error() noexcept;
} }
----
Returns: A proxy object of unspecified type, implicitly convertible to any instance of the `result` class template, as well as to <<error_id>>.
* If the proxy object is converted to some `result<U>`:
** If `*this` is in <<result,Value state>>, returns `result<U>(error_id())`.
** Otherwise the state of `*this` is moved into the returned `result<U>`.
* If the proxy object is converted to an `error_id`:
** If `*this` is in <<result,Value state>>, returns a default-initialized <<error_id>> object.
** If `*this` is in <<result,Error capture state>>, all captured error objects are <<tutorial-loading,loaded>> in the calling thread, and the captured `error_id` value is returned.
** If `*this` is in <<result,Error state>>, returns the stored `error_id`.
* If the proxy object is not used, the state of `*this` is not modified.
WARNING: The returned proxy object refers to `*this`; avoid holding on to it.
'''
[[result::load]]
==== `load`
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
template <class... Item>
error_id result<T>::load( Item && ... item ) noexcept;
} }
----
This member function is designed for use in `return` statements in functions that return `result<T>` to forward additional error objects to the caller.
Effects: :: As if `error_id(thispass:[->]error()).load(std::forward<Item>(item)...)`.
Returns: :: `*this`.
'''
[[result::operator_eq]]
==== `operator=`
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
result<T> & result<T>::operator=( result && ) noexcept;
template <class T>
template <class U>
result<T> & result<T>::operator=( result<U> && ) noexcept;
} }
----
Effects: :: Destroys `*this`, then re-initializes it as if using the appropriate `result<T>` constructor. Basic exception-safety guarantee.
'''
[[result::has_value]]
==== `has_value`
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
bool result<T>::has_value() const noexcept;
} }
----
Returns: :: If `*this` is in <<result,value state>>, returns `true`, otherwise returns `false`.
'''
[[result::has_error]]
==== `has_error`
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
bool result<T>::has_error() const noexcept;
} }
----
Returns: :: If `*this` is in <<result,value state>>, returns `false`, otherwise returns `true`.
'''
[[result::operator_bool]]
==== `operator bool`
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
result<T>::operator bool() const noexcept;
} }
----
Returns: :: If `*this` is in <<result,value state>>, returns `true`, otherwise returns `false`.
'''
[[result::value]]
==== `value`
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
void result<void>::value() const;
template <class T>
T const & result<T>::value() const;
template <class T>
T & result<T>::value();
struct bad_result: std::exception { };
} }
----
[[result::bad_result]]
Effects: :: If `*this` is in <<result,value state>>, returns a reference to the stored value, otherwise throws `bad_result`.
'''
[[result::operator_ptr]]
==== `operatorpass:[->]`
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
T const * result<T>::operator->() const noexcept;
template <class T>
T * result<T>::operator->() noexcept;
} }
----
Returns :: If `*this` is in <<result,value state>>, returns a pointer to the stored value; otherwise returns 0.
'''
[[result::operator_deref]]
==== `operator*`
.#include <boost/leaf/result.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class T>
T const & result<T>::operator*() const noexcept;
template <class T>
T & result<T>::operator*() noexcept;
} }
----
Requires: :: `*this` must be in <<result,value state>>.
Returns :: a reference to the stored value.
'''
[[verbose_diagnostic_info]]
=== `verbose_diagnostic_info`
.#include <boost/leaf/handle_errors.hpp>
[source,c++]
----
namespace boost { namespace leaf {
class verbose_diagnostic_info: public error_info
{
//Constructors unspecified
friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x );
};
} }
----
Handlers passed to error handling functions such as <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> may take an argument of type `verbose_diagnostic_info const &` if they need to print diagnostic information about the error.
The message printed by `operator<<` includes the message printed by `error_info`, followed by information about error objects that were communicated to LEAF (to be associated with the error) for which there was no storage available in any active <<context>> (these error objects were discarded by LEAF, because no handler needed them).
The additional information includes the types and the values of all such error objects.
[NOTE]
--
The behavior of `verbose_diagnostic_info` (and <<diagnostic_info>>) is affected by the value of the macro `BOOST_LEAF_CFG_DIAGNOSTICS`:
* If it is 1 (the default), LEAF produces `verbose_diagnostic_info` but only if an active error handling context on the call stack takes an argument of type `verbose_diagnostic_info`;
* If it is 0, the `verbose_diagnostic_info` functionality is stubbed out even for error handling contexts that take an argument of type `verbose_diagnostic_info`. This could save some cycles on the error path in some programs (but is probably not worth it).
--
WARNING: Using `verbose_diagnostic_info` may allocate memory dynamically, but only if an active error handler takes an argument of type `verbose_diagnostic_info`.
[[predicates]]
== Reference: Predicates
TIP: The contents of each Reference section are organized alphabetically.
A predicate is a special type of error handler argument which enables the <<handler_selection_procedure,handler selection procedure>> to consider the _value_ of available error objects, not only their type; see <<tutorial-predicates>>.
The following predicates are available:
* <<match>>
* <<match_value>>
* <<match_member>>
* <<catch_>>
* <<if_not>>
In addition, any user-defined type `Pred` for which `<<is_predicate,is_predicate>><Pred>::value` is `true` is treated as a predicate. In this case, it is required that:
* `Pred` defines an accessible member type `error_type` to specify the error object type it requires;
* `Pred` defines an accessible static member function `evaluate`, which returns a boolean type, and can be invoked with an object of type `error_type const &`;
* A `Pred` instance can be initialized with an object of type `error_type`.
When an error handler takes an argument of a predicate type `Pred`, the <<handler_selection_procedure,handler selection procedure>> drops the handler if an error object `e` of type `Pred::error_type` is not available. Otherwise, the handler is dropped if `Pred::evaluate(e)` returns `false`. If the handler is invoked, the `Pred` argument is initialized with `Pred{e}`.
NOTE: Predicates are evaluated before the error handler is invoked, and so they may not access dynamic state (of course the error handler itself can access dynamic state, e.g. by means of lambda expression captures).
.Example 1:
[source,c++]
----
enum class my_error { e1 = 1, e2, e3 };
struct my_pred
{
using error_type = my_error; <1>
static bool evaluate(my_error) noexcept; <2>
my_error matched; <3>
}
namespace boost { namespace leaf {
template <>
struct is_predicate<my_pred>: std::true_type
{
};
} }
----
<1> This predicate requires an error object of type `my_error`.
<2> The handler selection procedure will call this function with an object `e` of type `my_error` to evaluate the predicate...
<3> ...and if successful, initialize the `my_pred` error handler argument with `my_pred{e}`.
.Example 2:
[source,c++]
----
struct my_pred
{
using error_type = leaf::e_errno; <1>
static bool evaluate(leaf::e_errno const &) noexcept; <2>
leaf::e_errno const & matched; <3>
}
namespace boost { namespace leaf {
template <>
struct is_predicate<my_pred>: std::true_type
{
};
} }
----
<1> This predicate requires an error object of type <<e_errno>>.
<2> The handler selection procedure will call this function with an object `e` of type `e_errno` to evaluate the predicate...
<3> ...and if successful, initialize the `my_pred` error handler argument with `my_pred{e}`.
'''
[[catch_]]
=== `catch_`
.#include <boost/leaf/pred.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class... Ex>
struct catch_
{
std::exception const & matched;
// Other members not specified
};
template <class Ex>
struct catch_<Ex>
{
Ex const & matched;
// Other members not specified
};
template <class... Ex>
struct is_predicate<catch_<Ex...>>: std::true_type
{
};
} }
----
[.text-right]
<<is_predicate>>
When an error handler takes an argument of type that is an instance of the `catch_` template, the <<handler_selection_procedure,handler selection procedure>> first checks if a `std::exception` was caught. If not, the handler is dropped. Otherwise, the handler is dropped if the caught `std::exception` can not be `dynamic_cast` to any of the specified types `Ex...`.
If the error handler is invoked, the `matched` member can be used to access the exception object.
NOTE: See also: <<tutorial-predicates>>.
TIP: While `catch_` requires that the caught exception object is of type that derives from `std::exception`, it is not required that the `Ex...` types derive from `std::exception`.
.Example 1:
[source,c++]
----
struct ex1: std::exception { };
struct ex2: std::exception { };
leaf::try_catch(
[]
{
return f(); // throws
},
[](leaf::catch_<ex1, ex2> c)
{ <1>
assert(dynamic_cast<ex1 const *>(&c.matched) || dynamic_cast<ex2 const *>(&c.matched));
....
} );
----
<1> The handler is selected if `f` throws an exception of type `ex1` or `ex2`.
.Example 2:
[source,c++]
----
struct ex1: std::exception { };
leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[](ex1 & e)
{ <1>
....
} );
----
<1> The handler is selected if `f` throws an exception of type `ex1`. Notice that if we're interested in only one exception type, as long as that type derives from `std::exception`, the use of `catch_` is not required.
'''
[[if_not]]
=== `if_not`
.#include <boost/leaf/pred.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class P>
struct if_not
{
<<deduced>> matched;
// Other members not specified
};
template <class P>
struct is_predicate<if_not<P>>: std::true_type
{
};
} }
----
[.text-right]
<<is_predicate>>
When an error handler takes an argument of type `if_not<P>`, where `P` is another predicate type, the <<handler_selection_procedure,handler selection procedure>> first checks if an error object of the type `E` required by `P` is available. If not, the handler is dropped. Otherwise, the handler is dropped if `P` evaluates to `true`.
If the error handler is invoked, `matched` can be used to access the matched object `E`.
NOTE: See also <<tutorial-predicates>>.
.Example:
[source,c++]
----
enum class my_enum { e1, e2, e3 };
leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[]( leaf::if_not<leaf::match<my_enum, my_enum::e1, my_enum::e2>> )
{ <1>
....
} );
----
[.text-right]
<<try_handle_some>> | <<match>>
<1> The handler is selected if an object of type `my_enum`, which [.underline]#*does not*# compare equal to `e1` or to `e2`, [.underline]#*is*# associated with the detected error.
'''
[[match]]
=== `match`
.#include <boost/leaf/pred.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class E, auto... V>
class match
{
<<deduced>> matched;
// Other members not specified
};
template <class E, auto... V>
struct is_predicate<match<E, V...>>: std::true_type
{
};
} }
----
[.text-right]
<<is_predicate>>
When an error handler takes an argument of type `match<E, V...>`, the <<handler_selection_procedure,handler selection procedure>> first checks if an error object `e` of type `E` is available. If it is not available, the handler is dropped. Otherwise, the handler is dropped if the following condition is not met:
[.text-center]
`p~1~ || p~2~ || ... p~n~`.
Where `p~i~` is equivalent to `e == V~i~`, except if `V~i~` is pointer to a function
[.text-center]
`bool (*V~i~)(T x)`.
In this case it is required that `V~i~ != 0` and that `x` can be initialized with `E const &`, and then `p~i~` is equivalent to:
[.text-center]
`V~i~(e)`.
[[category]]
In particular, it is valid to pass pointer to the function `leaf::category<Enum>` for any `V~i~`, where:
[.text-center]
`std::is_error_code_enum<Enum>::value || std::is_error_condition_enum<Enum>::value`.
In this case, `p~i~` is equivalent to:
[.text-center]
`&e.category() == &std::error_code(Enum{}).category()`.
If the error handler is invoked, `matched` can be used to access `e`.
NOTE: See also <<tutorial-predicates>>.
.Example 1: Handling of a subset of enum values.
[source,c++]
----
enum class my_enum { e1, e2, e3 };
leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[]( leaf::match<my_enum, my_enum::e1, my_enum::e2> m )
{ <1>
static_assert(std::is_same<my_enum, decltype(m.matched)>::value);
assert(m.matched == my_enum::e1 || m.matched == my_enum::e2);
....
} );
----
<1> The handler is selected if an object of type `my_enum`, which compares equal to `e1` or to `e2`, is associated with the detected error.
.Example 2: Handling of a subset of std::error_code enum values (requires at least {CPP}17, see Example 4 for a {CPP}11-compatible workaround).
[source,c++]
----
enum class my_enum { e1=1, e2, e3 };
namespace std
{
template <> struct is_error_code_enum<my_enum>: std::true_type { };
}
leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[]( leaf::match<std::error_code, my_enum::e1, my_enum::e2> m )
{ <1>
static_assert(std::is_same<std::error_code const &, decltype(m.matched)>::value);
assert(m.matched == my_enum::e1 || m.matched == my_enum::e2);
....
} );
----
<1> The handler is selected if an object of type `std::error_code`, which compares equal to `e1` or to `e2`, is associated with the detected error.
.Example 3: Handling of a specific std::error_code::category (requires at least {CPP}17).
[source,c++]
----
enum class enum_a { a1=1, a2, a3 };
enum class enum_b { b1=1, b2, b3 };
namespace std
{
template <> struct is_error_code_enum<enum_a>: std::true_type { };
template <> struct is_error_code_enum<enum_b>: std::true_type { };
}
leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[]( leaf::match<std::error_code, leaf::category<enum_a>, enum_b::b2> m )
{ <1>
static_assert(std::is_same<std::error_code const &, decltype(m.matched)>::value);
assert(&m.matched.category() == &std::error_code(enum_{}).category() || m.matched == enum_b::b2);
....
} );
----
<1> The handler is selected if an object of type `std::error_code`, which either has the same `std::error_category` as that of `enum_a` or compares equal to `enum_b::b2`, is associated with the detected error.
[[condition]]
The use of the `leaf::category` template requires automatic deduction of the type of each `V~i~`, which in turn requires {CPP}17 or newer. The same applies to the use of `std::error_code` as `E`, but LEAF provides a compatible {CPP}11 workaround for this case, using the template `condition`. The following is equivalent to Example 2:
.Example 4: Handling of a subset of std::error_code enum values using the {CPP}11-compatible API.
[source,c++]
----
enum class my_enum { e1=1, e2, e3 };
namespace std
{
template <> struct is_error_code_enum<my_enum>: std::true_type { };
}
leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[]( leaf::match<leaf::condition<my_enum>, my_enum::e1, my_enum::e2> m )
{
static_assert(std::is_same<std::error_code const &, decltype(m.matched)>::value);
assert(m.matched == my_enum::e1 || m.matched == my_enum::e2);
....
} );
----
Instead of a set of values, the `match` template can be given pointers to functions that implement a custom comparison. In the following example, we define a handler which will be selected to handle any error that communicates an object of the user-defined type `severity` with value greater than 4:
.Example 5: Handling of failures with severity::value greater than a specified threshold (requires at least {CPP}17).
[source,c++]
----
struct severity { int value; }
template <int S>
constexpr bool severity_greater_than( severity const & e ) noexcept
{
return e.value > S;
}
leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[]( leaf::match<severity, severity_greater_than<4>> m )
{
static_assert(std::is_same<severity const &, decltype(m.matched)>::value);
assert(m.matched.value > 4);
....
} );
----
'''
[[match_member]]
=== `match_member`
.#include <boost/leaf/pred.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <auto, auto... V>
struct match_member;
template <class E, class T, T E::* P, auto... V>
struct match_member<P, V...>
{
E const & matched;
// Other members not specified
};
template <auto P, auto... V>
struct is_predicate<match_member<P, V...>>: std::true_type
{
};
} }
----
[.text-right]
<<is_predicate>>
This predicate is similar to <<match_value>>, but able to bind any accessible data member of `E`; e.g. `match_member<&E::value, V...>` is equivalent to `match_value<E, V...>`.
NOTE: See also <<tutorial-predicates>>.
WARNING: `match_member` requires at least {CPP}17, whereas `match_value` does not.
'''
[[match_value]]
=== `match_value`
.#include <boost/leaf/pred.hpp>
[source,c++]
----
namespace boost { namespace leaf {
template <class E, auto... V>
struct match_value
{
E const & matched;
// Other members not specified
};
template <class E, auto... V>
struct is_predicate<match_value<E, V...>>: std::true_type
{
};
} }
----
[.text-right]
<<is_predicate>>
This predicate is similar to <<match>>, but where `match` compares the available error object `e` of type `E` to the specified values `V...`, `match_value` works with `e.value`.
NOTE: See also <<tutorial-predicates>>.
.Example:
[source,c++]
----
struct e_errno { int value; }
leaf::try_handle_some(
[]
{
return f(); // returns leaf::result<T>
},
[]( leaf::match_value<e_errno, ENOENT> m )
{ <1>
static_assert(std::is_same<e_errno const &, decltype(m.matched)>::value);
assert(m.matched.value == ENOENT);
....
} );
----
<1> The handler is selected if an object of type <<e_errno>>, with `.value` equal to `ENOENT`, is associated with the detected error.
[[traits]]
== Reference: Traits
TIP: The contents of each Reference section are organized alphabetically.
[[is_predicate]]
=== `is_predicate`
[source,c++]
.#include <boost/leaf/pred.hpp>>
----
namespace boost { namespace leaf {
template <class T>
struct is_predicate: std::false_type
{
};
} }
----
The `is_predicate` template is used by the <<handler_selection_procedure,handler selection procedure>> to detect predicate types. See <<tutorial-predicates>>.
'''
[[is_result_type]]
=== `is_result_type`
[source,c++]
.#include <boost/leaf/error.hpp>>
----
namespace boost { namespace leaf {
template <class R>
struct is_result_type: std::false_type
{
};
} }
----
The error handling functionality provided by <<try_handle_some>> and <<try_handle_all>> -- including the ability to <<tutorial-loading,load>> error objects of arbitrary types -- is compatible with any external `result<T>` type R, as long as for a given object `r` of type `R`:
* If `bool(r)` is `true`, `r` indicates success, in which case it is valid to call `r.value()` to recover the `T` value.
* Otherwise `r` indicates a failure, in which case it is valid to call `r.error()`. The returned value is used to initialize an `error_id` (note: `error_id` can be initialized by `std::error_code`).
To use an external `result<T>` type R, you must specialize the `is_result_type` template so that `is_result_type<R>::value` evaluates to `true`.
Naturally, the provided `leaf::<<result,result>><T>` class template satisfies these requirements. In addition, it allows error objects to be transported across thread boundaries, using a `std::shared_ptr<<<polymorphic_context,polymorphic_context>>>`.
[[macros]]
== Reference: Macros
TIP: The contents of each Reference section are organized alphabetically.
'''
[[BOOST_LEAF_ASSIGN]]
=== `BOOST_LEAF_ASSIGN`
.#include <boost/leaf/error.hpp>
[source,c++]
----
#define BOOST_LEAF_ASSIGN(v, r)\
auto && <<temp>> = r;\
if( !<<temp>> )\
return <<temp>>.error();\
v = std::forward<decltype(<<temp>>)>(<<temp>>).value()
----
`BOOST_LEAF_ASSIGN` is useful when calling a function that returns `result<T>` (other than `result<void>`), if the desired behavior is to forward any errors to the caller verbatim.
In case of success, the result `value()` of type `T` is assigned to the specified variable `v`, which must have been declared prior to invoking `BOOST_LEAF_ASSIGN`. However, it is possible to use `BOOST_LEAF_ASSIGN` to declare a new variable, by passing in `v` its type together with its name, e.g. `BOOST_LEAF_ASSIGN(auto && x, f())` calls `f`, forwards errors to the caller, while capturing successful values in `x`.
NOTE: See also <<BOOST_LEAF_AUTO>>.
'''
[[BOOST_LEAF_AUTO]]
=== `BOOST_LEAF_AUTO`
.#include <boost/leaf/error.hpp>
[source,c++]
----
#define BOOST_LEAF_AUTO(v, r)\
BOOST_LEAF_ASSIGN(auto v, r)
----
[.text-right]
<<BOOST_LEAF_ASSIGN>>
`BOOST_LEAF_AUTO` is useful when calling a function that returns `result<T>` (other than `result<void>`), if the desired behavior is to forward any errors to the caller verbatim.
.Example:
[source,c++]
----
leaf::result<int> compute_value();
leaf::result<float> add_values()
{
BOOST_LEAF_AUTO(v1, compute_value()); <1>
BOOST_LEAF_AUTO(v2, compute_value()); <2>
return v1 + v2;
}
----
<1> Call `compute_value`, bail out on failure, define a local variable `v1` on success.
<2> Call `compute_value` again, bail out on failure, define a local variable `v2` on success.
Of course, we could write `add_value` without using `BOOST_LEAF_AUTO`. This is equivalent:
----
leaf::result<float> add_values()
{
auto v1 = compute_value();
if( !v1 )
return v1.error();
auto v2 = compute_value();
if( !v2 )
return v2.error();
return v1.value() + v2.value();
}
----
NOTE: See also <<BOOST_LEAF_ASSIGN>>.
'''
[[BOOST_LEAF_CHECK]]
=== `BOOST_LEAF_CHECK`
.#include <boost/leaf/error.hpp>
[source,c++]
----
#if BOOST_LEAF_CFG_GNUC_STMTEXPR
#define BOOST_LEAF_CHECK(r)\
({\
auto && <<temp>> = (r);\
if( !<<temp>> )\
return <<temp>>.error();\
std::move(<<temp>>);\
}).value()
#else
#define BOOST_LEAF_CHECK(r)\
{\
auto && <<temp>> = (r);\
if( !<<temp>> )\
return <<temp>>.error();\
}
#endif
----
`BOOST_LEAF_CHECK` is useful when calling a function that returns `result<void>`, if the desired behavior is to forward any errors to the caller verbatim.
.Example:
[source,c++]
----
leaf::result<void> send_message( char const * msg );
leaf::result<int> compute_value();
leaf::result<int> say_hello_and_compute_value()
{
BOOST_LEAF_CHECK(send_message("Hello!")); <1>
return compute_value();
}
----
<1> Try to send a message, then compute a value, report errors using BOOST_LEAF_CHECK.
Equivalent implementation without `BOOST_LEAF_CHECK`:
----
leaf::result<float> add_values()
{
auto r = send_message("Hello!");
if( !r )
return r.error();
return compute_value();
}
----
If `BOOST_LEAF_CFG_GNUC_STMTEXPR` is `1` (which is the default under `pass:[__GNUC__]`), `BOOST_LEAF_CHECK` expands to a https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html[GNU C statement expression], which allows its use with non-`void` result types in any expression; see <<checking_for_errors>>.
'''
[[BOOST_LEAF_THROW_EXCEPTION]]
=== `BOOST_LEAF_THROW_EXCEPTION`
[source,c++]
.#include <boost/leaf/exception.hpp>
----
#define BOOST_LEAF_THROW_EXCEPTION <<exact-dedfinition-unspecified>>
----
Effects: :: `BOOST_LEAF_THROW_EXCEPTION(e...)` is equivalent to `leaf::<<throw_exception,throw_exception>>(e...)`, except the current source location is automatically communicated with the thrown exception, in a `<<e_source_location>>` object (in addition to all `e...` objects).
'''
[[BOOST_LEAF_NEW_ERROR]]
=== `BOOST_LEAF_NEW_ERROR`
.#include <boost/leaf/error.hpp>
[source,c++]
----
#define BOOST_LEAF_NEW_ERROR <<exact-definition-unspecified>>
----
Effects: :: `BOOST_LEAF_NEW_ERROR(e...)` is equivalent to `leaf::<<new_error,new_error>>(e...)`, except the current source location is automatically passed, in a `<<e_source_location>>` object (in addition to all `e...` objects).
[[rationale]]
== Design
=== Rationale
Definition: :: Objects that carry information about error conditions are called error objects. For example, objects of type `std::error_code` are error objects.
NOTE: The following reasoning is independent of the mechanism used to transport error objects, whether it is exception handling or anything else.
Definition: :: Depending on their interaction with error objects, functions can be classified as follows:
* *Error initiating*: functions that initiate error conditions by creating new error objects.
* *Error neutral*: functions that forward to the caller error objects communicated by lower-level functions they call.
* *Error handling*: functions that dispose of error objects they have received, recovering normal program operation.
A crucial observation is that _error initiating_ functions are typically low-level functions that lack any context and can not determine, much less dictate, the correct program behavior in response to the errors they may initiate. Error conditions which (correctly) lead to termination in some programs may (correctly) be ignored in others; yet other programs may recover from them and resume normal operation.
The same reasoning applies to _error neutral_ functions, but in this case there is the additional issue that the errors they need to communicate, in general, are initiated by functions multiple levels removed from them in the call chain, functions which usually are -- and should be treated as -- implementation details. An _error neutral_ function should not be coupled with error object types communicated by _error initiating_ functions, for the same reason it should not be coupled with any other aspect of their interface.
Finally, _error handling_ functions, by definition, have the full context they need to deal with at least some, if not all, failures. In their scope it is an absolute necessity that the author knows exactly what information must be communicated by lower level functions in order to recover from each error condition. Specifically, none of this necessary information can be treated as implementation details; in this case, the coupling which is to be avoided in _error neutral_ functions is in fact desirable.
We're now ready to define our
Design goals: ::
* *Error initiating* functions should be able to communicate [underline]#all# information available to them that is relevant to the failure being reported.
* *Error neutral* functions should not be coupled with error types communicated by lower-level _error initiating_ functions. They should be able to augment any failure with additional relevant information available to them.
* *Error handling* functions should be able to access all the information communicated by _error initiating_ or _error neutral_ functions that is needed in order to deal with failures.
The design goal that _error neutral_ functions are not coupled with the static type of error objects that pass through them seems to require dynamic polymorphism and therefore dynamic memory allocations (the Boost Exception library meets this design goal at the cost of dynamic memory allocation).
As it turns out, dynamic memory allocation is not necessary due to the following
Fact: ::
* *Error handling* functions "know" which of the information _error initiating_ and _error neutral_ functions are [.underline]#able# to communicate is [.underline]#actually needed# in order to deal with failures in a particular program. Ideally, no resources should be [.line-through]#used# wasted storing or communicating information which is not currently needed to handle errors, [.underline]#even if it is relevant to the failure#.
For example, if a library function is able to communicate an error code but the program does not need to know the exact error code, then that information may be ignored at the time the library function attempts to communicate it. On the other hand, if an _error handling_ function needs that information, the memory needed to store it can be reserved statically in its scope.
The LEAF functions <<try_handle_some>>, <<try_handle_all>> and <<try_catch>> implement this idea. Users provide error handling lambda functions, each taking arguments of the types it needs in order to recover from a particular error condition. LEAF simply provides the space needed to store these types (in the form of a `std::tuple`, using automatic storage duration) until they are passed to a suitable handler.
At the time this space is reserved in the scope of an error handling function, `thread_local` pointers of the required error types are set to point to the corresponding objects within it. Later on, _error initiating_ or _error neutral_ functions wanting to communicate an error object of a given type `E` use the corresponding `thread_local` pointer to detect if there is currently storage available for this type:
* If the pointer is not null, storage is available and the object is moved into the pointed storage, exactly once -- regardless of how many levels of function calls must unwind before an _error handling_ function is reached.
* If the pointer is null, storage is not available and the error object is discarded, since no error handling function makes any use of it in this program -- saving resources.
This almost works, except we need to make sure that _error handling_ functions are protected from accessing stale error objects stored in response to previous failures, which would be a serious logic error. To this end, each occurrence of an error is assigned a unique <<error_id>>. Each of the `E...` objects stored in error handling scopes is assigned an `error_id` as well, permanently associating it with a particular failure.
Thus, to handle a failure we simply match the available error objects (associated with its unique `error_id`) with the argument types required by each user-provided error handling function. In terms of {CPP} exception handling, it is as if we could write something like:
[source,c++]
----
try
{
auto r = process_file();
//Success, use r:
....
}
catch(file_read_error &, e_file_name const & fn, e_errno const & err)
{
std::cerr <<
"Could not read " << fn << ", errno=" << err << std::endl;
}
catch(file_read_error &, e_errno const & err)
{
std::cerr <<
"File read error, errno=" << err << std::endl;
}
catch(file_read_error &)
{
std::cerr << "File read error!" << std::endl;
}
----
Of course this syntax is not valid, so LEAF uses lambda functions to express the same idea:
[source,c++]
----
leaf::try_catch(
[]
{
auto r = process_file(); //Throws in case of failure, error objects stored inside the try_catch scope
//Success, use r:
....
}
[](file_read_error &, e_file_name const & fn, e_errno const & err)
{
std::cerr <<
"Could not read " << fn << ", errno=" << err << std::endl;
},
[](file_read_error &, e_errno const & err)
{
std::cerr <<
"File read error, errno=" << err << std::endl;
},
[](file_read_error &)
{
std::cerr << "File read error!" << std::endl;
} );
----
[.text-right]
<<try_catch>> | <<e_file_name>> | <<e_errno>>
Similar syntax works without exception handling as well. Below is the same snippet, written using `<<result,result>><T>`:
[source,c++]
----
return leaf::try_handle_some(
[]() -> leaf::result<void>
{
BOOST_LEAF_AUTO(r, process_file()); //In case of errors, error objects are stored inside the try_handle_some scope
//Success, use r:
....
return { };
}
[](leaf::match<error_enum, file_read_error>, e_file_name const & fn, e_errno const & err)
{
std::cerr <<
"Could not read " << fn << ", errno=" << err << std::endl;
},
[](leaf::match<error_enum, file_read_error>, e_errno const & err)
{
std::cerr <<
"File read error, errno=" << err << std::endl;
},
[](leaf::match<error_enum, file_read_error>)
{
std::cerr << "File read error!" << std::endl;
} );
----
[.text-right]
<<result>> | <<try_handle_some>> | <<match>> | <<e_file_name>> | <<e_errno>>
NOTE: Please post questions and feedback on the Boost Developers Mailing List.
'''
[[exception_specifications]]
=== Critique 1: Error Types Do Not Participate in Function Signatures
A knee-jerk critique of the LEAF design is that it does not statically enforce that each possible error condition is recognized and handled by the program. One idea I've heard from multiple sources is to add `E...` parameter pack to `result<T>`, essentially turning it into `expected<T,E...>`, so we could write something along these lines:
[source,c++]
----
expected<T, E1, E2, E3> f() noexcept; <1>
expected<T, E1, E3> g() noexcept <2>
{
if( expected<T, E1, E2, E3> r = f() )
{
return r; //Success, return the T
}
else
{
return r.handle_error<E2>( [] ( .... ) <3>
{
....
} );
}
}
----
<1> `f` may only return error objects of type `E1`, `E2`, `E3`.
<2> `g` narrows that to only `E1` and `E3`.
<3> Because `g` may only return error objects of type `E1` and `E3`, it uses `handle_error` to deal with `E2`. In case `r` contains `E1` or `E3`, `handle_error` simply returns `r`, narrowing the error type parameter pack from `E1, E2, E3` down to `E1, E3`. If `r` contains an `E2`, `handle_error` calls the supplied lambda, which is required to return one of `E1`, `E3` (or a valid `T`).
The motivation here is to help avoid bugs in functions that handle errors that pop out of `g`: as long as the programmer deals with `E1` and `E3`, he can rest assured that no error is left unhandled.
Congratulations, we've just discovered exception specifications. The difference is that exception specifications, before being removed from {CPP}, were enforced dynamically, while this idea is equivalent to statically-enforced exception specifications, like they are in Java.
Why not use the equivalent of exception specifications, even if they are enforced statically?
"The short answer is that nobody knows how to fix exception specifications in any language, because the dynamic enforcement {CPP} chose has only different (not greater or fewer) problems than the static enforcement Java chose. ... When you go down the Java path, people love exception specifications until they find themselves all too often encouraged, or even forced, to add `throws Exception`, which immediately renders the exception specification entirely meaningless. (Example: Imagine writing a Java generic that manipulates an arbitrary type `T`).footnote:[https://herbsutter.com/2007/01/24/questions-about-exception-specifications/]"
-- Herb Sutter
Consider again the example above: assuming we don't want important error-related information to be lost, values of type `E1` and/or `E3` must be able to encode any `E2` value dynamically. But like Sutter points out, in generic contexts we don't know what errors may result in calling a user-supplied function. The only way around that is to specify a single type (e.g. `std::error_code`) that can communicate any and all errors, which ultimately defeats the idea of using static type checking to enforce correct error handling.
That said, in every program there are certain _error handling_ functions (e.g. `main`) which are required to handle any error, and it is highly desirable to be able to enforce this requirement at compile-time. In LEAF, the `try_handle_all` function implements this idea: if the user fails to supply at least one handler that will match any error, the result is a compile error. This guarantees that the scope invoking `try_handle_all` is prepared to recover from any failure.
'''
[[translation]]
=== Critique 2: LEAF Does Not Facilitate Mapping Between Different Error Types
Most {CPP} programs use multiple C and {CPP} libraries, and each library may provide its own system of error codes. But because it is difficult to define static interfaces that can communicate arbitrary error code types, a popular idea is to map each library-specific error code to a common program-wide enum.
For example, if we have --
[source,c++,options="nowrap"]
----
namespace lib_a
{
enum error
{
ok,
ec1,
ec2,
....
};
}
----
[source,c++,options="nowrap"]
----
namespace lib_b
{
enum error
{
ok,
ec1,
ec2,
....
};
}
----
-- we could define:
[source,c++]
----
namespace program
{
enum error
{
ok,
lib_a_ec1,
lib_a_ec2,
....
lib_b_ec1,
lib_b_ec2,
....
};
}
----
An error handling library could provide conversion API that uses the {CPP} static type system to automate the mapping between the different error enums. For example, it may define a class template `result<T,E>` with value-or-error variant semantics, so that:
* `lib_a` errors are transported in `result<T,lib_a::error>`,
* `lib_b` errors are transported in `result<T,lib_b::error>`,
* then both are automatically mapped to `result<T,program::error>` once control reaches the appropriate scope.
There are several problems with this idea:
* It is prone to errors, both during the initial implementation as well as under maintenance.
* It does not compose well. For example, if both of `lib_a` and `lib_b` use `lib_c`, errors that originate in `lib_c` would be obfuscated by the different APIs exposed by each of `lib_a` and `lib_b`.
* It presumes that all errors in the program can be specified by exactly one error code, which is false.
To elaborate on the last point, consider a program that attempts to read a configuration file from three different locations: in case all of the attempts fail, it should communicate each of the failures. In theory `result<T,E>` handles this case well:
[source,c++]
----
struct attempted_location
{
std::string path;
error ec;
};
struct config_error
{
attempted_location current_dir, user_dir, app_dir;
};
result<config,config_error> read_config();
----
This looks nice, until we realize what the `config_error` type means for the automatic mapping API we wanted to define: an `enum` can not represent a `struct`. It is a fact that we can not assume that all error conditions can be fully specified by an `enum`; an error handling library must be able to transport arbitrary static types efficiently.
[[errors_are_not_implementation_details]]
=== Critique 3: LEAF Does Not Treat Low Level Error Types as Implementation Details
This critique is a combination of <<exception_specifications,Critique 1>> and <<translation,Critique 2>>, but it deserves special attention. Let's consider this example using LEAF:
[source,c++]
----
leaf::result<std::string> read_line( reader & r );
leaf::result<parsed_line> parse_line( std::string const & line );
leaf::result<parsed_line> read_and_parse_line( reader & r )
{
BOOST_LEAF_AUTO(line, read_line(r)); <1>
BOOST_LEAF_AUTO(parsed, parse_line(line)); <2>
return parsed;
}
----
[.text-right]
<<result>> | <<BOOST_LEAF_AUTO>>
<1> Read a line, forward errors to the caller.
<2> Parse the line, forward errors to the caller.
The objection is that LEAF will forward verbatim the errors that are detected in `read_line` or `parse_line` to the caller of `read_and_parse_line`. The premise of this objection is that such low-level errors are implementation details and should be treated as such. Under this premise, `read_and_parse_line` should act as a translator of sorts, in both directions:
* When called, it should translate its own arguments to call `read_line` and `parse_line`;
* If an error is detected, it should translate the errors from the error types returned by `read_line` and `parse_line` to a higher-level type.
The motivation is to isolate the caller of `read_and_parse_line` from its implementation details `read_line` and `parse_line`.
There are two possible ways to implement this translation:
*1)* `read_and_parse_line` understands the semantics of *all possible failures* that may be reported by both `read_line` and `parse_line`, implementing a non-trivial mapping which both _erases_ information that is considered not relevant to its caller, as well as encodes _different_ semantics in the error it reports. In this case `read_and_parse_line` assumes full responsibility for describing precisely what went wrong, using its own type specifically designed for the job.
*2)* `read_and_parse_line` returns an error object that essentially indicates which of the two inner functions failed, and also transports the original error object without understanding its semantics and without any loss of information, wrapping it in a new error type.
The problem with *1)* is that typically the caller of `read_and_parse_line` is not going to handle the error, but it does need to forward it to its caller. In our attempt to protect the *one* error handling function from "implementation details", we've coupled the interface of *all* intermediate error neutral functions with the static types of errors they do not understand and do not handle.
Consider the case where `read_line` communicates `errno` in its errors. What is `read_and_parse_line` supposed to do with e.g. `EACCESS`? Turn it into `READ_AND_PARSE_LINE_EACCESS`? To what end, other than to obfuscate the original (already complex and platform-specific) semantics of `errno`?
And what if the call to `read` is polymorphic, which is also typical? What if it involves a user-supplied function object? What kinds of errors does it return and why should `read_and_parse_line` care?
Therefore, we're left with *2)*. There's almost nothing wrong with this option, since it passes any and all error-related information from lower level functions without any loss. However, using a wrapper type to grant (presumably dynamic) access to any lower-level error type it may be transporting is cumbersome and (like Niall Douglas <<interoperability,explains>>) in general probably requires dynamic allocations. It is better to use independent error types that communicate the additional information not available in the original error object, while error handlers rely on LEAF to provide efficient access to any and all low-level error types, as needed.
== Alternatives to LEAF
* https://www.boost.org/doc/libs/release/libs/exception/doc/boost-exception.html[Boost Exception]
* https://ned14.github.io/outcome[Boost Outcome]
* https://github.com/TartanLlama/expected[`tl::expected`]
Below we offer a comparison of Boost LEAF to Boost Exception and to Boost Outcome.
[[boost_exception]]
=== Comparison to Boost Exception
While LEAF can be used without exception handling, in the use case when errors are communicated by throwing exceptions, it can be viewed as a better, more efficient alternative to Boost Exception. LEAF has the following advantages over Boost Exception:
* LEAF does not allocate memory dynamically;
* LEAF does not waste system resources communicating error objects not used by specific error handling functions;
* LEAF does not store the error objects in the exception object, and therefore it is able to augment exceptions thrown by external libraries (Boost Exception can only augment exceptions of types that derive from `boost::exception`).
The following tables outline the differences between the two libraries which should be considered when code that uses Boost Exception is refactored to use LEAF instead.
NOTE: It is possible to access Boost Exception error information using the LEAF error handling interface. See <<tutorial-boost_exception_integration>>.
.Defining a custom type for transporting values of type T
[cols="1a,1a",options="header",stripes=none]
|====
| Boost Exception | LEAF
|
[source,c++,options="nowrap"]
----
typedef error_info<struct my_info_,T> my_info;
----
[.text-right]
https://www.boost.org/doc/libs/release/libs/exception/doc/error_info.html[`boost::error_info`]
|
[source,c++,options="nowrap"]
----
struct my_info { T value; };
----
|====
.Passing arbitrary info at the point of the throw
[cols="1a,1a",options="header",stripes=none]
|====
| Boost Exception | LEAF
|
[source,c++,options="nowrap"]
----
throw my_exception() <<
my_info(x) <<
my_info(y);
----
[.text-right]
https://www.boost.org/doc/libs/release/libs/exception/doc/exception_operator_shl.html[`operator<<`]
|
[source,c++,options="nowrap"]
----
leaf::throw_exception( my_exception(),
my_info{x},
my_info{y} );
----
[.text-right]
<<throw_exception>>
|====
.Augmenting exceptions in error neutral contexts
[cols="1a,1a",options="header",stripes=none]
|====
| Boost Exception | LEAF
|
[source,c++,options="nowrap"]
----
try
{
f();
}
catch( boost::exception & e )
{
e << my_info(x);
throw;
}
----
[.text-right]
https://www.boost.org/doc/libs/release/libs/exception/doc/exception.html[`boost::exception`] \| https://www.boost.org/doc/libs/release/libs/exception/doc/exception_operator_shl.html[`operator<<`]
|
[source,c++,options="nowrap"]
----
auto load = leaf::on_error( my_info{x} );
f();
----
[.text-right]
<<on_error>>
|====
.Obtaining arbitrary info at the point of the catch
[cols="1a,1a",options="header",stripes=none]
|====
| Boost Exception | LEAF
|
[source,c++,options="nowrap"]
----
try
{
f();
}
catch( my_exception & e )
{
if( T * v = get_error_info<my_info>(e) )
{
//my_info is available in e.
}
}
----
[.text-right]
https://www.boost.org/doc/libs/release/libs/exception/doc/get_error_info.html[`boost::get_error_info`]
|
[source,c++,options="nowrap"]
----
leaf::try_catch(
[]
{
f(); // throws
}
[](my_exception &, my_info const & x)
{
//my_info is available with
//the caught exception.
} );
----
[.text-right]
<<try_catch>>
|====
.Transporting of error objects
[cols="1a,1a",options="header",stripes=none]
|====
| Boost Exception | LEAF
| All supplied https://www.boost.org/doc/libs/release/libs/exception/doc/error_info.html[`boost::error_info`] objects are allocated dynamically and stored in the https://www.boost.org/doc/libs/release/libs/exception/doc/exception.html[`boost::exception`] subobject of exception objects.
| User-defined error objects are stored statically in the scope of <<try_catch>>, but only if their types are needed to handle errors; otherwise they are discarded.
|====
.Transporting of error objects across thread boundaries
[cols="1a,1a",options="header",stripes=none]
|====
| Boost Exception | LEAF
| https://www.boost.org/doc/libs/release/libs/exception/doc/exception_ptr.html[`boost::exception_ptr`] automatically captures https://www.boost.org/doc/libs/release/libs/exception/doc/error_info.html[`boost::error_info`] objects stored in a `boost::exception` and can transport them across thread boundaries.
| Transporting error objects across thread boundaries requires the use of <<capture>>.
|====
.Printing of error objects in automatically-generated diagnostic information messages
[cols="1a,1a",options="header",stripes=none]
|====
| Boost Exception | LEAF
| `boost::error_info` types may define conversion to `std::string` by providing `to_string` overloads *or* by overloading `operator<<` for `std::ostream`.
| LEAF does not use `to_string`. Error types may define `operator<<` overloads for `std::ostream`.
|====
[WARNING]
====
The fact that Boost Exception stores all supplied `boost::error_info` objects -- while LEAF discards them if they aren't needed -- affects the completeness of the message we get when we print `leaf::<<diagnostic_info,diagnostic_info>>` objects, compared to the string returned by https://www.boost.org/doc/libs/release/libs/exception/doc/diagnostic_information.html[`boost::diagnostic_information`].
If the user requires a complete diagnostic message, the solution is to use `leaf::<<verbose_diagnostic_info,verbose_diagnostic_info>>`. In this case, before unused error objects are discarded by LEAF, they are converted to string and printed. Note that this allocates memory dynamically.
====
'''
[[boost_outcome]]
=== Comparison to Boost Outcome
==== Design Differences
Like LEAF, the https://ned14.github.io/outcome[Boost Outcome] library is designed to work in low latency environments. It provides two class templates, `result<>` and `outcome<>`:
* `result<T,EC,NVP>` can be used as the return type in `noexcept` functions which may fail, where `T` specifies the type of the return value in case of success, while `EC` is an "error code" type. Semantically, `result<T,EC>` is similar to `std::variant<T,EC>`. Naturally, `EC` defaults to `std::error_code`.
* `outcome<T,EC,EP,NVP>` is similar to `result<>`, but in case of failure, in addition to the "error code" type `EC` it can hold a "pointer" object of type `EP`, which defaults to `std::exception_ptr`.
NOTE: `NVP` is a policy type used to customize the behavior of `.value()` when the `result<>` or the `outcome<>` object contains an error.
The idea is to use `result<>` to communicate failures which can be fully specified by an "error code", and `outcome<>` to communicate failures that require additional information.
Another way to describe this design is that `result<>` is used when it suffices to return an error object of some static type `EC`, while `outcome<>` can also transport a polymorphic error object, using the pointer type `EP`.
NOTE: In the default configuration of `outcome<T>` the additional information -- or the additional polymorphic object -- is an exception object held by `std::exception_ptr`. This targets the use case when an exception thrown by a lower-level library function needs to be transported through some intermediate contexts that are not exception-safe, to a higher-level context able to handle it. LEAF directly supports this use as well, see <<exception_to_result>>.
Similar reasoning drives the design of LEAF as well. The difference is that while both libraries recognize the need to transport "something else" in addition to an "error code", LEAF provides an efficient solution to this problem, while Outcome shifts this burden to the user.
The `leaf::result<>` template deletes both `EC` and `EP`, which decouples it from the type of the error objects that are transported in case of a failure. This enables lower-level functions to freely communicate anything and everything they "know" about the failure: error code, even multiple error codes, file names, URLs, port numbers, etc. At the same time, the higher-level error handling functions control which of this information is needed in a specific client program and which is not. This is ideal, because:
* Authors of lower-level library functions lack context to determine which of the information that is both relevant to the error _and_ naturally available to them needs to be communicated in order for a particular client program to recover from that error;
* Authors of higher-level error handling functions can easily and confidently make this determination, which they communicate naturally to LEAF, by simply writing the different error handlers. LEAF will transport the needed error objects while discarding the ones handlers don't care to use, saving resources.
TIP: The LEAF examples include an adaptation of the program from the https://ned14.github.io/outcome/tutorial/essential/result/[Boost Outcome `result<>` tutorial]. You can https://github.com/boostorg/leaf/blob/master/example/print_half.cpp?ts=4[view it on GitHub].
NOTE: Programs using LEAF for error handling are not required to use `leaf::result<T>`; for example, it is possible to use `outcome::result<T>` with LEAF.
[[interoperability]]
==== The Interoperability Problem
The Boost Outcome documentation discusses the important problem of bringing together multiple libraries -- each using its own error reporting mechanism -- and incorporating them in a robust error handling infrastructure in a client program.
Users are advised that whenever possible they should use a common error handling system throughout their entire codebase, but because this is not practical, both the `result<>` and the `outcome<>` templates can carry user-defined "payloads".
The following analysis is from the Boost Outcome documentation:
====
If library A uses `result<T, libraryA::failure_info>`, and library B uses `result<T, libraryB::error_info>` and so on, there becomes a problem for the application writer who is bringing in these third party dependencies and tying them together into an application. As a general rule, each third party library author will not have built in explicit interoperation support for unknown other third party libraries. The problem therefore lands with the application writer.
The application writer has one of three choices:
. In the application, the form of result used is `result<T, std::variant<E1, E2, ...>>` where `E1, E2 …` are the failure types for every third party library in use in the application. This has the advantage of preserving the original information exactly, but comes with a certain amount of use inconvenience and maybe excessive coupling between high level layers and implementation detail.
. One can translate/map the third party’s failure type into the application’s failure type at the point of the failure exiting the third party library and entering the application. One might do this, say, with a C preprocessor macro wrapping every invocation of the third party API from the application. This approach may lose the original failure detail, or mis-map under certain circumstances if the mapping between the two systems is not one-one.
. One can type erase the third party’s failure type into some application failure type, which can later be reconstituted if necessary. *This is the cleanest solution with the least coupling issues and no problems with mis-mapping*, but it almost certainly requires the use of `malloc` which the previous two did not.
====
The analysis above (emphasis added) is clear and precise, but LEAF and Boost Outcome tackle the interoperability problem differently:
* The Boost Outcome design asserts that the "cleanest" solution based on type-erasure is suboptimal ("almost certainly requires the use of `malloc`pass:[]"), and instead provides a system for injecting custom converters into the `outcome::convert` namespace, used to translate between library-specific and program-wide error types, even though this approach "may lose the original failure detail".
* The LEAF design asserts that coupling the signatures of <<rationale,error neutral>> functions with the static types of the error objects they need to forward to the caller <<translation,does not scale>>, and instead transports error objects directly to error handling scopes where they are stored statically, effectively implementing the third choice outlined above (without the use of `malloc`).
Further, consider that Outcome aims to hopefully become _the_ one error handling API all libraries would use, and in theory everyone would benefit from uniformity and standardization. But the reality is that this is wishful thinking. In fact, that reality is reflected in the design of `outcome::result<>`, in its lack of commitment to using `std::error_code` for its intended purpose: to be _the_ standard type for transporting error codes. The fact is that `std::error_code` became _yet another_ error code type programmers need to understand and support.
In contrast, the design of LEAF acknowledges that {CPP} programmers don't even agree on what a string is. If your project uses 10 different libraries, this probably means 15 different ways to report errors, sometimes across uncooperative interfaces (e.g. C APIs). LEAF helps you get the job done.
== Benchmark
https://github.com/boostorg/leaf/blob/master/benchmark/benchmark.md[This benchmark] compares the performance of LEAF, Boost Outcome and `tl::expected`.
== Running the Unit Tests
The unit tests can be run with https://mesonbuild.com[Meson Build] or with Boost Build. To run the unit tests:
=== Meson Build
Clone LEAF into any local directory and execute:
[source,sh]
----
cd leaf
meson bld/debug
cd bld/debug
meson test
----
See `meson_options.txt` found in the root directory for available build options.
=== Boost Build
Assuming the current working directory is `<boostroot>/libs/leaf`:
[source,sh]
----
../../b2 test
----
[[configuration]]
== Configuration
The following configuration macros are recognized:
* `BOOST_LEAF_CFG_DIAGNOSTICS`: Defining this macro as `0` stubs out both <<diagnostic_info>> and <<verbose_diagnostic_info>> (if the macro is left undefined, LEAF defines it as `1`).
* `BOOST_LEAF_CFG_STD_SYSTEM_ERROR`: Defining this macro as `0` disables the `std::error_code` / `std::error_condition` integration. In this case LEAF does not `#include <system_error>`, which may be too heavy for embedded platforms (if the macro is left undefined, LEAF defines it as `1`).
* `BOOST_LEAF_CFG_STD_STRING`: Defining this macro as `0` disables all use of `std::string` (this requires `BOOST_LEAF_CFG_DIAGNOSTICS=0` as well). In this case LEAF does not `#include <string>` which may be too heavy for embedded platforms (if the macro is left undefined, LEAF defines it as `1`).
* `BOOST_LEAF_CFG_CAPTURE`: Defining this macro as `0` disables the ability of `leaf::result` to transport errors between threads. In this case LEAF does not `#include <memory>`, which may be too heavy for embedded platforms (if the macro is left undefined, LEAF defines it as `1`).
* `BOOST_LEAF_CFG_GNUC_STMTEXPR`: This macro controls whether or not <<BOOST_LEAF_CHECK>> is defined in terms of a https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html[GNU C statement expression], which enables its use to check for errors similarly to how the questionmark operator works in some languages (see <<checking_for_errors>>). By default the macro is defined as `1` under `pass:[__GNUC__]`, otherwise as `0`.
* `BOOST_LEAF_CFG_WIN32`: Defining this macro as 1 enables the default constructor in <<e_LastError>>, and the automatic conversion to string (via `FormatMessageA`) when <<verbose_diagnostic_info>> is printed. If the macro is left undefined, LEAF defines it as `0` (even on windows, since including `windows.h` is generally not desirable). Note that the `e_LastError` type itself is available on all platforms, there is no need for conditional compilation in error handlers that use it.
* `BOOST_LEAF_NO_EXCEPTIONS`: Disables all exception handling support. If left undefined, LEAF defines it automatically based on the compiler configuration (e.g. `-fno-exceptions`).
* `BOOST_LEAF_NO_THREADS`: Disables all thread safety in LEAF.
[[configuring_tls_access]]
=== Configuring TLS Access
LEAF requires support for thread-local `void` pointers. By default, this is implemented by means of the {CPP}11 `thread_local` keyword, but in order to support <<embedded_platforms,embedded platforms>>, it is possible to configure LEAF to use an array of thread local pointers instead, by defining `BOOST_LEAF_USE_TLS_ARRAY`. In this case, the user is required to define the following two functions to implement the required TLS access:
[source,c++]
----
namespace boost { namespace leaf {
namespace tls
{
void * read_void_ptr( int tls_index ) noexcept;
void write_void_ptr( int tls_index, void * p ) noexcept;
}
} }
----
TIP: For efficiency, `read_void_ptr` and `write_void_ptr` should be defined `inline`.
Under `BOOST_LEAF_USE_TLS_ARRAY` the following additional configuration macros are recognized:
* `BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX` specifies the start TLS array index available to LEAF (if the macro is left undefined, LEAF defines it as `0`).
* `BOOST_LEAF_CFG_TLS_ARRAY_SIZE` may be defined to specify the size of the TLS array. In this case TLS indices are validated via `BOOST_LEAF_ASSERT` before being passed to `read_void_ptr` / `write_void_ptr`.
* `BOOST_LEAF_CFG_TLS_INDEX_TYPE` may be defined to specify the integral type used to store assigned TLS indices (if the macro is left undefined, LEAF defines it as `unsigned char`).
TIP: Reporting error objects of types that are not used by the program to handle failures does not consume TLS pointers. The minimum size of the TLS pointer array required by LEAF is the total number of different types used as arguments to error handlers (in the entire program), plus one.
WARNING: Beware of `read_void_ptr`/`write_void_ptr` accessing thread local pointers beyond the static boundaries of the thread local pointer array; this will likely result in undefined behavior.
[[embedded_platforms]]
=== Embedded Platforms
Defining `BOOST_LEAF_EMBEDDED` is equivalent to the following:
[source,c++]
----
#ifndef BOOST_LEAF_CFG_DIAGNOSTICS
# define BOOST_LEAF_CFG_DIAGNOSTICS 0
#endif
#ifndef BOOST_LEAF_CFG_STD_SYSTEM_ERROR
# define BOOST_LEAF_CFG_STD_SYSTEM_ERROR 0
#endif
#ifndef BOOST_LEAF_CFG_STD_STRING
# define BOOST_LEAF_CFG_STD_STRING 0
#endif
#ifndef BOOST_LEAF_CFG_CAPTURE
# define BOOST_LEAF_CFG_CAPTURE 0
#endif
----
LEAF supports FreeRTOS out of the box, please define `BOOST_LEAF_TLS_FREERTOS` (in which case LEAF automatically defines `BOOST_LEAF_EMBEDDED`, if it is not defined already).
For other embedded platforms, please define `BOOST_LEAF_USE_TLS_ARRAY`, see <<configuring_tls_access>>.
If your program does not use concurrency at all, simply define `BOOST_LEAF_NO_THREADS`, which requires no TLS support at all (but is NOT thread-safe).
[[portability]]
== Portability
The source code is compatible with {CPP}11 or newer.
LEAF uses thread-local storage (only for pointers). By default, this is implemented via the {CPP}11 `thread_local` storage class specifier, but the library is easily configurable to use any platform-specific TLS API instead (it ships with built-in support for FreeRTOS). See <<configuration>>.
== Limitations
When using dynamic linking, it is required that error types are declared with `default` visibility, e.g.:
[source,c++]
----
struct __attribute__ ((visibility ("default"))) my_error_info
{
int value;
};
----
This works as expected except on Windows, where thread-local storage is not shared between the individual binary modules. For this reason, to transport error objects across DLL boundaries, it is required that they're captured in a <<polymorphic_context>>, just like when <<tutorial-async>>.
TIP: When using dynamic linking, it is always best to define module interfaces in terms of C (and implement them in {CPP} if appropriate).
== Acknowledgements
Special thanks to Peter Dimov and Sorin Fetche.
Ivo Belchev, Sean Palmer, Jason King, Vinnie Falco, Glen Fernandes, Augustín Bergé -- thanks for the valuable feedback.
Documentation rendered by https://asciidoctor.org/[Asciidoctor] with https://github.com/zajo/asciidoctor_skin[these customizations].
|