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
|
\documentclass[11pt,a4paper]{report}
\usepackage{times}
\usepackage{pl}
\usepackage{plpage}
%\usepackage{xpce}
\usepackage{html}
\sloppy
\onefile
\htmloutput{.} % Output directory
\htmlmainfile{pl2cpp} % Main document file
\bodycolor{white} % Page colour
\renewcommand{\runningtitle}{A C++ interface to SWI-Prolog}
\makeindex
\begin{document}
\title{A C++ interface to SWI-Prolog}
\author{Jan Wielemaker \& Peter Ludemann \\
SWI-Prolog Solutions b.v. \\
E-mail: \email{jan@swi-prolog.org}}
\maketitle
\begin{abstract}
This document describes a C++ interface to SWI-Prolog. SWI-Prolog could
be used with C++ for a very long time, but only by calling the extern
"C" functions of the C-interface. The interface described here
provides a true C++ layer around the C-interface for much more concise
and natural programming from C++. The interface deals with automatic
type-conversion to and from native C data-types, transparent mapping of
exceptions, making queries to Prolog and registering foreign predicates.
This document describes version~2 of the C++ interface. Version~1 is
considered \textit{deprecated}. Version 2 is implemented by
\file{SWI-cpp2.h} and \file{SWI-cpp2.cpp}. This is a much more mature
C++ interface has been designed and implemented by Peter Ludemann.
\end{abstract}
\vfill
\vfill
\vfill
\newpage
\tableofcontents
\newpage
\chapter{A C++ interface to SWI-Prolog}
\label{sec:cpp2}
\section{Summary of changes between Versions 1 and 2}
\label{sec:summary-cpp2-changes}
Version~1 is in \file{SWI-cpp.h}; version~2 is in \file{SWI-cpp2.h},
\file{SWI-cpp2.cpp}, \file{SWI-cpp2-plx.h}, and \file{SWI-cpp2-atommap.h}.
The overall structure of the API has been retained - that is, it is a
thin layer of lightweight classes on top of the interface provided by
\file{SWI-Prolog.h}. Based on experience with the API, most of the
conversion operators and some of the comparison operators have been
removed or deprecated, and replaced by ``getter'' methods; the
overloaded constructors have been replaced by subclasses for the
various types. Some changes were also made to ensure that the
\const{[]} operator for \ctype{PlTerm} and \ctype{PlTermv} doesn't
cause unexpected implicit conversions.\footnote{If
there is an implicit conversion operator from \ctype{PlTerm} to
\ctype{term_t} and also to \ctype{char*}, then the \const{[]} operator
is ambiguous if \exam{f} is overloaded to accept a \ctype{term_t} or
\ctype{char*} in the code \exam{PlTerm t=...; f(t[0])}. }
Prolog errors are now converted to C++ exceptions (which contain
the exception term rather being a subclass of \ctype{PlTerm} as in
version 1), where they can be caught and thrown using the usual C++
mechanisms; and the subclasses that create exceptions have been
changed to functions. In addition, an exception type \ctype{PlFail}
has been added, together with \ctype{PlCheckFail}, to allow more compact
code by ``short circuit'' return to Prolog on failure.
A convenience class for creating blobs has been added, so that an
existing structure can be converted to a blob with only a few lines
of code. More specifically:
\begin{itemize}
\item \file{SWI-cpp2.cpp} has been added, containing the
implementation of some functions. This is included by default
from \file{SWI-cpp2.h} or can be compiled separately.
\item
The constructor PlTerm() is restricted to a few
unambiguous cases - instead, you should use the appropriate
subclass constructors that specify the type (PlTerm_var(),
PlTerm_atom(), etc.).
\item
Wrapper functions have been provided for almost all the PL_*()
functions in \file{SWI-Prolog.h}, and have the same names with
the ``PL'' replaced by ``Plx''.\footnote{``Pl'' is used
throughout the \file{SWI-cpp2.h} interface, and the ``x'' is
for ``eXtended with eXception handling.''}
Where appropriate, these check return codes and throw a C++
exception (created from the Prolog error).
See \secref{cpp2-wrapper-functions}.
Many of these wrapper functions are also methods in the \ctype{PlAtom}
and \ctype{PlTerm} classes, with the arguments changed from
\ctype{atom_t} and \ctype{term_t} to \ctype{PlAtom} and \ctype{PlTerm}
and in some cases \ctype{char*} and \ctype{wchar_t*}
changed to \ctype{std::string} and \ctype{std::wstring}.
These wrappers are available if you include \file{SWI-cpp2.h}
(they are in a separate \file{SWI-cpp2-plx.h} file for ease
of maintenance).
\item
Instead of returning \const{false} from a foreign predicate to
indicate failure, you can throw PlFail(). The
convenience function PlCheckFail(rc) can be used to
throw PlFail() if \const{false} is returned from a function in
\file{SWI-Prolog.h}. If the wrapper functions or class methods
are used, Prolog errors result in a C++ \ctype{PlException}
exception.\footnote{If a ``Plx_'' wrapper is used to call a
\file{SWI-Prolog.h} function, a Prolog error will have already
resulted in throwing \ctype{PlException}};
PlCheckFail(rc) is used to additionally throw
\ctype{PlFail}, similar to returning \const{false} from the
top-level of a foreign predicate - Prolog will check for an error
and call throw/1 if appropriate.
\item
The \ctype{PlException} class is now a subclass of \ctype{std::exception}
and encapsulates a Prolog error.
Prolog errors are converted into \exam{throw PlException(...)}.
If the user code does not catch the \ctype{PlException}, the PREDICATE()
macro converts the error to a Prolog error upon return to the
Prolog caller.
\item
The C++ constructors, functions, and methods use the wrapper
functions to throw a C++ exception on error (this C++ exception is
converted to a Prolog exception when control returns to
Prolog).
\item
The ``cast'' operators (e.g., \exam{(char*)t}, \exam{(int64_t)t},
\exam{static_cast<char*>(t)})
have been deprecated, replaced by ``getters'' (e.g.,
\exam{t.as_string()}, \exam{t.as_int64_t()}).
\item
The overloaded assignment operator for unification is deprecated,
replaced by \cfuncref{PlTerm::unify_term}{}, \cfuncref{PlTerm::unify_atom}{},
etc., and the helper \cfuncref{PlCheckFail}{}.
\item
Many of the equality and inequality operators are deprecated;
replaced by the PlTerm::as_string() or PlTerm::get_nchars() methods
and the associated
\ctype{std::string}, comparison operators. The \cfuncref{PlTerm::as_string}{} method
allows specifying the encoding to use whereas the \exam{==} and
similar operators do not allow for this.
\item
Methods that return \ctype{char*} have been replaced by methods
that return \ctype{std::string} to ensure that lifetime issues
don't cause subtle bugs.\footnote{If you want to
return a \ctype{char*} from a function, you should not do
\exam{return t.as_string().c_str()} because that will return
a pointer to local or stack memory. Instead, you should
change your interface to return a \ctype{std::string} and apply
the \exam{c_str()} method to it. These lifetime errors can
\emph{sometimes} be caught by specifying the Gnu C++ or Clang
options \exam{-Wreturn-stack-address} or
\exam{-Wreturn-local-addr} - as of 2023-04, Clang seems to do a
better analysis.}
\item
Most constructors, methods, and functions that accept \ctype{char*}
or \ctype{wchar_t*}
arguments also accept \ctype{std::string} or \ctype{std::wstring}
arguments. Where possible, encoding information can also be
specified.
\item
Type-checking methods have been added: PlTerm::type(),
PlTerm::is_variable(), PlTerm::is_atom(), etc.
\item
\ctype{PlString} has been renamed to \ctype{PlTerm_string} to make it clear
that it's a term that contains a Prolog string.
\item
More \exam{PL_...(term_t, ...)} methods have been added to \ctype{PlTerm},
and \exam{PL_...(atom_t, ...)} methods have been added to \ctype{PlAtom}.
Where appropriate, the arguments use \ctype{PlTerm}, \ctype{PlAtom}, etc.
instead of \ctype{term_t}, \ctype{atom_t}, etc.
\item
Most functions/methods that return an \ctype{int} for true/false now
return a C++ \ctype{bool}.
\item
The wrapped C types fields (\ctype{term_t}, \ctype{atom_t}, etc.)
have been renamed from \exam{handle}, \exam{ref}, etc. to
\exam{C_}.\footnote{This is done by subclassing from
\ctype{Wrapped<term_t>}, \ctype{Wrapped<atom_t>}, etc., which
define the field \exam{C_}, standard constructors, the methods
is_null(), not_null(), reset(), reset(v), reset_wrapped(v),
plus the constant \const{null}.} This value can be accessed by
the unwrap() and unwrap_as_ptr() methods.
There is also a ``friend'' function PlUnwrapAsPtr().
\item
A convenience function \exam{PlControl::context_unique_ptr<ContextType>()}
has been added, to simplify dynamic memory allocation in
non-deterministic predicates.
\item
A convenience function PlRewindOnFail() has been added,
to simplify non-deterministic code that does backtracking by
checking unification results.
\item
\ctype{PlStringBuffers} provides a simpler interface for allocating
strings on the stack than PL_STRINGS_MARK() and PL_STRINGS_RELEASE().
However, this is mostly not needed because most functions now
use \ctype{std::string}: see \secref{cpp2-strings}.
\item
\ctype{PlStream} provides a simpler interface for streams than
PL_get_stream(), PL_acquire_stream(), and PL_release_stream().
See \secref{cpp2-stream-io}.
\item
Wrapper classes for \ctype{record_t} have been added. The
\ctype{PlRecordExternalCopy} class contains the opaque handle,
as a convenience.
\item
Wrapper class for \ctype{control_t} has been added and the
PREDICATE_NONDET() has been modified to use it.
\end{itemize}
More details on the rationale and how to port from version 1 to
version 1 are given in \secref{cpp2-rationale} and
\secref{cpp2-porting-1-2}.
\section{A simple example}
\label{sec:cpp2-foreign-example}
Here is the ``simple example'' in the
\href{https://www.swi-prolog.org/pldoc/man?section=foreign-example}{Foreign Language Interface},
rewritten in C++. As before, it is compiled by
\begin{code}
swipl-ld -o calc -goal true calc.cpp calc.pl
\end{code}
\begin{code}
#include <string>
#include <SWI-cpp2.h>
int main(int argc, char **argv) {
PlEngine e(argv[0]);
// combine all the arguments in a single string
std::string expression;
for (int n = 1; n < argc; n++) {
if (n != 1) {
expression.append(" ");
}
expression.append(argv[n]);
}
// Lookup calc/1 and make the arguments and call
PlPredicate pred("calc", 1, "user");
PlTerm_string h0(expression);
PlQuery q(pred, PlTermv(h0), PL_Q_NORMAL);
return q.next_solution() ? 0 : 1;
}
\end{code}
\section{Sample code}
\label{sec:cpp2-sample-code}
The file
\href{https://github.com/SWI-Prolog/packages-cpp/blob/master/test_cpp.cpp}{test_cpp.cpp}
contains examples of Prolog predicates written in C++. This file is
used for testing (called from
\href{https://github.com/SWI-Prolog/packages-cpp/blob/master/test_cpp.pl}{test_cpp.pl}).
Notable examples:
\begin{itemize}
\item add_num/3 - same as \exam{A3 is A1+A2}, converting the sum
to an integer if possible.
\item name_arity/3 - C++ implementation of functor/3.
\item average/3 - computes the average of all the solutions to \arg{Goal}
\item can_unify/2 - tests whether the two arguments can unify with each
other, without instantiating anything (similar to unifiable/3).
\item eq1/1, eq2/2, eq3/2 - three different ways of implementing =/2.
\item write_list/1 - outputs the elements of a list, each on a new line.
\item cappend/3 - appends two lists (requires that the two lists are
instantiated).
\item square_roots/2 - same as \verb$bagof(Sqrt, X^(between(0,4,X), Sqrt is sqrt(X)), A2)$.
\item range_cpp/3 - on backtracking, generates all integers starting
at \arg{A1} and less than \arg{A2} (that is, one less than between/3).
\item int_info/2 - on backtracking generates all the integral types with their
minimum and maximum values.
\end{itemize}
The file
\href{https://github.com/SWI-Prolog/packages-cpp/blob/master/test_cpp.cpp}{likes.cpp}
contains a simple program that calls the Prolog predicate likes/2 and
happy/1 (these predicates are defined in
\href{https://github.com/SWI-Prolog/packages-cpp/blob/master/test_cpp.pl}{likes.pl}.
The usage and how to compile the code are in comments in \file{likes.cpp}
\section{Introduction}
\label{sec:cpp2-intro}
C++ provides a number of features that make it possible to define a
more natural and concise interface to dynamically typed languages than
plain C does. Using type-conversion (\jargon{casting}) and
overloading, native data-types can be easily translated into
appropriate Prolog types, automatic destructors can be used to deal
with most of the cleanup required and C++ exception handling can be
used to map Prolog exceptions and interface conversion errors to C++
exceptions, which are automatically mapped to Prolog exceptions as
control is turned back to Prolog.
However, there are subtle differences between Prolog and C++ that can
lead to confusion; in particular, the lifetime of terms do not fit
well with the C++ notion of constructor/destructor. It might be
possible to handle this with ``smart pointers'', but that would lead to
other complications, so the decision was made to provide a thin layer
between the underlying C functions and the C++ classes/methods/functions.
More information on the SWI-Prolog native types is given in
\href{https://www.swi-prolog.org/pldoc/man?section=foreigntypes}{Interface
Data Types}.
It would be tempting to use C++ implicit conversion operators and
method overloading to automatically convert between C++ types such as
\ctype{std::string} and \ctype{int64_t} and Prolog foreign language
interface types such as \ctype{term_t} and \ctype{atom_t}. However,
types such as \ctype{term_t} are unsigned integers, so many of the
automatic type conversions can inadvertently do something other than
what the programmer intended, resulting in subtle bugs that are
difficult to find. Therefore Version 2 of this interface reduces the
amount of automatic conversion and introduces some redundancy, to
avoid these subtle bugs, by using ``getter'' methods rather than
conversion operators, and using naming conventions for explicitly
specifying constructors.
\subsection{Acknowledgements}
\label{sec:cpp2-acknowledgements}
I would like to thank Anjo Anjewierden for comments on the definition,
implementation and documentation of the original C++ interface. Peter
Ludemann implemented the current version (2) of the interface (see
\secref{summary-cpp2-changes}).
\section{The life of a PREDICATE}
\label{sec:cpp2-life-of-a-predicate}
A foreign predicate is defined using the PREDICATE()
macro, plus a few variations on this, such as
PREDICATE_NONDET(), NAMED_PREDICATE(), and
NAMED_PREDICATE_NONDET(). These define an internal name for
the function, register it with the SWI-Prolog runtime (where it will
be picked up by the use_foreign_library/1 directive), and define the
names \exam{A1}, \exam{A2}, etc. for the arguments.\footnote{You can
define your own names for the arguments, for example:
\exam{auto dir=A1, db=A2;} or \exam{PlTerm options(A3);}.}
If a non-deterministic predicate is being
defined, an additional parameter \exam{handle} is defined (of type
\ctype{PlControl}).
The foreign predicate returns a value:
\begin{itemize}
\item \const{true} - success
\item \const{false} - failure or an error (see \secref{cpp2-exceptions}
and \href{https://www.swi-prolog.org/pldoc/man?section=foreign-exceptions}{Prolog exceptions in foreign code}).
\item ``retry'' - for non-deterministic predicates, gives a ``context''
for backtracking / redoing the call for the next solution.
\end{itemize}
If a predicate fails, it
could be simple failure (the equivalent of calling the builtin fail/0
predicate) or an error (the equivalent of calling the throw/1
predicate). When a Prolog exception is raised, it is important that a
return be made to the calling environment as soon as possible. In C
code, this requires checking every call for failure, which can become
cumbersome; with the C++ API, most errors are thrown as exceptions to
the enclosing PREDICATE() wrapper, and turned back into Prolog errors.
The C++ API provides Plx_*() functions that are the same as the PL_*()
functions except that where appropriate they check for exceptions and
thrown a PlException().
Addditionally, the function PlCheckFail() can be used to
check for failure and throw a \ctype{PlFail} exception that
is handled before returning to Prolog with failure.
The following three snippets do essentially the same thing (for
implementing the equivalent of =/2); however the first version (with
PlTerm::unify_term()) and second version (with Plx_unify()) throw a
C++ \ctype{PlExceptionFail} exception if there's an error and
otherwise return \const{true} or \const{false}; the third version
(with \cfuncref{PlCheckFail}{}) throws a \ctype{PlFail} exception for
failure (and \ctype{PlExceptionFail} for an error) and otherwise
returns \const{true} - the PREDICATE() wrapper handles all of these
appropriately and reports the same result back to Prolog; but you
might wish to distinguish the two situations in more complex code.
\begin{code}
PREDICATE(eq, 2)
{ return A1.unify_term(A2);
}
\end{code}
\begin{code}
PREDICATE(eq, 2)
{ return Plx_unify(A1.unwrap(), A2.unwrap()));
}
\end{code}
\begin{code}
PREDICATE(eq, 2)
{ PlCheckFail(A1.unify_term(A2));
return true;
}
\end{code}
\section{Overview}
\label{sec:cpp2-overview}
One useful area for exploiting C++ features is type-conversion.
Prolog variables are dynamically typed and all information is passed
around using the C-interface type \ctype{term_t}. In C++,
\ctype{term_t} is embedded in the \jargon{lightweight} class
\ctype{PlTerm}. Other lightweight classes, such as \ctype{PlAtom} for
\ctype{atom_t} are also provided. Constructors and operator
definitions provide flexible operations and integration with
important C-types (\ctype{char*}, \ctype{wchar_t*}, \ctype{long} and
\ctype{double}), plus the C++-types (\ctype{std::string},
\ctype{std::wstring}). (\ctype{char*} and \ctype{wchar_t*} are
deprecated in the C++ API; \ctype{std::string} and
\ctype{std::wstring} are safer and should be used instead.)
Another useful area is in handling errors and cleanup. Prolog errors
can be modeled using C++ exceptions; and C++'s destructors can be used
to clean up error situations, to prevent memory and other resource
leaks.
\subsection{Design philosophy of the classes}
\label{sec:cpp2-philosophy}
See also \secref{cpp2-naming} for more on naming conventions and
standard methods.
The general philosophy for C++ classes is that a ``half-created'' object
should not be possible - that is, the constructor should either
succeed with a completely usable object or it should throw an
exception. This API tries to follow that philosophy, but there are
some important exceptions and caveats. (For more on how the C++ and
Prolog exceptions interrelate, see \secref{cpp2-exceptions}.)
Most of the PL_*() functions have corresponding wrapper methods. For
example, PlTerm::get_atom() calls Plx_get_atom(), which calls
PL_get_atom(). If the PL_get_atom() has an error, it creates a Prolog
error; the Plx_get_atom() wrapper checks for this and converts the
error to a C++ exception, which is thrown; upon return to Prolog, the
exception is turned back into a Prolog error. Therfore, code typically
does not need to check for errors.
Some functions return \const{false} to indicate either failure or an
error, for example PlTerm::unify_term(); for such methods, a check is
made for an error and an exception is thrown, so the return value of
\const{false} only means failure. (The whole thing can be wrapped in
PlCheckFail(), in which case a \ctype{PlFail} exception is thrown,
which is converted to failure in Prolog.) For more on this, see
\secref{cpp2-wrapper-functions}, and for handling failure, see
\secref{cpp2-plframe}.
For PL_*() functions that take or return \ctype{char*} or
\ctype{wchar_t*} values, there are also wrapper functions and methods
that use \ctype{std::string} or \ctype{std::wstring}. Because these
copy the values, there is usually no need to enclose the calls with
\ctype{PlStringBuffers} (which wraps PL_STRING_MARK() and
PL_STRING_RELEASE()). See also the rationale for string:
\secref{cpp2-rationale-strings}.
Many of the classes (\ctype{PlAtom}, \ctype{PlTerm}, etc.) are thin
wrappers around the C interface's types (\ctype{atom_t},
\ctype{term_t}, etc.). As such, they inherit the concept of ``null''
from these types (which is abstracted as \ctype{PlAtom::null},
\ctype{PlTerm::null}, etc., which typically is equivalent to
\const{0}). Normally, you shouldn't need to check whether the object
is ``fully created'', for the rare situations where a check is needed,
the methods is_null() and not_null() are provided.
Most of the classes have constructors that create a
``complete'' object. For example,
\begin{code}
PlAtom foo("foo");
\end{code}
will ensure that the object \exam{foo} is useable and will throw an
exception if the atom can't be created. However, if you choose
to create a \ctype{PlAtom} object from an \ctype{atom_t} value,
no checking is done (similarly, no checking is done if you
create a \ctype{PlTerm} object from a \ctype{term_t}
value).
In many situations, you will be using a term; for these, there are
special constructors. For example:
\begin{code}
PlTerm_atom foo("foo"); // Same as PlTerm(PlAtom("foo"))
PlTerm_string str("a string");
\end{code}
To help avoid programming errors, some of the classes do not have a
default ``empty'' constructor. For example, if you with to create a
\ctype{PlAtom} that is uninitialized, you must explicitly use
\exam{PlAtom(PlAtom::null)}. This make some code a bit more cumbersome
because you can't omit the default constructors in struct initalizers.
Many of the classes have an as_string() method\footnote{This might be changed
in future to to_string(), to be consistent with
\exam{std::to_string()}}, which is useful for debugging.
The method names such as
as_int32_t() were chosen itnstead of to_int32_t() because they imply
that the representation is already an \ctype{int32_t}, and not that
the value is converted to a \ctype{int32_t}. That is, if the value is
a float, \ctype{int32_t} will fail with an error rather than (for example)
truncating the floating point value to fit into a 32-bit integer.
Many of the classes wrap long-lived items, such as atoms, functors,
predicates, or modules. For these, it's often a good idea to define
them as \ctype{static} variables that get created at load time, so
that a lookup for each use isn't needed (atoms are unique, so
\exam{PlAtom("foo")} requires a lookup for an atom \exam{foo} and
creates one if it isn't found).
C code sometimes creates objects ``lazily'' on first use:
\begin{code}
void my_function(...)
{ static atom_t ATOM_foo = 0;
...
if ( ! foo )
foo = PL_new_atom("foo");
...
}
\end{code}
For C++, this can be done in a simpler way, because C++
will call a local ``\ctype{static}'' constructor on
first use.
\begin{code}
void my_function(...)
{ static PlAtom ATOM_foo("foo");
}
\end{code}
The class \ctype{PlTerm} (which wraps \ctype{term_t}) is the most
used. Although a \ctype{PlTerm} object can be created
from a \ctype{term_t} value, it is intended to be used with a
constructor that gives it an initial value. The default constructor
calls PL_new_term_ref() and throws an exception if this fails. The
various constructors are described in
\secref{cpp2-plterm}. Note that the default constructor
is not public; to create a ``variable'' term, you should use the
subclass constructor PlTerm_var().
\subsection{Summary of files}
\label{sec:cpp2-files-summary}
The following files are provided:
\begin{itemize}
\item
\file{SWI-cpp2.h}
- Include this file to get the C++ API. It automatically includes
\file{SWI-cpp2-plx.h} and \file{SWI-cpp2.cpp}, unless the
macro \const{_SWI_CPP2_CPP_SEPARATE} is defined, in which case
you must compile \file{SWI-cpp2.cpp} separately.
\item
\file{SWI-cpp2.cpp}
- Contains the implementations of some methods and functions.
If you wish to compile this separately, you must define
the macro \const{_SWI_CPP2_CPP_SEPARATE} before your
include for \file{SWI-cpp2.h}.
\item
\file{SWI-cpp2-plx.h}
- Contains the wrapper functions for the most of the functions in
\file{SWI-Prolog.h}. This file is not intended to be used by
itself, but is \exam{\#include}d by \file{SWI-cpp2.h}.
\item
\file{SWI-cpp2-atommap.h}
- Contains a utility class for mapping atom-to-atom or atom-to-term,
which is useful for naming long-lived blobs instead of having to
pass them around as arguments.
\item
\file{test_cpp.cpp}, \file{test_cpp.pl}
- Contains various tests, including some longer sequences of
code that can help in understanding how the C++ API
is intended to be used.
In addition, there are \file{test_ffi.cpp}, \file{test_ffi.pl}, which
often have the same tests written in C, without the C++ API.
\end{itemize}
\subsection{Summary of classes}
\label{sec:cpp2-class-summary}
The list below summarises the classes defined in the C++ interface.
\begin{description}
\classitem{PlTerm}
Generic Prolog term that wraps \ctype{term_t} (for more details on \ctype{term_t}, see
\href{https://www.swi-prolog.org/pldoc/man?section=foreigntypes}{Interface Data Types}).
This is a ``base class'' whose constructor is
protected; subclasses specify the actual contents. Additional methods
allow checking the Prolog type, unification, comparison, conversion to
native C++-data types, etc. See \secref{cpp2-plterm-casting}.
For more details about \ctype{PlTerm}, see \secref{cpp2-plterm}
\classitem{PlCompound}
Subclass of \ctype{PlTerm} with constructors for building compound
terms. If there is a single string argument, then PL_chars_to_term()
or PL_wchars_to_term() is used to parse the string and create the
term. If the constructor has two arguments, the first is name of
a functor and the second is a \ctype{PlTermv} with the arguments.
\classitem{PlTermv}
Vector of Prolog terms. See PL_new_term_refs(). The \const{[]} operator
is overloaded to access elements in this vector. \ctype{PlTermv} is used
to build complex terms and provide argument-lists to Prolog goals.
\classitem{PlAtom}
Wraps \ctype{atom_t} in their internal Prolog
representation for fast comparison. (For more details on
\ctype{atom_t}, see
\href{https://www.swi-prolog.org/pldoc/man?section=foreigntypes}{Interface
Data Types}).
For more details of \ctype{PlAtom}, see \secref{cpp2-extraction-comparison-char-star}.
\classitem{PlFunctor}
A wrapper for \ctype{functor_t}, which maps to the internal
representation of a name/arity pair.
\classitem{PlPredicate}
A wrapper for \ctype{predicate_t}, which maps to the internal
representation of a Prolog predicate.
\classitem{PlModule}
A wrapper for \ctype{module_t}, which maps to the internal
representation of a Prolog module.
\classitem{PlQuery}
Represents opening and enumerating the solutions to a Prolog query.
\classitem{PlException}
If a call to Prolog results in an error, the C++ interface converts
the error into a \ctype{PlException} object and throws it. If the
enclosing code doesn't intercept the exception, the \ctype{PlException}
object is turned back into a Prolog error when control returns to Prolog
from the PREDICATE() macros. This is a subclass of \ctype{PlExceptionBase},
which is a subclass of \ctype{std::exception}.
\classitem{PlFrame}
This utility-class can be used to discard unused term-references as well
as to do \jargon{data-backtracking}.
\classitem{PlEngine}
This class is used in \jargon{embedded} applications (applications
where the main control is held in C++). It provides creation and
destruction of the Prolog environment.
\classitem{PlRegister}
Encapsulates PL_register_foreign() to allow using C++ global
constructors for registering foreign predicates.
\classitem{PlFail}
Can be thrown to short-circuit processing and return failure to
Prolog. Performance-critical code should use \exam{return false}
instead if failure is expected. An error can be signaled by calling
Plx_raise_exception() or one of the PL_*_error() functions and then
throwing \ctype{PlFail}; but it's better style to create the error
throwing one of the subclasses of \ctype{PlException} e.g.,
\exam{throw PlTypeError("int", t)}.
Subclass of \ctype{PlExceptionFailBase}.
\classitem{PlExceptionFail}
In some situations, a Prolog error cannot be turned into a
\ctype{PlException} object, so a \ctype{PlExceptionFail} object
is thrown. This is turned into failure by the PREDICATE()
macro, resulting in normal Prolog error handling.
Subclass of \ctype{PlExceptionFailBase}.
\classitem{PlExceptionBase}
A ``do nothing'' subclass of \ctype{std::exception}, to allow catching
\ctype{PlException}, \ctype{PlExceptionFail} or \ctype{PlFail}
in a single ``catch'' clause.
\classitem{PlExceptionFailBase}
A ``do nothing'' subclass of \ctype{PlExceptionBase}, to allow catching
\ctype{PlExceptionFail} or \ctype{PlFail}
in a single ``catch'' clause, excluding \ctype{PlException}.
\end{description}
\subsection{Wrapper functions}
\label{sec:cpp2-wrapper-functions}
The various PL_*() functions in \file{SWI-Prolog.h} have corresponding
Plx_*() functions, defined in \file{SWI-cpp2-plx.h}, which is always
included by \file{SWI-cpp2.h}. There are three kinds of wrappers, with
the appropriate one being chosen according to the semantics of
the wrapped function:
\begin{itemize}
\item
``as-is'' - the PL_*() function cannot cause an error. If it has a
return value, the caller will want to use it.
\item
``exception wrapper'' - the PL_*() function can return \const{false},
indicating an error. The Plx_*() function checks for this and
throws a \ctype{PlException} object containing the error. The
wrapper uses \exam{template<typename C_t> C_t PlEx(C_t rc)},
where \exam{C_t} is the return type of the PL_*() function.
\item
``success, failure, or error'' - the PL_*() function can return
\const{true} if it succeeds and \const{false} if it fails or has a
runtime error. If it fails, the wrapper checks for a Prolog error
and throws a \ctype{PlException} object containing the error. The
wrapper uses \exam{template<typename C_t> C_t PlWrap(C_t rc)},
where \exam{C_t} is the return type of the PL_*() function.
\end{itemize}
A few PL_*() functions do not have a corresponding Plx*() function
because they do not fit into one of these categories. For example,
PL_next_solution() has multiple return values (\const{PL_S_EXCEPTION},
\const{PL_S_LAST}, etc.) if the query was opened with the
\const{PL_Q_EXT_STATUS} flag.
Most of the PL_*() functions whose first argument is of type
\ctype{term_t}, \ctype{atom_t}, etc. have corresponding methods
in classes \ctype{PlTerm}, \ctype{PlAtom}, etc.
\emph{Important}: You should use the Plx_*() wrappers only in the
context of a PREDICATE() call, which will handle any C++ exceptions.
Some blob callbacks can also handle an exception (see
\secref{cpp2-blobs}). Everywhere else, the result of calling a
Plx_*() function is unpredicatable - probably a crash.
\subsection{Naming conventions, utility functions and methods}
\label{sec:cpp2-naming}
See also the discussion on design philosophy in \secref{cpp2-philosophy}.
The classes all have names starting with ``Pl'', using CamelCase;
this contrasts with the C functions that start with ``PL_'' and
use underscores.
The wrapper classes (\ctype{PlFunctor}, \ctype{PlAtom},
\ctype{PlTerm}), etc. all contain a field \exam{C_} that contains the
wrapped value (\ctype{functor_t}, \ctype{atom_t}, \ctype{term_t}
respectively). If this wrapped value is needed, it should be accessed
using the unwrap() or unwrap_as_ptr() methods.
In some cases, it's natural to use a pointer to a wrapper class.
For those, the function PlUnwrapAsPtr() returns \ctype{nullptr} if
the pointer is null; otherwise it returns the wrapped value (which
itself might be some kind of ``null'').
The wrapper classes, which subclass \ctype{WrappedC$<$\ldots$>$},
all define the following methods and constants:
\begin{itemize}
\item
Default constructor (sets the wrapped value to \exam{null}).
Some classes do not have a default constructor because it
can lead to subtle bugs - instead, they either have a different
way of creating the object or can use the ``null'' value for
the class.
\item
Constructor that takes the wrapped value (e.g.,
for \ctype{PlAtom}, the constructor takes an \ctype{atom_t}
value).
\item
\exam{C_} - the wrapped value.
This can be used directly when calling C functions,
for example, if \exam{t} and \exam{a} are of type \ctype{PlTerm}
and \ctype{PlAtom}: \verb$PlEx(PL_put_atom(t.unwrap(),a.unwrap()))$
(although it's better to do \verb$Plx_put_atom(t.unwrap(),a.unwrap())$,
which does the check).
\item
\exam{null} - the null value (typically \exam{0}, but
code should not rely on this).
\item
\exam{is_null()}, \exam{not_null()} - test
for the wrapped value being \exam{null}.
\item
\exam{reset()} - set the wrapped value to \exam{null}
\item
\exam{reset(new_value)} - set the wrapped value from the wrapped type
(e.g., PlTerm::reset(term_t new_value))
\item
\exam{reset_wrapped(new_value)} - set the wrapped value from the
same type (e.g., PlTerm::reset_wrapped(PlTerm new_value))
\item
The \ctype{bool} operator is disabled - you should
use not_null() instead.\footnote{The reason: a
\ctype{bool} conversion causes ambiguity with \exam{PlAtom(PlTterm)}
and \exam{PlAtom(atom_t)}.}
\end{itemize}
The method unwrap() can be used to access the \exam{C_} field, and can
be used wherever a \ctype{atom_t} or \ctype{term_t} is used. For
example, the PL_scan_options() example code can be written as follows.
Note the use of \exam{\&callback.unwrap()} to pass a pointer to the
wrapped \ctype{term_t} value.
\begin{code}
PREDICATE(mypred, 2)
{ auto options = A2;
int quoted = false;
size_t length = 10;
PlTerm_var callback;
PlCheckFail(PL_scan_options(options, 0, "mypred_options", mypred_options,
"ed, &length, &callback.unwrap()));
callback.record(); // Needed if callback is put in a blob that Prolog doesn't know about.
// If it were an atom (OPT_ATOM): register_ref().
<implement mypred>
}
\end{code}
For functions in \file{SWI-Prolog.h} that don't have a C++ equivalent
in \file{SWI-cpp2.h}, \cfuncref{PlCheckFail}{} is a convenience
function that checks the return code and throws a \ctype{PlFail}
exception on failure or \ctype{PlException} if there was an
exception. The enclosing PREDICATE() code catches \ctype{PlFail}
exceptions and converts them to the \ctype{foreign_t} return code for
failure. If the failure from the C function was due to an exception
(e.g., unification failed because of an out-of-memory condition), the
foreign function caller will detect that situation and convert the
failure to an exception.
The ``getter'' methods for \ctype{PlTerm} all throw an exception if the
term isn't of the expected Prolog type. The ``getter'' methods typically
start with ``as'', e.g. PlTerm::as_string(). There are also other ``getter''
methods, such as PlTerm::get_float_ex() that wrap PL_*() functions.
``getters'' for integers have an additional problem, in that C++
doesn't define the sizes of \ctype{int}, \ctype{long}, or
\ctype{size_t}. It seems to be impossible to make an overloaded method
that works for all the various combinations of integer types on all
compilers, so there are specific methods for \ctype{int64_t},
\ctype{uint64_t}, \ctype{size_t}.
In some cases,it is possible to overload methods; for example, this
allows the following code without knowing the exact definition of
\ctype{size_t}:
\begin{code}
PREDICATE(p, 1)
{ size_t sz;
A1.integer(&sz);
...
}
\end{code}
\emph{It is strongly recommended that you enable conversion checking.}
For example, with GNU C++, use these options (possibly with \exam{-Werror}):
\exam{-Wconversion -Warith-conversion -Wsign-conversion -Wfloat-conversion}.
There is an additional problem with characters - C promotes
them to \ctype{int} but C++ doesn't. In general, this shouldn't
cause any problems, but care must be used with the various
getters for integers.
\subsection{PlTerm class}
\label{sec:cpp2-plterm}
As we have seen from the examples, the \ctype{PlTerm} class plays a
central role in conversion and operating on Prolog data. This section
provides complete documentation of this class.
There are a number of subclasses that exist only to provide a safe way
of constructing at term.
There is also a subclass (\ctype{PlTermScoped}) that helps reclaim
terms.
Most of the \ctype{PlTerm} constructors are defined as subclasses of
\ctype{PlTerm}, with a name that reflects the Prolog type of what is
being created (e.g., \ctype{PlTerm_atom} creates a term from an atom;
\ctype{PlTerm_string} creates a term from a Prolog string). This is
done to ensure that the there is no ambiguity in the constructors -
for example, there is no way to distinguish between \ctype{term_t},
\ctype{atom_t}, and ordinary integers, so there are constructors
PlTerm(), PlTerm_atom(), and PlTerm_integer. All of the constructors
are ``explicit'' because implicit creation of \ctype{PlTerm} objects
can lead to subtle and difficult to debug errors.
If a constructor fails (e.g., out of memory), a \ctype{PlException} is
thrown. The class and subclass constructors are as follows.
\begin{description}
\constructor{PlTerm}{PlAtom a}
Creates a term reference containing an atom, using PL_put_atom().
It is the same as PlTerm_atom().
\constructor{PlTerm}{term_t t}
Creates a term reference from a C term (by wrapping it).
As this is a lightweight class, this is a no-op in
the generated code.
\constructor{PlTerm}{PlRecord r}
Creates a term reference from a record, using PL_recorded().
\constructor{PlTerm_atom}{atom_t a}
Creates a term reference containing an atom.
\constructor{PlTerm_atom}{PlAtom a}
Creates a term reference containing an atom.
\constructor{PlTerm_atom}{const char *text}
Creates a term reference containing an atom, after creating the atom from the \arg{text}.
\constructor{PlTerm_atom}{const wchar_t *text}
Creates a term reference containing an atom, after creating the atom from the \arg{text}.
\constructor{PlTerm_atom}{const std::string\& text}
Creates a term reference containing an atom, after creating the atom from the \arg{text}.
\constructor{PlTerm_atom}{const std::wstring\& text}
Creates a term reference containing an atom, after creating the atom from the \arg{text}.
\constructor{PlTerm_var}{}
Creates a term reference for an uninstantiated variable.
Typically this term is then unified with another object.
\constructor{PlTerm_term_t}{}
Creates a term reference from a C \ctype{term_t}.
This is a lightweight class, so no code is generated.
\constructor{PlTerm_integer}{}
Subclass of \ctype{PlTerm} with constructors for building a term that
contains a Prolog integer from a
\ctype{long}.\footnote{PL_put_integer() takes a \ctype{long} argument.}
\constructor{PlTerm_int64}{}
Subclass of \ctype{PlTerm} with constructors for building
a term that contains a Prolog integer from a \ctype{int64_t}.
\constructor{PlTerm_uint64}{}
Subclass of \ctype{PlTerm} with constructors for building
a term that contains a Prolog integer from a \ctype{uint64_t}.
\constructor{PlTerm_size_t}{}
Subclass of \ctype{PlTerm} with constructors for building
a term that contains a Prolog integer from a \ctype{size_t}.
\constructor{PlTerm_float}{}
Subclass of \ctype{PlTerm} with constructors for building
a term that contains a Prolog float.
\constructor{PlTerm_pointer}{}
Subclass of \ctype{PlTerm} with constructors for building
a term that contains a raw pointer. This is mainly for
backwards compatibility; new code should use \jargon{blobs}.
A pointer is
represented in Prolog as a mangled integer. The mangling is designed
to make most pointers fit into a \jargon{tagged-integer}. Any valid
pointer can be represented. This mechanism can be used to represent
pointers to C++ objects in Prolog. Please note that \ctype{MyClass}
should define conversion to and from \ctype{void *}.
\begin{code}
PREDICATE(make_my_object, 1)
{ auto myobj = new MyClass();
return A1.unify_pointer(myobj);
}
PREDICATE(my_object_contents, 2)
{ auto myobj = static_cast<MyClass*>(A1.as_pointer());
return A2.unify_string(myobj->contents);
}
PREDICATE(free_my_object, 1)
{ auto myobj = static_cast<MyClass*>(A1.as_pointer());
delete myobj;
return true;
}
\end{code}
\constructor{PlTerm_string}{}
Subclass of \ctype{PlTerm} with constructors for building
a term that contains a Prolog string object.
For constructing a term from the text form, see
\ctype{PlCompound}.
\constructor{PlTerm_list_codes}{}
Subclass of \ctype{PlTerm} with constructors for building
Prolog lists of character integer values.
\constructor{PlTerm_chars}{}
Subclass of \ctype{PlTerm} with constructors for building
Prolog lists of one-character atoms (as atom_chars/2).
\constructor{PlTerm_tail}{}
SubClass of \ctype{PlTerm} for building and analysing Prolog lists.
\end{description}
The methods are:
\begin{description}
\cfunction{bool}{PlTerm::get_atom}{PlAtom* a} Wrapper of PL_get_atom(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_bool}{int* value} Wrapper of PL_get_bool(), throwing an exception on Prolog error.
\cfunction{chars}{PlTerm::get_chars}{char**s, unsigned int flags} Wrapper of PL_get_chars(), throwing an exception on Prolog error.
\cfunction{chars}{PlTerm::get_list_chars}{char**s, unsigned int flags} Wrapper of PL_get_list_chars(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_list_chars}{char **s, unsigned int flags} Wrappper of PL_get_list_chars(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_atom_nchars}{size_t *len, char **a} Wrappper of PL_get_atom_nchars(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_list_nchars}{size_t *len, char **s, unsigned int flags} Wrappper of PL_get_list_nchars(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_nchars}{size_t *len, char **s, unsigned int flags} Wrappper of PL_get_nchars(), throwing an exception on Prolog error. Deprecated: see PlTerm::get_nchars(flags) that returns a \ctype{std::string}. If you use this, be sure to wrap it with \ctype{PlStringBuffers}, and if you use the \const{BUF_MALLOC} flag, you can use \ctype{std::unique_ptr<char, decltype(\&PL_free)>} to manage the pointer.
\cfunction{bool}{PlTerm::get_wchars}{size_t *length, pl_wchar_t **s, unsigned flags} Wrappper of PL_get_wchars(), throwing an exception on Prolog error. Deprecated: see PlTerm::getwchars(flags) that returns a \ctype{std::wstring}. If you use this, be sure to wrap it with \ctype{PlStringBuffers}, and if you use the \const{BUF_MALLOC} flag, you can use \ctype{std::unique_ptr<char, decltype(\&PL_close)>} to manage the pointer.
\cfunction{bool}{PlTerm::get_integer}{int *i} Wrappper of PL_get_integer(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_long}{long *i} Wrappper of PL_get_long(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_intptr}{intptr_t *i} Wrappper of PL_get_intptr(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_pointer}{void **ptr} Wrappper of PL_get_pointer(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_float}{double *f} Wrapper Plx_get_float(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_functor}{PlFunctor *f} Wrappper of PL_get_functor(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_name_arity}{PlAtom *name, size_t *arity} Wrappper of PL_get_name_arity(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_compound_name_arity}{PlAtom *name, size_t *arity} Wrappper of PL_get_compound_name_arity(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_module}{PlModule *module} Wrappper of PL_get_module(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_arg}{size_t index, PlTerm a} Wrappper of PL_get_arg(index, ), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_dict_key}{PlAtom key, PlTerm dict, PlTerm value} Wrappper of PL_get_dict_key(key.), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_list}{PlTerm h, PlTerm t} Wrappper of PL_get_list(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_head}{PlTerm h} Wrappper of PL_get_head(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_tail}{PlTerm t} Wrappper of PL_get_tail(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_nil}{} Wrappper of PL_get_nil(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_blob}{void **blob, size_t *len, PL_blob_t **type} Wrappper of PL_get_blob(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_file_name}{char **name, int flags} Wrappper of PL_get_file_name() (does not throw a C++ exception).
\cfunction{bool}{PlTerm::get_file_nameW}{wchar_t **name, int flags} Wrappper of PL_get_file_nameW(), (does not throw a C++ exception).
\cfunction{std::string}{PlTerm::get_file_name}{char **name, int flags} Wrapper of PL_get_file_name(), ignoring \const{PL_FILE_NOERRORS} - throws \const{PlFail} on failure, which is interpreted by the enclosing \ctype{PREDICATE} as either failure or an error, depending on the flag bit \const{PL_FILE_NOERRORS}.
\cfunction{std::wstring}{PlTerm::get_file_nameW}{int flags} Same as PlTerm::get_file_name(), but returns a wide-character string.
\cfunction{bool}{PlTerm::get_attr}{term_t a} Wrappper of PL_get_attr(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_atom_ex}{PlAtom *a} Wrapper of PL_get_atom_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_integer_ex}{int *i} Wrapper of PL_get_integer_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_long_ex}{long *i} Wrapper of PL_get_long_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_int64_ex}{int64_t *i} Wrapper of PL_get_int64_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_uint64_ex}{uint64_t *i} Wrapper of PL_get_uint64_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_intptr_ex}{intptr_t *i} Wrapper of PL_get_intptr_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_size_ex}{size_t *i} Wrapper of PL_get_size_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_bool_ex}{int *i} Wrapper of PL_get_bool_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_float_ex}{double *f} Wrapper of PL_get_float_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_char_ex}{int *p, int eof} Wrapper of PL_get_char_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::unify_bool_ex}{int val} Wrapper of PL_unify_bool_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_pointer_ex}{void **addrp} Wrapper of PL_get_pointer_ex(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::unify_list_ex}{PlTerm h, PlTerm t} Wrappper of PL_unify_list_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::unify_nil_ex}{} Wrapper of PL_unify_nil_ex(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::get_list_ex}{PlTerm h, PlTerm t} Wrappper of PL_get_list_ex(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::get_nil_ex}{} Wrapper of PL_get_nil_ex(), throwing an exception on Prolog error.
\cfunction{int}{PlTerm::type}{} Wrapper of PL_term_type(unwrap()), returning \const{PL_VARIABLE}, \const{PL_ATOM}, etc, throwing an exception on Prolog error.
bo\cfunction{ol}{PlTerm::is_attvar}{} Wrappper of PL_is_attvar(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_variable}{} Wrappper of PL_is_variable(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_ground}{} Wrappper of PL_is_ground(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_atom}{} Wrappper of PL_is_atom(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_integer}{} Wrappper of PL_is_integer(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_string}{} Wrappper of PL_is_string(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_atom_or_string}{} \exam{is_atom()} or \exam{is_string()}.
\cfunction{bool}{PlTerm::is_float}{} Wrappper of PL_is_float(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_rational}{} Wrappper of PL_is_rational(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_compound}{} Wrappper of PL_is_compound(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_callable}{} Wrappper of PL_is_callable(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_list}{} Wrappper of PL_is_list(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_dict}{} Wrappper of PL_is_dict(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_pair}{} Wrappper of PL_is_pair(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_atomic}{} Wrappper of PL_is_atomic(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_number}{} Wrappper of PL_is_number(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_acyclic}{} Wrappper of PL_is_acyclic(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_functor}{PlFunctor f} Wrappper of PL_is_functor(), throwing an exception on Prolog error.
\cfunction{bool}{PlTerm::is_blob}{PL_blob_t **type} Wrappper of PL_is_blob(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::must_be_attvar}{} Throw \ctype{PlTypeError} if PlTerm::is_attvar() fails.
\cfunction{void}{PlTerm::must_be_variable}{} Throw \ctype{PlTypeError} if PlTerm::is_variable() fails.
\cfunction{void}{PlTerm::must_be_ground}{} Throw \ctype{PlTypeError} if PlTerm::is_ground() fails.
\cfunction{void}{PlTerm::must_be_atom}{} Throw \ctype{PlTypeError} if PlTerm::is_atom() fails.
\cfunction{void}{PlTerm::must_be_integer}{} Throw \ctype{PlTypeError} if PlTerm::is_integer() fails.
\cfunction{void}{PlTerm::must_be_string}{} Throw \ctype{PlTypeError} if PlTerm::is_string() fails.
\cfunction{void}{PlTerm::must_be_atom_or_string}{} Throw \ctype{PlTypeError} if PlTerm::is_atom_or_string() fails.
\cfunction{void}{PlTerm::must_be_float}{} Throw \ctype{PlTypeError} if PlTerm::is_float() fails.
\cfunction{void}{PlTerm::must_be_rational}{} Throw \ctype{PlTypeError} if PlTerm::is_rational() fails.
\cfunction{void}{PlTerm::must_be_compound}{} Throw \ctype{PlTypeError} if PlTerm::is_compound() fails.
\cfunction{void}{PlTerm::must_be_callable}{} Throw \ctype{PlTypeError} if PlTerm::is_callable() fails.
\cfunction{void}{PlTerm::must_be_list}{} Throw \ctype{PlTypeError} if PlTerm::is_list() fails.
\cfunction{void}{PlTerm::must_be_dict}{} Throw \ctype{PlTypeError} if PlTerm::is_dict() fails.
\cfunction{void}{PlTerm::must_be_pair}{} Throw \ctype{PlTypeError} if PlTerm::is_pair() fails.
\cfunction{void}{PlTerm::must_be_atomic}{} Throw \ctype{PlTypeError} if PlTerm::is_atomic() fails.
\cfunction{void}{PlTerm::must_be_number}{} Throw \ctype{PlTypeError} if PlTerm::is_number() fails.
\cfunction{void}{PlTerm::must_be_acyclic}{} Throw \ctype{PlTypeError} if PlTerm::is_acyclic() fails.
\cfunction{void}{PlTerm::put_variable}{} Wrapper of PL_put_variable(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_atom}{PlAtom a} Wrapper of PL_put_atom(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_bool}{int val} Wrapper of PL_put_bool(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_atom_chars}{const char *chars} Wrapper of PL_put_atom_chars(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_string_chars}{const char *chars} Wrapper of PL_put_string_chars(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_chars}{int flags, size_t len, const char *chars} Wrapper of PL_put_chars(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_list_chars}{const char *chars} Wrapper of PL_put_list_chars(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_list_codes}{const char *chars} Wrapper of PL_put_list_codes(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_atom_nchars}{size_t l, const char *chars} Wrapper of PL_put_atom_nchars(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_string_nchars}{size_t len, const char *chars} Wrapper of PL_put_string_nchars(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_list_nchars}{size_t l, const char *chars} Wrapper of PL_put_list_nchars(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_list_ncodes}{size_t l, const char *chars} Wrapper of PL_put_list_ncodes(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_integer}{long i} Wrapper of PL_put_integer(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_pointer}{void *ptr} Wrapper of PL_put_pointer(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_float}{double f} Wrapper of PL_put_float(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_functor}{PlFunctor functor} Wrapper of PL_put_functor(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_list}{} Wrapper of PL_put_list(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_nil}{} Wrapper of PL_put_nil(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_term}{PlTerm t2} Wrapper of PL_put_term(), throwing an exception on Prolog error.
\cfunction{void}{PlTerm::put_blob}{void *blob, size_t len, PL_blob_t *type} Wrapper of PL_put_blob(), throwing an exception on Prolog error.
\cfunction{PlRecord}{PlTerm::record}{} Returns a \ctype{PlRecord} constructed from the term.
Same as PlRecord(*this).
\cfunction{void}{PlTerm::integer}{bool *v} Wrapper of PL_cvt_i_bool().
\cfunction{void}{PlTerm::integer}{char *v} Wrapper of PL_cvt_i_char().
\cfunction{void}{PlTerm::integer}{int *v} Wrapper of PL_cvt_i_int().
\cfunction{void}{PlTerm::integer}{long *v} Wrapper of PL_cvt_i_long().
\cfunction{void}{PlTerm::integer}{long long *v} Wrapper of PL_cvt_i_llong().
\cfunction{void}{PlTerm::integer}{short *v} Wrapper of PL_cvt_i_short().
\cfunction{void}{PlTerm::integer}{signed char *v} Wrapper of PL_cvt_i_schar().
\cfunction{void}{PlTerm::integer}{unsigned char *v} Wrapper of PL_cvt_i_uchar().
\cfunction{void}{PlTerm::integer}{unsigned int *v} Wrapper of PL_cvt_i_uint().
\cfunction{void}{PlTerm::integer}{unsigned long *v} Wrapper of PL_cvt_i_ulong().
\cfunction{void}{PlTerm::integer}{unsigned long long *v} Wrapper of PL_cvt_i_ullong().
\cfunction{void}{PlTerm::integer}{unsigned short *v} Wrapper of PL_cvt_i_ushort().
\cfunction{const std::string}{PlTerm::as_string}{PlEncoding enc=ENC_OUTPUT}
Calls PlTerm::get_nchars(CVT_ALL|CVT_WRITEQ|CVT_EXCEPTION).
This method is provided mainly for debugging.
\emph{The definition is subject to change in future} - if you want precise
control, use PlTerm::get_nchars().
\cfunction{const std::wstring}{PlTerm::as_wstring}{}
Calls PlTerm::get_wchars(CVT_ALL|CVT_WRITEQ|CVT_EXCEPTION).
This method is provided mainly for debugging.
\emph{The definition is subject to change in future} - if you want precise
control, use PlTerm::get_nchars().
\cfunction{long}{PlTerm::as_long}{} Wrapper of PL_cvt_i_*().
\cfunction{int32_t}{PlTerm::as_int32_t}{} Wrapper of PL_cvt_i_*().
\cfunction{uint32_t}{PlTerm::as_uint32_t}{} Wrapper of PL_cvt_i_*().
\cfunction{uint64_t}{PlTerm::as_uint64_t}{} Wrapper of PL_cvt_i_*().
\cfunction{int64_t}{PlTerm::as_int64_t}{} Wrapper of PL_cvt_i_*().
\cfunction{size_t}{PlTerm::as_size_t}{} Wrapper of PL_cvt_i_*().
\cfunction{int}{PlTerm::as_int}{} Wrapper of PL_cvt_i_*().
\cfunction{unsigned}{PlTerm::as_uint}{} Wrapper of PL_cvt_i_*().
\cfunction{unsigned long}{PlTerm::as_ulong}{} Wrapper of PL_cvt_i_*().
\cfunction{bool}{PlTerm::as_bool}{} Wrapper of PL_cvt_i_*().
\cfunction{void}{PlTerm::as_nil}{} Wrapper of PL_get_nil_ex(), throwing an
exception if the term isn't ``nil''.
\cfunction{double}{PlTerm::as_float}{} Wrapper of PL_get_float_ex(), throwing an
exception if the term isn't a float.
\cfunction{double}{PlTerm::as_double}{} Wrapper of PL_get_float_ex(), throwing an
exception if the term isn't a float.
\cfunction{void *}{PlTerm::as_pointer}{} (Deprecated: should use blob API).
Wrapper of PL_get_pointer_ex(), throwing an exception if the term isn't a blob.
\cfunction{const std::string}{PlTerm::get_nchars}{unsigned int flags}
Calls PL_get_nchars(..., flags) and converts the result to a \ctype{std::string}.
The flags \const{BUF_MALLOC}, \const{BUF_STACK}, and \const{BUF_ALLOW_STACK}
are ignored and replaced by \const{BUF_DISCARDABLE}.
\cfunction{const std::wstring}{PlTerm::get_wchars}{unsigned int flags}
Calls PL_get_wchars(..., flags) and converts the result to a \ctype{std::wstring}.
The flags \const{BUF_MALLOC}, \const{BUF_STACK}, and \const{BUF_ALLOW_STACK}
are ignored and replaced by \const{BUF_DISCARDABLE}.
\cfunction{PlAtom}{PlTerm::as_atom}{} Wrapper of PL_get_atom_ex(), throwing an exception
if the term is not an atom.
\cfunction{bool}{PlTerm::eq_if_atom}{PlAtom a} Returns true if the term is an atom
and equal to \arg{a}.
\cfunction{PlTerm::operator []}{size_t index} Wrapper for PL_get_arg(),
throwing an exception if the term isn't a compound or the index is
out of range.
\cfunction{size_t}{PlTerm::arity}{} Gets the arity of the term; throws \ctype{PlTypeError} if not a "compound" or atom.
\cfunction{PlAtom}{PlTerm::name}{} Gets the name of the term; \ctype{PlTypeError} if not a "compound" or atom.
\cfunction{bool}{name_arity}{PlAtom *name, size_t *arity} Wrapper of PL_get_name_arity(); \arg{name} and/or \arg{arity} can be \const{nullptr}. Returns \const{false} if the term
isn't a compound or atom.
\cfunction{PlTerm}{PlTerm::copy_term_ref}{} Wrapper of PL_copy_term_ref(). Throws
an exception error (e.g., \ctype{PlResourceError}).
\cfunction{void}{PlTerm::free_term_ref}{} Wrapper of PL_free_term_ref().
Is safe to use if the object wraps \const{PlTerm::null}.
Does \emph{not} reset the wrapped term. This is used implicitly in
\ctype{PlTermScoped}'s destructor, which does reset the wrapped term.
\cfunction{void}{PlTerm::free_term_ref_reset}{} Same as
PlTerm::free_term_ref() plus PlTerm::reset().
\ctype{PlTermScoped}'s destructor, which does reset the wrapped term.
\cfunction{bool}{nify_term}{PlTerm t2} Wrapper of PL_unify(). Throws an exception
on error and returns \const{false} if unification fails. If on failure, there
isn't an immediate return to Prolog (e.g., by wrapping the call with
PlCheckFail()), this method should be called within the context
of \ctype{PlFrame}, and PlFrame::rewind() should be called.
\cfunction{bool}{PlTerm::unify_atom}{PlAtom a} Wrapper of PL_unify_atom(), throwing
an exception on error.
\cfunction{bool}{PlTerm::unify_chars}{int flags, size_t len, const char *s} Wrapper of PL_unify_chars(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_chars}{int flags, const std::string\& s} Wrapper of PL_unify_chars(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_atom}{const char* v} Wrapper of PL_unify_atom_chars(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_atom}{const wchar_t* v} Wrapper of PL_unify_wchars(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_atom}{const std::string\& v} Wrapper of PL_unify_atom_nchars(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_atom}{const std::wstring\& v} Wrapper of PL_unify_wchars(), throwing an exception on error.
% bool unify_list_codes(const char* v) const { return Plx_unify_list_codes(unwrap(), v); } // TODO: [[deprecated]]
% bool unify_list_chars(const char* v) const { return Plx_unify_list_chars(unwrap(), v); } // TODO: [[deprecated]]
cfunction{bool}{PlTerm::unify_integer}{bool v} Wrapper of PL_unify_int64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{char v} Wrapper of PL_unify_int64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{int v} Wrapper of PL_unify_int64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{long v} Wrapper of PL_unify_int64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{long long v} Wrapper of PL_unify_int64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{short v} Wrapper of PL_unify_int64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{signed char v} Wrapper of PL_unify_int64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{unsigned char v} Wrapper of PL_unify_uint64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{unsigned int v} Wrapper of PL_unify_uint64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{unsigned long v} Wrapper of PL_unify_uint64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{unsigned long long v} Wrapper of PL_unify_uint64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_integer}{unsigned short v} Wrapper of PL_unify_uint64(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_float}{double v} Wrapper of PL_unify_float(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_string}{const std::string\& v} Wrapper of PL_unify_string_nchars(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_string}{const std::wstring\& v} Wrapper of PL_unify_wchars(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_functor}{PlFunctor f} Wrapper of PL_unify_functor(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_pointer}{void *ptr} Wrapper of PL_unify_pointer(), throwing an exception on error. An alternative to this is to use a blob that wraps a pointer - see \secref{cpp2-blobs-sample-code-pointer}.
\cfunction{bool}{PlTerm::unify_nil}{} Wrapper of PL_unify_nil(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_list}{PlTerm h, PlTerm t} Wrapper of PL_unify_list(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_bool}{bool val} Wrapper of PL_unify_bool(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_blob}{const PlBlob* blob} Wrapper of PL_unify_blob(), throwing an exception on error.
\cfunction{bool}{PlTerm::unify_blob}{const void *blob, size_t len, const PL_blob_t *type} Wrapper of PL_unify_blob(), throwing an exception on error.
\cfunction{int}{PlTerm::compare}{PlTerm t2} Wrapper for PL_compare(), returning -1, 0, 1
for the result of standard order comparison of the term with \arg{a2}.
\cfunction{bool}{operator ==}{PlTerm t2} \exam{compare(t2) == 0}.
\cfunction{bool}{operator !=}{PlTerm t2} \exam{compare(t2) != 0}.
\cfunction{bool}{operator < }{PlTerm t2} \exam{compare(t2) < 0}.
\cfunction{bool}{operator > }{PlTerm t2} \exam{compare(t2) > 0}.
\cfunction{bool}{operator <=}{PlTerm t2} \exam{compare(t2) <= 0}.
\cfunction{bool}{operator >=}{PlTerm t2} \exam{compare(t2) >= 0}.
\cfunction{int}{write}{IOSTREAM *s, int precedence, int flags} Wrapper for PL_write_term().
\cfunction{void}{reset_term_refs}{} Wrapper for PL_reset_term_refs().
\cfunction{bool}{call}{PlModule module} Wrapper for PL_call(unwrap()); \arg{module} defaults to ``null''. Throws a C++ exception if there's a Prolog error, otherwise returns the success or failure of the call.
\end{description}
\subsection{PlTermScoped class (experimental)}
\label{sec:cpp-plterm-scoped}
\emph{This class is experimental and subject to change.}
Normally all term references in a \jargon{scope} are discarded
together or all term references created after a specific one are
reclaimed using PlTerm::reset_term_refs(). A \ctype{PlTermScoped}
object is the same as a \ctype{PlTerm} object except that
PL_free_term_ref() is called on its wrapped term when the object goes
out of scope. This shrinks the current foreign frame if the term is
the last one in the frame and otherwise it marks it for reuse.
Here is an example, where \ctype{PlTermScoped} is inside a for-loop.
If \ctype{PlTerm} were used instead, the stack would grow by the number
of items in the array; \ctype{PlTermScoped} ensures that stack doesn't
grow.\footnote{Assuming that unify_atom_list() is called from a
predicate implementation, if \ctype{PlTerm} were used instead of
\ctype{PlTermCopy}, all the created terms would be discarded when
the Prolog stack frame is unwound; the use of \ctype{PlTermScoped}
reuses the terms in that stack frame.}
A slightly more effiicient way of preventing the Prolog stack from
growing is to use PlTerm::put_term() to reuse a term reference; but
that is more difficult to understand and also more error-prone.
\begin{code}
bool
unify_atom_list(const std::vector<std::string>& array, PlTerm list)
{ PlTermScoped tail(list); // calls PL_copy_term_ref() to copy `list`
for( auto item : array )
{ PlTermScoped head; // var term
PlCheckFail(tail.unify_list(head, tail));
PlCheckFail(head.unify_chars(PL_ATOM, item));
}
return tail.unify_nil();
}
\end{code}
The design of \ctype{PlTermScoped} is modeled on
\ctype{std::unique_ptr}\footnote{\ctype{unique_ptr} was originally
called \ctype{scoped_ptr} in the Boost libraries, but the name was
changed to contrast with \ctype{std::shared_ptr}, which is
reference-counted.} and uses \jargon{move semantics} to ensure
safety.\footnote{\jargon{Move semantics} are a relatively new feature
in C++ and can be a bit difficult to understand. Roughly speaking, a
\jargon{move} is a copies the object and then calls its destructor, so
that any further use of the object is an error. If an object defines
move methods or constructors, it can optimize this operation, and also
can catch certain kinds of errors at compile time.}
A \ctype{PlTermScoped} object can be created either with or without a
wrapped term - the PlTermScoped::reset() method sets (or nulls) the
wrapped term. A \ctype{PlTermScoped} object cannot be copied or
passed as a value to a function; the PlTermScoped::release() method
returns the wrapped term and resets the \ctype{PlTermScoped} object so
that any further use of the \ctype{PlTermScoped} object is an error.
As shown in the example above, \ctype{PlTermScoped} can be used
instead of \ctype{PlTerm}, in places where a loop would otherwise
cause the stack to grow. There are limitations on the operations that
are allowed on a \ctype{PlTermScoped} object; in particular, a
\ctype{PlTermScoped} object cannot be copied and cannot be implicitly
converted to a \ctype{Plterm}.
The \ctype{PlTermScoped} constructors always create a new term ref, by
calling either PL_new_term_ref() or PL_copy_term_ref(). If you try to
copy or create a \ctype{PlTermScoped} object from another
\ctype{PlTermScoped} object, you will get a compile-time error; you can
set the value from a \ctype{PlTerm} object, which can be obtained by
calling PlTermScoped::release().
The methods derived from the PL_put_*() and PL_cons_*() functions
should not be used with a \ctype{PlTermScoped} object. If you need to
use these, you can use PlTermScoped::get() to get a \ctype{PlTerm},
for which a put_*() method can be used.
To copy a \ctype{PlTermScoped} object or to pass it as a value in
a function call, use the PlTermScoped::release()
method or std::move():
\begin{code}
PlTermScoped ts(...);
PlTerm t;
// Copy to a PlTerm:
t = ts.release(); // or: t = std::move(ts);
// Pass as a value to a function:
foo(ts.release()); // or: foo(std::move(ts);
// Copy to a PlTermScoped:
PlTermScoped ts2;
ts2.reset(ts.release()); // or: ts2.reset(std::move(ts));
\end{code}
The methods are (in addition to, or overriding the methods in \ctype{PlTerm}):
\begin{description}
\constructor{PlTermScoped}{} - same as PlTermScoped(PlTerm::null).
\constructor{PlTermScoped}{PlTerm t} - set the value
from t.copy_term_ref()
\constructor{PlTermScoped}{term_t t} - same as PlTermScoped(PlTerm(t)).
\constructor{PlTermScoped}{PlTermScoped\&\& m} - create a
new wrapped object from \arg{m} and reset \arg{m}. This is typically
used with std::move().
\cfunction{PlTermScoped\&}{PlTermScoped::operator=}{PlTermScoped\&\& m} - copy
\arg{m} and reset it. This is typically used with std::move().
\destructor{PlTermScoped} - if the wrapped term not null,
call PL_free_term_ref() on it.
\constructor{PlTermScoped}{PlTermScoped\& m} - deleted method.
\cfunction{PlTermScoped\&}{operator=}{PlTermScoped\& m} - deleted method.
\cfunction{void}{PlTermScoped::reset}{} - same as
PlTermScoped::reset(PlTerm::null).
\cfunction{void}{PlTermScoped::reset}{PlTerm src} -
sets the wrapped term from \arg{src}. To set the wrapped term
from a \ctype{PlTermScoped}, use PlTermScoped::release() to
convert it to a \ctype{PlTerm}.
\cfunction{PlTerm}{PlTermScoped::get}{} - convert the object
to a \ctype{PlTerm}. This is typically used when calling a function
that expects a \ctype{PlTerm} object and which will not call
PlTerm::free_term_ref() on it.
\cfunction{PlTerm}{PlTermScoped::release}{} - typically used
in the context \exam{t2.reset(t.release())} to copy a
\ctype{PlTermScoped}; this can also be written
\exam{t2=std::move(t)}.
\cfunction{void}{PlTermScoped::swap}{PlTermScoped\& src} -
swap two \ctype{PlTermScoped} objects' wrapped terms.
\end{description}
\subsection{Blobs}
\label{sec:cpp2-blobs}
\emph{Nomenclature warning:}
There are two different \ctype{release()} functions: % TODO: \cfunctionref
\begin{itemize}
\item The release() callback for a blob (see the definition
of \ctype{PL_blob_t}).
\item std::unique_ptr::release(), which passes ownership of
a \ctype{unique_ptr}.
\end{itemize}
\emph{Disclaimer:}
The blob API for C++ is not completely general, but is designed to
make common use cases easy to write. For other use cases, the
underlying C API can still be used. The use case is:
\begin{itemize}
\item The blob is defined as a subclass of \ctype{PlBlob}, which
provides a number of fields and methods, of which a few
can be overridden in the blob (notably: write_fields(),
compare_fields(), save(), load(), and the destructor).
\item The blob will not be subclassed.
\item The blob contains the foreign object or a pointer to it (e.g.,
a database connection or a pointer to a database connection),
plus optionally some other data.
\item The blob is created by a predicate that makes the foreign
object and stores it (or a pointer to it) within the blob -
for example, making a connection to a database or compiling
a regular expression into an internal form. This ``create'' predicate
uses \ctype{std::unique_ptr} to manage the blob (that is,
the blob is created using the \op{new} operator and is not
created on the stack).
\item Optionally, there can be a predicate that deletes the foreign object,
such as a file or database connection close.
\item The blob can be garbage collected, althought this might require
calling the predicate that deletes the foreign object first.
There is no provision for handling ``weak references'' (e.g.,
a separate lookup table or cache for the foreign objects).
\item The blob must have a default constructor that sets all the
fields to appropriate initial values.\footnote{This is
used by the load() callback; the default implementation
for a C++ blob is to throw an error.}
\item The blob's constructor throws an exception and cleans up any
resources if it cannot create the blob.\footnote{This is not a
strong requirement, but the code is simpler if this style is
used.}
\item The foreign object can be deleted when the blob is deleted.
That is, the foreign object is created using the \const{new}
operator and passes ownership to the blob. More complex
behavior is possible, using PlAtom::register_ref()
and PlAtom::unregister_ref().
\item The blob's lifetime is controlled by Prolog and its
destructor is invoked when the blob is garbage collected.
Optionally, the predicate that deletes the foreign object
deletes the foreign object and the Prolog garbage collector
only frees the blob.
\end{itemize}
A Prolog blob consists of five parts:
\begin{itemize}
\item A \ctype{PL_blob_t} structure that defines the callbacks.
The PL_BLOB_DEFINITION() macro is typically used to create this,
with the callbacks pointing to methods in the C++ blob.
\item A structure that contains the blob data. This must have
a constructor that references the \ctype{PL_blob_t} structure,
and optionally a virtual destructor. The \const{PL_BLOB_SIZE}
macro is used to define some required methods.
\item A ``create'' or ``open'' predicate that unifies one of its arguments
with a newly created blob that contains the foreign object.
The blob is created using the \op{new} operator (not on the
stack) and managed with \ctype{std::unique_ptr}.
\item (Optionally) a ``close'' predicate that does the opposite of the
``create'' or ``open'' predicate.
\item Predicates that manipulate the foreign object (e.g., for a
file-like object, these could be read, write, seek, etc.).
\end{itemize}
For the \ctype{PL_blob_t} structure, the C++ API provides the
PL_BLOB_DEFINITION(blob_class,blob_name) macro, which references a set
of template functions that allow easily setting up the callbacks. The
C interface allows more flexibility by allowing some of the callbacks
to default; however, the C++ API for blobs provides suitable callbacks
for all of them, using the PL_BLOB_DEFINITION() macro.
For the data, which is subclassed from \ctype{PlBlob}, the programmer
defines the various fields, a constructor that initializes them, and a
destructor. Optionally, override methods can be defined for one of
more of the methods PlBlob::compare_fields(), PlBlob::write_fields(),
PlBlob::save(), PlBlob::load(), PlBlob::pre_delete(). More details on
these are given later.
There is a mismatch between how Prolog does memory management (and
garbage collection) and how C++ does it. In particular, Prolog assumes
that cleanup will be done in the release() callback function
associated with the blob whereas C++ typically does cleanup in a
destructor. The blob interface gets around this mismatch by providing
a default release() callback that assumes that the blob was
created using \const{PL_BLOB_NOCOPY} and manages memory using a
\ctype{std::unique_ptr}.\footnote{This release() function has nothing
to do with std::unique_ptr::release().} More details on this are in
\secref{cpp2-c++-features}.
The C blob interface has a flag that determines how memory is managed:
\const{PL_BLOB_NOCOPY}. The PL_BLOB_DEFINITION() macro sets this, so
Prolog will call the C++ destructor when the blob is garbage
collected. (This call is done indirectly, using a callback that is
registeered with Prolog.)
The C++ API for blobs only supports blobs with
\const{PL_BLOB_NOCOPY}.\footnote{The API can probably also support
blobs with \const{PL_BLOB_UNIQUE}, but there seems to be little
point in setting this flag for non-text blobs.}
\subsubsection{A review of C++ features used by the API}
\label{sec:cpp2-c++-features}
Some slightly obscure features of C++ are used with \ctype{PlBlob} and
\ctype{ContextType}, and can easily cause subtle bugs or memory leaks
if not used carefully.
When a C++ object is created, its memory is allocated (either on the
stack or on the heap using \op{new}), and the constructors are called
in this order:
\begin{itemize}
\item the base class's constructor (possibly specified in
the intialization list)
\item the constructors for all the fields (possibly specified
by an initial value and/or being in the initialization list)
\item the object's constructor.
\end{itemize}
When the object is deleted (either by stack pop or the \op{delete}
operator), the destructors are called in the reverse order.
There are special forms of the constructor for copying, moving, and
assigning. The ``copy constructor'' has a signature \ctype{Type(const
Type\&)} and is used when an object is created by copying, for example
by assignment or passing the object on the stack in a function
call. The ``move constructor'' has the signature \ctype{Type(Type\&\&)}
and is equivalent to the copy constructor for the new object followed
by the destructor for the old object. (Assignment is usually allowed
to default but can also be specified).
Currently, the copy and move constructors are not used, so it is best
to explicitly mark them as not existing:
\begin{code}
Type(const Type&) = delete;
Type(Type&&) = delete;
Type& operator =(const Type&) = delete;
Type& operator =(Type&&) = delete;
\end{code}
A constructor may throw an exception - good programming style is to
not leave a ``half constructed'' object but to throw an
exception. Destructors are not allowed to throw
exceptions,\footnote{because the destructor might be invoked by
another exception, and C++ has no mechanism for dealing with a second
exception.} which complicates the API somewhat.
More details about constructors and destructors can be found in
the FAQs for \href{https://isocpp.org/wiki/faq/ctors}{constructors}
and \href{https://isocpp.org/wiki/faq/dtors}{destructors}.
Many classes or types have a constructor that simply assigns a default
value (e.g., 0 for \ctype{int}) and the destructor does nothing. In
particular, the destructor for a pointer does nothing, which can lead
to memory leaks. To avoid memory leaks, the smart pointer
\ctype{std::unique_ptr}\footnote{The name ``unique'' is to distinguish
this from a ``shared'' pointer. A shared pointer can share ownership
with multiple pointers and the pointed-to object is deleted only when
all pointers to the object have been deleted. A unique pointer allows
only a single pointer, so the pointed-to object is deleted when the
unique pointer is deleted.} can be used, whose destructor deletes its
managed object. Note that \ctype{std::unique_ptr} does not enforce
single ownership; it merely makes single ownership easy to manage and
it detects most common mistakes, for example by not having copy
constructor or assignment operator.
For example, in the following, the implicit destructor for \exam{p}
does nothing, so there will be a memory leak when a \ctype{Ex1} object
is deleted:
\begin{code}
class Ex1 {
public:
Ex1() : p(new int) { }
int *p;
};
\end{code}
To avoid a memory leak, the code could be changed to this:
\begin{code}
class Ex1 {
public:
Ex1() p(new int) { }
~Ex1() { delete p; }
int *p;
};
\end{code}
but it is easier to do the following, where the destructor for
\ctype{std::unique_ptr} will free the memory:
\begin{code}
class Ex1 {
public:
Ex1() p(new int) { }
std::unique_ptr<int> p;
};
\end{code}
The same concept applies to objects that are created in code - if a
C++ object is created using \op{new}, the programmer must manage when
its destructor is called. In the following, if the call to
\exam{data->validate()} fails, there will be a memory leak:
\begin{code}
MyData *foo(int some_value) {
MyData *data = new MyData(...);
data->some_field = some_value;
if (! data->validate() )
throw std::runtime_error("Failed to validate data");
return data;
}
\end{code}
Ths could fixed by adding \exam{delete data} before
throwing the \const{runtime_error}; but this doesn't handle the
situation of \exam{data->validate()} throwing an exception (which
would require a catch/throw).
Instead, it's easiser to use \ctype{std::unique_ptr}, which takes
care of every return or exception path:
\begin{code}
MyData *foo(int some_value) {
std::unique_ptr<MyData> data(new MyData(...));
data->some_field = some_value;
if (! data->validate() )
throw std::runtime_error("Failed to validate data");
return data.release(); // don't delete the new MyData
}
\end{code}
The destructor for \ctype{std::unique_ptr} will delete the data when
it goes out of scope (in this case, by return or throw) unless the
std::unique_ptr::release() method is called.\footnote{The call to
\exam{unique_ptr<MYData>::release}{} doesn't call the destructor;
it can be called using std::unique_ptr::get_deleter().}
In the code above, the \exam{throw} will cause the
\ctype{unique_ptr}'s destructor to be called, which will free the
data; but the data will not be freed in the \exam{return} statement
because of the unique_ptr::release(). Using this style, a pointer to
data on the heap can be managed as easily as data on the stack. The
current C++ API for blobs takes advantage of this - in particular,
there are two methods for unifying a blob:
\begin{itemize}
\item PlTerm::unify_blob(const PlBlob* blob) - does no memory management
\item PlTerm::unify_blob(std::unique_std<PlBlob>* blob) - if
unification fails or raises an error, the memory is automatically freed;
otherwise the memory's ownership is transferred to Prolog, which may
garbage collect the blob by calling the blob's destructor.
Note that this uses a pointer to the pointer, so that
PlTerm::unify_blob() can modify it.
\end{itemize}
\ctype{unique_ptr} allows specifying the delete function. For example,
the following can be used to manage memory created with PL_malloc():
\begin{code}
std::unique_ptr<void, decltype(&PL_free)> ptr(PL_malloc(...), &PL_free);
\end{code}
or, when memory is allocated within a PL_*() function (in this case,
using the Plx_*() wrapper for PL_get_nchars()):
\begin{code}
size_t len;
char *str = nullptr;
Plx_get_nchars(t, &len, &str.get(), BUF_MALLOC|CVT_ALL|CVT_WRITEQ|CVT_VARIABLE|REP_UTF8|CVT_EXCEPTION);
std::unique_ptr<char, decltype(&PL_free)> _str(str, &PL_free);
\end{code}
The current C++ API assumes that the C++ blob is allocated on the
heap. If the programmer wishes to use the stack,
they can use \ctype{std::unique_ptr} to automatically delete the
object if an error is thrown -
PlTerm::unify_blob(std::unique_ptr<PlBlob>*) prevents the automatic
deletion if unification succeeds.
A \ctype{unique_ptr} needs a bit of care when it is passed as an
argument. The unique_ptr::get() method can be used to get the ``raw''
pointer; the \op{delete} must not be used with this pointer.
Or, the unique_ptr::release() method can be used to transfer
ownership without calling the object's destructor.
Using unique_ptr::release() is a bit incovenient, so instead the
\ctype{unique_ptr} can be passed as a pointer (or a reference). This
does not create a new scope, so the pointer must be assigned to a
local variable. For example, the code for unify_blob() is something
like:
\begin{code}
bool PlTerm::unify_blob(std::unique_ptr<PlBlob>* b) const
{ std::unique_ptr<PlBlob> blob(std::move(*b));
if ( !unify_blob(blob.get()) )
return false;
(void)blob.release();
return true;
}
\end{code}
The line declaration for \exam{blob} uses the ``move constructor'' to
set the value of a newly scoped variable (\exam{std::move(*b)} is a
cast, so \ctype{unique_ptr}'s move constructor is used). This has the
same effect as calling \exam{b->reset()}, so from this point on,
\exam{b} has the value \const{nullptr}.
Alternatively, the local \ctype{unique_ptr} could be set by
\begin{code}
std::unique_ptr<PlBlob> blob(b->release());
\end{code}
or
\begin{code}
std::unique_ptr<PlBlob> blob;
blob.swap(*b);
\end{code}
If the call to PlTerm::unify_blob() fails or throws an exception, the
virtual destructor for \exam{blob} is called.
Otherwise, the call to \exam{blob.release()} prevents the destructor
from being called - Prolog now owns the blob object and can call its
destructor when the garbage collector reclaims it.
\subsubsection{How to define a blob using C++}
\label{sec:cpp2-blobs-howto}
TL;DR: Use PL_BLOB_DEFINITION() to define the blob with the flag
\const{PL_BLOB_NOCOPY} and the default \ctype{PlBlob} wrappers; define
your struct as a subclass of \ctype{PlBlob} with no copy constructor,
move constructor, or assignment operator; create a blob using
\exam{std::unique_ptr<PlBlob>(new ...)}, call PlTerm::unify_blob().
Optionally, define one or more of: compare_fields(), write_fields(),
save(), load() methods (these are described after the sample code).
\subsubsection{The life of a PlBlob}
\label{sec:cpp2-blobs-life}
In this section, the blob is of type \ctype{MyBlob}, a subclass
of \ctype{PlBlob}. (Example code is given in \secref{cpp2-blobs-sample-code})
and \secref{cpp2-blobs-sample-code-pointer}.
A blob is typically created by calling a predicate that does
the following:
\begin{itemize}
\item Creates the blob using
\begin{code}
auto ref = std::unique_ptr<PlBlob>(new MyBlob>(...))}
\end{code}
or
\begin{code}
auto ref = std::make_unique<MyBlob>(...);
\end{code}
\item After the fields of the blob are filled in:
\begin{code}
return PlTerm::unify_blob(&ref);
\end{code}
If unification fails or throws an exception, the object is automatically
freed and its destructor is called.
If make_unique() was used to create the pointer, you need to call
PlTerm::unify_blob() as follows, because C++'s type inferencing can't figure
out that this is a covariant type:
\begin{code}
std::unique_ptr<PlBlob> refb(ref.release());
// refb now "owns" the ptr - from here on, ref == nullptr
return A2.unify_blob(&refb);
\end{code}
If unification succeeds, Prolog calls:
\begin{itemize}
\item PlBlobV<MyBlob>acquire(), which calls
\item MyBlob::acquire(), which sets the field \arg{MyBlob::symbol_},
which is usually accessed using the method MyBlob::symbol_term().
If this all succeeds, PlTerm::unify_blob(ref) calls
\exam{ref->release()} to pass ownership of the blob to Prolog
(when the blob is eventually garbage collected, the blob's destructor
will be called).
\end{itemize}
\end{itemize}
At this point, the blob is owned by Prolog and may be freed by
its atom garbage collector, which will call the blob's destructor
(if the blob shouldn't be deleted, it can override the
the PlBlob::pre_delete() method to return \const{false}).
Whenever a predicate is called with the blob as an argument (e.g.,
as \arg{A1}), the blob can be accessed by
\exam{PlBlobv<MyBlob>::cast_check(A1.as_atom())}.
Within a method, the Prolog blob can be accessed as a term (e.g., for
constructing an error term) using the method MyBlob::symbol_term().
This field is initialized by the call to PlTerm::unify_blob(); if
MyBlob::symbol_term() is called before a successful call to
PlTerm::unify_blob(), MyBlob::symbol_term() returns a
\ctype{PlTerm_var}.
When the atom garbage collector runs, it frees the blob by first
calling the release() callback, which does \op{delete}, which calls
the destructor MyBlob::~MyBlob(). Note that C++ destructors are not
supposed to raise exception; they also should not cause a Prolog
error, which could cause deadlock unless the real work is done in
another thread.
Often it is desired to release the resources before the garbage
collector runs. To do this, the programmer can provide a ``close''
predicate that is the inverse of the ``open'' predicate that created
the blob. This typically has the same logic as the destructor, except
that it can raise a Prolog error.
\subsubsection{C++ exceptions and blobs}
\label{sec:cpp2-blobs-exceptions}
When a blob is used in the context of a PREDICATE() macro, it can
raise a C++ exception (\ctype{PlFail} or \ctype{PlException}) and the
PREDICATE() code will convert the exception to the appropriate Prolog
failure or error; memory allocation exceptions are also handled.
Blobs have callbacks, which can run outside the context of a
PREDICATE(). Their exception handling is as follows:
\begin{description}
\cfunction{void}{PlBlob::acquire}{}, which is called from PlBlobV<MyBlob>::acquire(),
can throw a C++ exception. The programmer cannot override this.
\cfunction{int}{PlBlob::compare_fields}{const PlBlob *_b}, which is called from PlBlobV<MyBlob>::compare(),
should not throw an exception. A Prolog error won't work as it uses ``raw
pointers'' and thus a GC or stack shift triggered by creating the
exception will upset the system.
\cfunction{bool}{PlBlob::write_fields}{IOStream *s, int flags}, which is called from PlBlobV<MyBlob>::write(),
can throw an exception, just like code inside a PREDICATE().
In particular, you can wrap calls to Sfprintf() in \cfuncref{PlCheckFail}{},
although the calling context will check for errors on the stream,
so checking the Sfprintf() result isn't necessary.
\cfunction{void}{PlBlob::PlBlob::save}{IOStream *fd} can throw a C++ exception, including PlFail().
\cfunction{PlAtom}{PlBlob::PlBlob::load}{IOSTREAM *fd} can throw a C++ exception, which is converted to
a return value of \const{PlAtom::null}, which is interpreted by
Prolog as failure.
\cfunction{bool}{PlBlob::PlBlob::pre_delete}{}, which is called from PlBLobV<MyBLOB>::release(),
can return \const{false} (or throw a \ctype{PlException} or
\ctype{PlExceptinFailBase}, which will be interpreted as a
return value of \const{false}), resulting in the blob not being
garbage collected, and the destructor not being called. Note
that this doesn't work well with final clean-up atom garbage
collection, which disregards the return value and also doesn't
respect the ordering of blob dependencies (e.g., if an iterator
blob refers to a file-like blob, the file-like blob might be
deleted before the iterator is deleted).
This code runs in the \const{gc} thread.
The only PL_*() function that can safely be called are
PL_unregister_atom() (which is what PlAtom::unregister_ref()
calls).
\end{description}
\subsubsection{Sample PlBlob code (connection to database)}
\label{sec:cpp2-blobs-sample-code}
Here is minimal sample code for creating a blob that owns a connection
to a database. It has a single field (\exam{connection}) and
defines compare_fields() and write_fields().
A second sample code shows how to wrap a system pointer -
\secref{cpp2-blobs-sample-code-pointer}
\begin{code}
struct MyConnection
{ std::string name;
explicit MyConnection();
explicit MyConnection(const std::string& _name);
bool open();
bool close() noexcept;
void portray(PlStream& strm) const;
};
struct MyBlob;
static PL_blob_t my_blob = PL_BLOB_DEFINITION(MyBlob, "my_blob");
struct MyBlob : public PlBlob
{ std::unique_ptr<MyConnection> connection;
explicit MyBlob()
: PlBlob(&my_blob) { }
explicit MyBlob(const std::string& connection_name)
: PlBlob(&my_blob),
connection(std::make_unique<MyConnection>(connection_name))
{ if ( !connection->open() )
throw MyBlobError("my_blob_open_error");
}
PL_BLOB_SIZE
~MyBlob() noexcept
{ if ( !close() )
Sdprintf("***ERROR: Close MyBlob failed: %s\n", name().c_str()); // Can't use PL_warning()
}
inline std::string
name() const
{ return connection ? connection->name : "";
}
bool close() noexcept
{ if ( !connection )
return true;
bool rc = connection->close();
connection.reset(); // Can be omitted, leaving deletion to ~MyBlob()
return rc;
}
PlException MyBlobError(const char* error) const
{ return PlGeneralError(PlCompound(error, PlTermv(symbol_term())));
}
int compare_fields(const PlBlob* _b_data) const override
{ auto b_data = static_cast<const MyBlob*>(_b_data); // See note about cast
return name().compare(b_data->name());
}
bool write_fields(IOSTREAM *s, int flags) const override
{ PlStream strm(s);
strm.printf(",");
return write_fields_only(strm);
}
bool write_fields_only(PlStream& strm) const
{ if ( connection )
connection->portray(strm);
else
strm.printf("closed");
return true;
}
bool portray(PlStream& strm) const
{ strm.printf("MyBlob(");
write_fields_only(strm);
strm.printf(")");
return true;
}
};
// %! create_my_blob(+Name: atom, -MyBlob) is semidet.
PREDICATE(create_my_blob, 2)
{ // Allocating the blob uses std::unique_ptr<MyBlob> so that it'll be
// deleted if an error happens - the auto-deletion is disabled by
// ref.release() inside unify_blob() before returning success.
auto ref = std::unique_ptr<PlBlob>(new MyBlob(A1.as_atom().as_string()));
return A2.unify_blob(&ref);
}
// %! close_my_blob(+MyBlob) is det.
// % Close the connection, silently succeeding if is already
// % closed; throw an exception if something goes wrong.
PREDICATE(close_my_blob, 1)
{ auto ref = PlBlobV<MyBlob>::cast_ex(A1, my_blob);
if ( !ref->close() )
throw ref->MyBlobError("my_blob_close_error");
return true;
}
// %! portray_my_blob(+Stream, +MyBlob) is det.
// % Hook predicate for
// % user:portray(MyBlob) :-
// % blob(MyBlob, my_blob), !,
// % portray_my_blob(current_output, MyBlob).
PREDICATE(portray_my_blob, 2)
{ auto ref = PlBlobV<MyBlob>::cast_ex(A2, my_blob);
PlStream strm(A1, 0);
return ref->portray(strm);
}
\end{code}
\subsubsection{Discussion of the sample PlBlob code}
\label{sec:cpp2-blobs-sample-code-discussion}
\begin{itemize}
\item PL_BLOB_DEFINITION(MyBlob, "my_blob") creates a
\ctype{PL_blob_t} structure with the wrapper functions and flags
set to \const{PL_BLOB_NOCOPY}.
It should be declared outside the \ctype{PlBlob} class and should
not be marked \exam{const} - otherwise, a runtime error can
occur.\footnote{The cause of the runtime error is not clear, but
possibly has to do with the order of initializing globals, which
is unspecified for C++.}
\item The \ctype{MyBlob} struct is a subclass of \ctype{PlBlob}.
See below for a discussion of the default behaviors.
\begin{itemize}
\item \ctype{MyBlob} contains a pointer to a \ctype{MyConnection} object
and keeps a copy of the connection's name. The \ctype{MyConnection}
object is handled by a \ctype{std::unique_ptr} smart pointer, so that
it is automatically freed when the \ctype{MyBlob} object is freed.
\item A default constructor is defined - this is needed for the
load() and save() methods; it invokes the \ctype{PlBlob}
constructor.
\item The \ctype{MyBlob} class must not provide a copy or move
constructor, nor an assignment operator (PlBlob has these as
\op{delete}, so if you try to use one of these, you will
get a compile-time error).
\item \ctype{PlBlob}'s constructor sets \exam{blob_t_} to a pointer
to the \ctype{my_blob} definition. This is used for run-time
consistency checking by the various callback functions and for
constructing error terms (see PlBlob::symbol_term()).
\item \ctype{PlBlob}'s acquire() is called by
PlBlobV<MyBlob>::acquire() and fills in the \exam{symbol_}
field. \ctype{MyBlob} must not override this - it is not a
virtual method.
The \exam{symbol_} field can be accessed by PlBlob::symbol_term().
\item PlBlob::symbol_term() Creates a term from the blob, for use in
error terms. It is always safe to use this; if the symbol
hasn't been set (because acquire() hasn't been called),
symbol_term() returns a ``var'' term - this can be checked
with PlTerm::is_variable().
\item The MyBlob(connection_name) constructor creates a
\ctype{MyConnection} object. If this fails, an exception is
thrown. The constructor then calls MyConnection::open() and
throws an exception if that fails. (The code would be similar if
instead the constructor for \ctype{MyConnection} also did an
open and threw an exception on failure.)
\item The \const{PL_BLOB_SIZE} is boilerplate that defines a
blob_size_() method that is used when the blob is created.
\item The destructor ~MyBlob() is called when the blob is released
by the garbage collector and in turn calls the MyBlob::close(),
throwing away the result. If there is an error, a message is
printed because there is no other way report the error. For this
reason, it is preferred that the program explicitly calls the
close_my_blob/1 predicate, which can raise an error. One way of
doing this is by using the at_halt/1 hook.
\item The MyBlob::close() method is called by either the destructor
or by the close_my_blob/1 predicate. Because it can be called by
the garbage collector, which does not provide the usual
environment and which may also be in a different thread, the
only Prolog function that can be called is
PlAtom::unregister_ref(); and the MyBlob::close() method must
not throw an exception.\footnote{It isn't enough to just catch
exceptions; for example, if the code throws \exam{PlUnknownError("...")},
that will try to create a Prolog term,
which will crash because the environment for creating terms is
not available.} Because there is no mechanism for reporting an
error, the destructor prints a message on failure (calling
PL_warning() would cause a crash).
PlBlob::close() calls MyConnection::close() and then frees the
object. Error handling is left to the caller because of the
possibility that this is called in the context of garbage
collection. It is not necessary to free the \ctype{MyConnection}
object here - if it is not freed, the
\ctype{std::unique_ptr<MyConnection>}'s destructor would free
it.
\item PlBlob::MyBlobError() is a convenience method for creating
errror terms.
\item PlBlob::compare_fields() makes the blob comparison function
more deterministic by comparing the name fields; if the names
are the same, the comparison will be done by comparing the
addresses of the blobs (which is the default behavior for blobs
defined using the C API).
PlBlob::compare_fields() is called by
PlBlobV<PlBlob>::compare(), which provides the default
comparison if PlBlob::compare_fields() returns \const{0}
(``equal'').
The \arg{_b_data} argument is of type \ctype{const PlBlob*} -
this is cast to \ctype{const MyBlob*} using a
\const{static_cast}. This is safe because Prolog guarantees
that PlBlobV<PlBlob>::compare() will only be called if both
blobs are of the same type.
\item PlBlob::write_fields() outputs the name and the status of the
connection, in addition to the default of outputting the blob
type and its address. This is for illustrative purposes only; an
alternative is to have a my_blob_properties/2 predicate to
provide the information.
The \arg{flags} argument is the same as given to
PlBlobV<PlBlob>::write(), which is a bitwise \emph{or} of zero
or more of the \const{PL_WRT_*} flags that were passed in to the
caling PL_write_term() (defined in \file{SWI-Prolog.h}). The
\arg{flags} do not have the \const{PL_WRT_NEWLINE} bit set, so
it is safe to call PlTerm::write() and there is no need for
writing a trailing newline.
If anything in PlBlob::write_fields() throws a C++ exception, it
will be caught by the calling PlBlobV<PlBlob>::write() and
handled appropriately.
\item PlBlob::save() and PlBlob::load() are not defined, so the
defaults are used - they throw an error on an attempt to save
the blob (e.g., by using qsave_program/[1,2]).\footnote{The C
API defaults would save the internal form of the blob, which is
probably not what you want, so the C++ API throws an error as
its default.}
\end{itemize}
\item create_my_blob/2 predicate:
\begin{itemize}
\item \exam{std::unique_ptr<PlBlob>()} creates a MyBlob that is
deleted when it goes out of scope. If an exception occurs
between the creation of the blob or if the call to unify_blob()
fails, the pointer will be automatically freed (and the
\ctype{MyBlob} destructor will be called).
PlTerm::unify_blob() is called with a pointer to a
\ctype{std::unique_ptr}, which takes ownership of the object by
calling std::unique_ptr<PlBlob>::release() and passes the
pointer to Prolog, which then owns it. This also sets \arg{ref}
to \const{nullptr}, so any attempt to use \arg{ref} after a call
to PlTerm::unify_blob() will be an error.
If you wish to create a \ctype{MyBlob} object instead of a
\ctype{PlBlob} object, a slightly different form is used:
\begin{code}
auto ref = std::make_unique<MyBlob>(...);
...
std::unique_ptr<PlBlob> refb(ref.release());
PlCheckFail(A2.unify_blob(&refb));
return true;
\end{code}
\end{itemize}
\item close_my_blob/1 predicate:
\begin{itemize}
\item The argument is turned into a \ctype{MyBlob} pointer using the
PlBlobV<MyBlob>::cast_ex() function, which will throw a
\except{type_error} if the argument isn't a blob of the expected
type.
\item The MyBlob::close() method is called - if it fails,
a Prolog error is thrown.
\end{itemize}
\end{itemize}
\subsubsection{Sample PlBlob code (wrapping a pointer)}
\label{sec:cpp2-blobs-sample-code-pointer}
\begin{code}
struct MyFileBlob;
static PL_blob_t my_file_blob = PL_BLOB_DEFINITION(MyFileBlob, "my_file_blob");
static const PlOptionsFlag<int>
MyFileBlob_options("MyFileBlob-options",
{ {"absolute", PL_FILE_ABSOLUTE},
{"ospath", PL_FILE_OSPATH},
{"search", PL_FILE_SEARCH},
{"exist", PL_FILE_EXIST},
{"read", PL_FILE_READ},
{"write", PL_FILE_WRITE},
{"execute", PL_FILE_EXECUTE},
{"noerrors", PL_FILE_NOERRORS} });
struct MyFileBlob : public PlBlob
{ std::FILE* file_;
std::string mode_;
int flags_;
std::string filename_;
std::vector<char> buffer_; // used by read(), to avoid re-allocation
explicit MyFileBlob()
: PlBlob(&my_file_blob) { }
explicit MyFileBlob(PlTerm filename, PlTerm mode, PlTerm flags)
: PlBlob(&my_file_blob),
mode_(mode.as_string())
{ flags_ = MyFileBlob_options.lookup_list(flags);
filename_ = filename.get_file_name(flags_);
file_ = fopen(filename_.c_str(), mode_.c_str());
if ( !file_ ) // TODO: get error code (might not be existence error)
throw PlExistenceError("my_file_blob_open", PlTerm_string(filename_));
// for debugging:
// PlTerm_string(filename.as_string() + "\" => \"" +
// filename_ + "\", \"" + mode_ +
// ", flags=" + MyFileBlob_options.as_string(flags_) + "\")")
}
PL_BLOB_SIZE
std::string read(size_t count)
{ assert(sizeof buffer_[0] == sizeof (char));
assert(sizeof (char) == 1);
buffer_.reserve(count);
return std::string(buffer_.data(),
std::fread(buffer_.data(), sizeof buffer_[0], count, file_));
}
bool eof() const
{ return std::feof(file_);
}
bool error() const
{ return std::ferror(file_);
}
virtual ~MyFileBlob() noexcept
{ if ( !close() )
// Can't use PL_warning()
Sdprintf("***ERROR: Close MyFileBlob failed: (%s)\n", filename_.c_str());
}
bool close() noexcept
{ if ( !file_ )
return true;
int rc = std::fclose(file_);
file_ = nullptr;
return rc == 0;
}
PlException MyFileBlobError(const std::string error) const
{ return PlGeneralError(PlCompound(error, PlTermv(symbol_term())));
}
int compare_fields(const PlBlob* _b_data) const override
{ // dynamic_cast is safer than static_cast, but slower (see documentation)
// It's used here for testing (the documentation has static_cast)
auto b_data = dynamic_cast<const MyFileBlob*>(_b_data);
return filename_.compare(b_data->filename_);
}
bool write_fields(IOSTREAM *s, int flags) const override
{ PlStream strm(s);
strm.printf(",");
return write_fields_only(strm);
}
bool write_fields_only(PlStream& strm) const
{ // For debugging:
// strm.printf("%s mode=%s flags=%s", filename_.c_str(), mode_.c_str(),
// MyFileBlob_options.as_string(flags_).c_str());
strm.printf("%s", filename_.c_str());
if ( !file_ )
strm.printf("-CLOSED");
return true;
}
bool portray(PlStream& strm) const
{ strm.printf("MyFileBlob(");
write_fields_only(strm);
strm.printf(")");
return true;
}
};
PREDICATE(my_file_open, 4)
{ auto ref = std::unique_ptr<PlBlob>(new MyFileBlob(A2, A3, A4));
return A1.unify_blob(&ref);
}
PREDICATE(my_file_close, 1)
{ auto ref = PlBlobV<MyFileBlob>::cast_ex(A1, my_file_blob);
if ( !ref->close() ) // TODO: get the error code
throw ref->MyFileBlobError("my_file_blob_close_error");
return true;
}
\end{code}
\subsubsection{Discussion of the sample PlBlob code (wrapping a pointer)}
\label{sec:cpp2-blobs-sample-code-pointer-discussion}
\begin{itemize}
\item This code provides a simple wrapper for some of the C ``stdio''
functions defined in \file{<cstdio>}. The blob wraps the file
pointer returned from fopen() and also keeps a few other values
for debugging (the mode, flags, filename from the call to fopen())
plus a buffer for read operations.
\item A utility class `PlOptionsFlag` is defined in file{SWI-cpp2-flags.h},
for mapping a list of atoms to a bit-field flag. For example, the
list \exam{[search,read]} would map to `exam{PL_FILE_SEARCH|PL_FILE_READ}`.
\item The \ctype{MyFileBlob} struct defines the blob that wraps a
\ctype{FILE*}. The constructor (which is called by predicate
my_file_open/4) converts the \arg{flags} term (a list of atoms or
strings) to a flag that is passed to PL_get_file_name(), to convert
the \arg{filename} to a string containing the abslute file
name. This is then passed to fopen(), together with the
\arg{mode}. If the call to fopen() fails, a C++ exception is thrown,
to be handled by Prolog. Other errors, such as a wrong argument type
to PL_get_file_name() can also cause an exception.
\item MyFileBlob::read() ensures that the buffer is big enough and
then calls `fread()` to return the buffer's contents.
\item MyFileBlob::eof() and MyFileBlob::error() call feof()
and ferror() respectively. They can be used to check the status
of the call to MyFileBlob::read().
\item The destructor calls MyFileBlob::close() and outputs a warning
if it fails - a destructor is not allowed to throw a C++ exception,
so this is the best we can do; it's better if the programmer explicitly
closes the file rather than depending on the garbage collector to
free the blob.
\item MyFileBlob::close() calls fclose(). It then sets the \ctype{FILE*}
to null, so that close won't be done twice.
\item MyFileBlob::compare_fields(), MyFileBlob::write_fields(),
MyFileBlob::write_fields_only(), MyFileBlob::portray() are similar
to the same methods in \ctype{MyBlob} in \secref{cpp2-blobs-sample-code}.
\item Predicate my_file_open(File,Filename,Mode,Flags) calls the
\ctype{MyFileBlob} constructor with \arg{Filename}, \arg{Mode},
\arg{flags} and unifies the blob with \arg{File}.
\item Predicate my_file_close/1 calls MyFileBlob::close(), checks for
an error and creates a Prolog error if the close failed.
\end{itemize}
\subsubsection{Identifying blobs by atoms}
\label{sec:cpp2-atom-blob}
Passing a Prolog blob around can be inconvenient; it is easier if a
blob can be identified an atom. An example of this is with streams,
which are identified by atoms such as \exam{user_input}.
A utility class \ctype{AtomMap} is provided for this situation.
See \secref{cpp2-atom-map}.
\subsection{Limitations of the interface}
\label{sec:cpp2-limitations}
The C++ API remains a work in progress.
\subsubsection{Strings}
\label{sec:cpp2-strings}
SWI-Prolog string handling has evolved over time. The functions that
create atoms or strings using \ctype{char*} or \ctype{wchar_t*} are
``old school''; similarly with functions that get the string as
\ctype{char*} or \ctype{wchar_t*}. The PL_{get,unify,put}_[nw]chars()
family is more friendly when it comes to different input, output,
encoding and exception handling.
Roughly, the modern API is PL_get_nchars(), PL_unify_chars() and
PL_put_chars() on terms. There is only half of the API for atoms as
PL_new_atom_mbchars() and PL-atom_mbchars(), which take an encoding,
length and char*.
For return values, \ctype{char*} is dangerous because it can point to
local or stack memory. For this reason, wherever possible, the C++ API
returns a \ctype{std::string}, which contains a copy of the
string. This can be slightly less efficient that returning a
\ctype{char*}, but it avoids some subtle and pervasive bugs that even
address sanitizers can't detect.\footnote{If we wish to minimize the
overhead of passing strings, this can be done by passing in a pointer
to a string rather than returning a string value; but this is more
cumbersome and modern compilers can often optimize the code to avoid
copying the return value.}
Some functions require allocating string space using PL_STRINGS_MARK().
The \ctype{PlStringBuffers} class provides a \jargon{RAII} wrapper that
ensures the matching PL_STRINGS_RELEASE() is done.
The \ctype{PlAtom} or \ctype{PlTerm} member functions that need
the string buffer use \ctype{PlStringBuffers}, and then copy the
resulting string to a \ctype{std::string} value.
The C++ API has functions such as PlTerm::get_nchars() that use
\ctype{PlStringBuffers} and then copy the result to a
\ctype{std::string} result, so the programmer often doesn't need
to use \ctype{PlStringBuffers}.
\begin{description}
\classitem{PlStringBuffers}
A \jargon{RAII} wrapper for allocating a string that is created using
\const{BUF_STACK}. This isn't needed if you use a method such as
PlTerm::as_string(), but is needed for calling certain PL_*() or
Plx_*() wrapped functions.
The constructor calls PL_STRINGS_MARK() and
the destructor calls PL_STRINGS_RELEASE(). Here is an example of its
use, for writing an atom to a stream, using Plx_atom_wchars(), which
must be called within a strings buffer:
\begin{code}
PREDICATE(w_atom_cpp, 2)
{ auto stream(A1), term(A2);
PlStream strm(stream, STIO_OUTPUT);
PlStringBuffers _string_buffers;
const pl_wchar_t *sa = Plx_atom_wchars(term.as_atom().unwrap(), nullptr);
strm.printfX("/%Ws/", sa);
return true;
}
\end{code}
\end{description}
\subsubsection{Stream I/O}
\label{sec:cpp2-stream-io}
\ctype{PlStream} can be used to get a stream from a Prolog term, or to
lock the stream so that other threads cannot interleave their
output. With either usage, \ctype{PlStream} is a \jargon{RAII} class
that ensure the matchin PL_release_stream() is done, and also handles
some subtle problems with C++ exceptions.
The methods are:
\begin{description}
\constructor{PlStream}{term_t t, int flags} - see
PL_get_stream() for documentation of the flags.
Throws a C++ exception on error.
\constructor{PlStream}{IOSTREAM *s} - calls
PL_acquire_stream() to lock the stream.
Throws a C++ exception on error.
\destructor{PlStream} - calls PlStream::release().
See below for caveats if there are exceptions.
\cfunction{void}{PlStream::release}{} calls PL_release_stream(),
throwing an exception if there has been an I/O error on
the stream, and sets the \ctype{PlStream} object to an
invalid stream (see PlStream::check_stream()).
\cppcast{IOSTREAM*}{PlStream} - when used in a context
that requires an \ctype{IOSTREAM*}, \ctype{PlStream}
is implicitly converted to \ctype{IOSTREAM*}.
\cfunction{void}{check_stream}{} checks that the
\ctype{PlStream} object contains a valid stream and
throws an exception if it doesn't. This is used to
ensure that PlStream::release() hasn't been called.
\end{description}
Most of the stream I/O functions have corresponding methods
in \ctype{PlStream}. For example, Sfprintf() corresponds to
PlStream::printf(). PlStream::seek() and PlStream::tell() call
Sseek64() and Stell64() instead of \ctype{long} (they are also
deprecated: PlStream::seek64() and PlStream::tell64() are
preferred).
The C interface to stream I/O doesn't raise a Prolog error when
there's a stream error (typically indicated by a -1 return
code). Instead, the error sets a flag on the stream and
PL_release_stream() creates the error term. The
\ctype{PlStream} destructor calls PL_release_stream(); but
it's a fatal error in C++ to raise an exception in a destructor if the
destructor is invoked by stack-unwinding due to another exception,
including the pseudo-exceptions \ctype{PlFail} and
\ctype{PlExceptionFail}.
To get around this, the various stream I/O functions have wrapper
methods in the \ctype{PlStream} class that check for an error
and call PlStream::release() to create the Prolog error, which
is thrown as a C++ error.
The destructor calls PlStream::release(), which throws a C++
exception if there is a stream error. This is outside the destructor,
so it is safe - the destructor checks if the stream has been released
and does nothing in that situation.
The following two code examples do essentially the same thing:
\begin{code}
PREDICATE(name_arity, 1)
{ PlStream strm(Scurrent_output);
strm.printf("name = %s, arity = %zd\n", A1.name().as_string().c_str(), A1.arity());
return true;
}
\end{code}
\begin{code}
PREDICATE(name_arity, 1)
{ PlStream strm(Scurrent_output);
try
{ strm.printf("name = %s, arity = %zd\n", A1.name().as_string().c_str(), A1.arity());
} PREDICATE_CATCH({strm.release(); return false;})
return true;
}
\end{code}
If you write the code as follows, using Sfprintf() directly, it is
possible that a fatal exception will be raised on an I/O error:
\begin{code}
PREDICATE(name_arity, 1)
{ PlStream strm(Scurrent_output);
Sfprintf(strm, "name = %s, arity = %zd\n", A1.name().as_string().c_str(), A1.arity());
return true;
// WARNING: the PlStream destructor might throw a C++
// exception on stack unwinding, giving a fatal
// fatal runtime exception.
}
\end{code}
If you don't use these, and want to throw an exception if there's an
error, the following code works because \ctype{PlStream}
(and the underlying PL_acquire_stream()) can be called recursively:
\begin{code}
{ PlStream strm(...);
strm.release();
}
\end{code}
\subsubsection{Object handles}
\label{sec:cpp2-limitations-handles}
Many of the ``opaque object handles'', such as \ctype{atom_t},
\ctype{term_t}, and \ctype{functor_t} are integers.\footnote{Typically
\ctype{uintptr_t} values, which the C standard defines as
``an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer.''}
As such, there is no compile-time detection of passing the
wrong handle to a function.
This leads to a problem with classes such as \ctype{PlTerm} -
C++ overloading cannot be used to distinguish, for example, creating
a term from an atom versus creating a term from an integer.
There are a number of possible solutions, including:
\begin{itemize}
\item A subclass for each kind of initializer;
\item A tag for each kind of intializer;
\item Change the C code to use a \ctype{struct}
instead of an integer.
\end{itemize}
It is impractical to change the C code, both because of the
amount of edits that would be required and also because of
the possibility that the changes would inhibit some optimizations.
There isn't much difference between subclasses versus tags; but
as a matter of design, it's better to specify things as constants
than as (theoretically) variables, so the decision was to use
subclasses.
\subsection{Linking embedded applications using swipl-ld} \label{sec:cpp2-plld}
The utility program \program{swipl-ld} (Win32: swipl-ld.exe) works with
both C and C++ programs. See
\href{https://www.swi-prolog.org/pldoc/man?section=plld}{Linking embedded applications using swipl-ld}
for more details.
Your C++ compiler should support at least C++-17.
To avoid incompatibilities amongst the various C++ compilers' ABIs,
the object file from compiling \file{SWI-cpp2.cpp} is not included in
the shared object \file{libswipl}; instead, it must be compiled along
with any foreign predicate files. If the macro
\const{_SWI_CPP2_CPP_SEPARATE} is defined before the include for
\file{SWI-cpp2.h}, then \file{SWI-cpp2.cpp} is not automatically
included and must be compiled separately - either by creating a
\file{.a} file or by adding a \verb$#include <SWI-cpp2.cpp>$ to one of
your source files.
\section{Examples}
\label{sec:cpp2-examples}
Before going into a detailed description of the C++ classes we present
a few examples illustrating the ``feel'' of the interface.
\subsection{Hello(World)}
\label{sec:cpp2-hello-world}
This simple example shows the basic definition of the predicate hello/1
and how a Prolog argument is converted to C-data:
\begin{code}
PREDICATE(hello, 1)
{ cout << "Hello " << A1.as_string() << endl;
return true;
}
\end{code}
The arguments to PREDICATE() are the name and arity of the predicate.
The macros A<n> provide access to the predicate arguments by position
and are of the type \ctype{PlTerm}. The C or C++ string for a \ctype{PlTerm}
can be extracted using as_string(), or as_wstring() methods;\footnote{The C-string
values can be extracted from \ctype{std::string} by using c_str(), but you
must be careful to not return a pointer to a local/stack value, so this
isn't recommende.}
and similar access methods provide an easy type-conversion
for most Prolog data-types, using the output of write/1 otherwise:
\begin{code}
?- hello(world).
Hello world
Yes
?- hello(X)
Hello _G170
X = _G170
\end{code}
\subsection{Adding numbers}
\label{sec:cpp2-ex-adding-numbers}
This example shows arithmetic using the C++ interface, including
unification, type-checking, and conversion. The predicate add/3 adds
the two first arguments and unifies the last with the result.
\begin{code}
PREDICATE(add, 3)
{ return A3.unify_integer(A1.as_long() + A2.as_long());
}
\end{code}
You can use your own variable names instead of \exam{A1},
\exam{A2}, etc.:
\begin{code}
PREDICATE(add, 3) // add(+X, +Y, +Result)
{ PlTerm x(A1);
PlTerm y(A2);
PlTerm result(A3);
return result.unify_integer(x.as_long() + y.as_long());
}
\end{code}
or more compactly:
\begin{code}
PREDICATE(add, 3) // add(+X, +Y, +Result)
{ auto x = A1, y = A2, result = A3;
return result.unify_integer(x.as_long() + y.as_long());
}
\end{code}
The as_long() method for a \ctype{PlTerm} performs a PL_get_long_ex()
and throws a C++ exception if the Prolog argument is not a Prolog
integer or float that can be converted without loss to a
\ctype{long}. The unify_integer() method of \ctype{PlTerm} is defined
to perform unification and returns \const{true} or \const{false}
depending on the result.
\begin{code}
?- add(1, 2, X).
X = 3.
?- add(a, 2, X).
[ERROR: Type error: `integer' expected, found `a']
Exception: ( 7) add(a, 2, _G197) ?
\end{code}
\subsection{Average of solutions - calling a Prolog goal}
\label{sec:cpp2-ex-average}
This example is a bit harder. The predicate average/3 is defined to take
the template \mbox{average(+Var, :Goal, -Average)}, where \arg{Goal}
binds \arg{Var} and will unify \arg{Average} with average of the
(integer) results.
\ctype{PlQuery} takes the name of a predicate and the goal-argument
vector as arguments. From this information it deduces the arity and
locates the predicate. The method PlQuery::next_solution() yields
\const{true} if there was a solution and \const{false} otherwise. If
the goal yields a Prolog exception, it is mapped into a C++ exception.
A return to Prolog does an implicit ``cut'' (PL_cut_query()); this
can also be done explicitly by the PlQuery::cut() method.
\begin{code}
PREDICATE(average, 3) /* average(+Templ, :Goal, -Average) */
{ long sum = 0;
long n = 0;
PlQuery q("call", PlTermv(A2));
while( q.next_solution() )
{ sum += A1.as_long();
n++;
}
return A3.unify_float(double(sum) / double(n));
}
\end{code}
\begin{code}
?- [user].
|: p(1).
|: p(10).
|: p(20).
|:
% user://1 compiled 0.00 sec, 3 clauses
true.
?- average(X, p(X), Average).
Average = 10.333333333333334.
\end{code}
\section{Rationale for changes from version 1}
\label{sec:cpp2-rationale}
\subsection{Implicit constructors and conversion operators}
\label{sec:cpp2-rationale-ctors}
The original version of the C++ interface heavily used implicit
constructors and conversion operators. This allowed, for example:
\begin{code}
PREDICATE(hello, 1)
{ cout << "Hello " << (char *)A1 << endl; // Deprecated
return true;
}
PREDICATE(add, 3)
{ return A3 = (long)A1 + (long)A2; // Deprecated
}
\end{code}
Version 2 is a bit more verbose:
\begin{code}
PREDICATE(hello, 1)
{ cout << "Hello " << A1.as_string() << endl;
return true;
}
PREDICATE(add, 3)
{ return A3.unify_int(A1.as_long() + A2.as_long());
}
\end{code}
There are a few reasons for this:
\begin{itemize}
\item
The implicit constructors and conversion operators, combined with
the C++ conversion rules for integers and floats, could sometimes
lead to subtle bugs that were difficult to find -- in one case, a
typo resulted in terms being unified with floating point values when
the code intended them to be atoms. This was mainly because the
underlying C types for terms, atoms, etc. are unsigned integers,
leading to confusion between numeric values and Prolog terms and
atoms.
\item
The overloaded assignment operator for unification changed the
usual C++ semantics for assignments from returning a reference
to the left-hand-side to returning a \ctype{bool}. In addition,
the result of unification should always be checked (e.g., an
``always succeed'' unification could fail due to an out-of-memory
error); the unify_XXX() methods return
a \ctype{bool} and they can be wrapped inside a \cfuncref{PlCheckFail}{}
to raise an exception on unification failure.
\item
The C-style of casts is deprecated in C++, so the expression
\exam{(char*)A1} becomes the more verbose
\exam{static_cast<std::string>(A1)}, which is longer than
\exam{A1.as_string()}. Also, the string casts don't allow for
specifying encoding.
\item
The implicit constructors and conversion operators were attractive because they allowed
directly calling the foreign language interface functions, for example:
\begin{code}
PlTerm t;
Pl_put_atom_chars(t, "someName");
\end{code}
whereas this is now required:
\begin{code}
PlTerm t;
Pl_put_atom_chars(t.as_term_t(), "someName");
\end{code}
However, this is mostly avoided by methods and constructors that
wrap the foreign language functions:
\begin{code}
PlTerm_atom t("someName");
\end{code}
or
\begin{code}
auto t = PlTerm_atom("someName");
\end{code}
Additionally, there are now wrappers for most of the PL_*() functions
that check the error return and throw a C++ exception as appropriate.
\end{itemize}
Over time, it is expected that some of these restrictions will be
eased, to allow a more compact coding style that was the intent of the
original API. However, too much use of overloaded
methods/constructors, implicit conversions and constructors can result
in code that's difficult to understand, so a balance needs to be
struck between compactness of code and understandability.
For backwards compatibility, much of the version 1 interface is still
available (except for the implicit constructors and operators), but
marked as ``deprecated''; code that depends on the parts that have been
removed can be easily changed to use the new interface.
\subsection{Strings}
\label{sec:cpp2-rationale-strings}
The version API often used \ctype{char*} for both setting and setting
string values. This is not a problem for setting (although encodings
can be an issue), but can introduce subtle bugs in the lifetimes of
pointers if the buffer stack isn't used
properly. \ctype{PlStringBuffers} makes the buffer stack easier to
use, but it would be preferable to avoid its use altogether. C++,
unlike C, has a standard string that allows easily keeping a copy
rather than dealing with a pointer that might become invalid. (Also,
C++ strings can contain null characters.)
C++ has default conversion operators from \ctype{char*} to
\ctype{std::string}, so some of the API support only
\ctype{std::string}, even though this can cause a small
inefficiency. If this proves to be a problem, additional overloaded
functions and methods can be provided in future (note that some
compilers have optimizations that reduce the overheads of using
\ctype{std::string}); but for performance-critical code, the C
functions can still be used.
There still remains the problems of Unicode and encodings.
\ctype{std::wstring} is one way of dealing with this. And for
interfaces that use \ctype{std::string}, an encoding can be
specified.\footnote{As of 2023-04, this had only been partially
implemented}. Some of the details for this - such as the
default encoding - may change slightly in the future.
\section{Porting from version 1 to version 2}
\label{sec:cpp2-porting-1-2}
The easiest way of porting from \file{SWI-cpp.h} to \file{SWI-cpp2.h}
is to change the \exam{\#include "SWI-cpp.h"} to \exam{\#include "SWI-cpp2.h"}
and look at the warning and error messages. Where possible, version 2
keeps old interfaces with a ``deprecated'' flag if there is a better way
of doing things with version 2.
For convenience when calling PL_*() functions, the Plx_*() wrapper
functions add error checking. Also, most of the PL_*() functions that
work with \ctype{term_t}, \ctype{atom_t}, etc. have corresponding
methods in \ctype{PlTerm}, \ctype{PlAtom}, etc.
Here is a list of typical changes:
\begin{itemize}
\item
Replace PlTerm() constructor with
PlTerm_var() for uninstantiated variables,
\cfuncref{PlTerm_atom}{a} for atoms, \cfuncref{PlTerm_term_t}{t}
for the raw \ctype{term_t}, \cfuncref{PlTerm_integer}{i},
\cfuncref{PlTerm_float}{v}, or \cfuncref{PlTerm_pointer}{p}.
\item
Examine uses of \ctype{char*} or \ctype{wchar_t} and replace them by
\ctype{std::string} or \ctype{std::wstring} if appropriate.
For example, \exam{cout << "Hello " << (char*)A1 << endl}
can be replaced by \exam{cout << "Hello " << A1.as_string() << endl}.
In general, \ctype{std::string} is safer than \ctype{char*} because
the latter can potentially point to freed memory.
\item
Instead of returning \const{false} from a predicate for failure,
you can do \exam{throw PlFail()}. This mechanism is also used by
\cfuncref{PlCheckFail}{rc}. Note that throwing an exception is
slower than returning \const{false}, so
performance-critical code should avoid \cfuncref{PlCheckFail}{rc}
if failure is expected to happen often.
\item
You can use the \cfuncref{PlEx}{rc} to check the return code
from a function in \file{SWI-Prolog} and throw a \ctype{PlFail}
exception to short-circuit execution and return failure (\const{false})
to Prolog (or throw a \ctype{PlException} if there was a Prolog error.
\item
\exam{PlAtom::handle} has been replaced by \exam{PlAtom::C_},
which should be accessed by PlAtom::unwrap().
\item
\exam{PlTerm::ref} has been replaced by \exam{PlTerm::C_},
which should be accessed by PlTerm::unwrap().
\item
\exam{PlFunctor::functor} has been replaced by \exam{PlFunctor::C_},
which should be accessed by PlFunctor::unwrap().
\item
The operator \exam{=} for unification has been deprecated,
replaced by various unify_*() methods
(\cfuncref{PlTerm::unify_term}{t2},
\cfuncref{PlTerm::unify_atom}{a}, etc.).
\item
The various ``cast'' operators have been deprecated or deleted;
you should use the various ``getter'' methods. For example,
\exam{static_cast<char*>(t)} is replaced by \exam{t.as_string().c_str()}
(and you should prefer \exam{t.as_striong()};
\exam{static_cast<int32_t>(t)} is replaced by \exam{t.as_int32_t()}, etc.
\item
It is recommended that you do not use \ctype{int} or
\ctype{long} because of problems porting between Unix and Windows
platforms; instead, use \ctype{int32_t}, \ctype{int64_t},
\ctype{uint32_t}, \ctype{uint64_t}, etc.
\end{itemize}
\section{The class PlFail}
\label{sec:cpp2-plfail}
The \ctype{PlFail} class is used for short-circuiting a function when
failure or an exception occurs and any errors will be handled in the
code generated by the PREDICATE() macro. See also
\secref{cpp2-exceptions-notes}).
For example, this code, using the C API:
\begin{code}
PREDICATE(unify_zero, 1)
{ if ( !PL_unify_integer(A1.unwrap(), 0) )
return false; // could be an error or failure
Sprintf("It's zero!\n");
return true;
}
\end{code}
can instead be written this way, using the C++ API:
\begin{code}
PREDICATE(unify_zero, 1)
{ PlCheckFail(A1.unify_integer(0));
Sprintf("It's zero!\n");
return true;
}
\end{code}
Using \exam{throw PlFail()} in performance-critical code can cause a
signficant slowdown. A simple benchmark showed a 15x to 20x slowdown
using \exam{throw PlFail()} compared to \exam{return false} (comparing
the first code sample above with the second and third samples; the
speed difference seems to have been because in the second sample, the
compiler did a better job of inlining). However, for most code, this
difference will be barely noticeable. And if the code usually succeeds,
there is no significant difference.
There was no significant performance difference between the C++
version and this C version:
\begin{code}
static foreign_t
unify_zero(term_t a1)
{ return PL_unify_integer(a1, 0);
}
\end{code}
\subsection{PlCheckFail(), and PlEx() convenience functions}
\label{sec:cpp2-plcheck}
If one of the C PL_*() functions in \file{SWI-Prolog.h} returns
failure, this can be either a Prolog-style failure (e.g. from
PL_unify() or PL_next_solution()) or an error. If the failure is due
to an error, it's usually best to immediately return to Prolog - and
this can be done with the PlEx() function, which turns
a Prolog error into a C++ \ctype{PlException}. \cfuncref{PlCheckFail}{}
calls PlEx() and additionally throws PlFail() if the failure is
for Prolog failure.
PlEx() calls PL_exception() to see if there is a
Prolog exception; if so, the Prolog exception is converted to a
\ctype{PlException} object, which is then thrown. For more details on
the C++ exceptions, see \secref{cpp2-exceptions}.
\begin{description}
\cfunction{void}{PlCheckFail}{bool rc}
If \arg{rc} is \const{false}, throw \ctype{PlFail} to return control
to Prolog with failure.
\cfunction{C_t}{PlWrap}{C_t rc, qid_t qid = 0}
If \arg{rc} indicates failure or an error, check for an error and throw
a \ctype{PlException} if there was one; otherwise, return the \arg{rc}.
\cfunction{void}{PlEx}{C_t rc, qid_t qid = 0}
If \arg{rc} is ``false'' (non-zero), throw \ctype{PlFail} to return control
to Prolog with failure.
This is the same as PlCheckFail() except it can also specify a
\ctype{qid_t} query ID.
\end{description}
\section{Overview of accessing and changing values}
\label{sec:cpp2-plterm-get-put-unify}
The \file{SWI-Prolog.h} header provides various functions for
accessing, setting, and unifying terms, atoms and other types.
Typically, these functions return a \const{0} (\const{false}) or
\const{1} (\const{true}) value for whether they succeeded or not. For
failure, there might also be an exception created - this can be tested
by calling PL_excpetion(0).
There are three major groups of methods:
\begin{itemize}
\item Put (set) a value, corresponding to the PL_put_*() functions.
\item Get a value, corresponding to the PL_get_*() and PL_get_*_ex() functions.
\item Unify a value, corresponding to the PL_unify_*() and PL_unify_*_ex() functions.
\end{itemize}
The ``put'' operations are typically done on an uninstantiated term (see
the PlTerm_var() constructor). These are expected to succeed, and
typically raise an exception failure (e.g., resource exception) - for
details, see the corresponding PL_put_*() functions in
\href{https://www.swi-prolog.org/pldoc/man?section=foreign-term-construct}{Constructing
Terms}.
For the ``get'' and ``unify'' operations, there are three possible failures:
\begin{itemize}
\item \const{false} return code
\item unification failure
\item exception (value of unexpected type or out of resources)
\end{itemize}
Each of these is communicated to Prolog by returning \const{false}
from the top level; exceptions also set a ``global'' exception term
(using PL_raise_exception()). The C++ programmer usually doesn't have
to worry about this; instead they can \exam{throw PlFail()} for
failure or \exam{throw PlException()} (or one of \ctype{PlException}'s
subclasses) and the C++ API will take care of everything.
\subsection{Converting PlTerm to native C and C++ types}
\label{sec:cpp2-plterm-casting}
These are \emph{deprecated} and replaced by the various \exam{as_*()} methods.
\ctype{PlTerm} can be converted to the following types:
\begin{description}
\cppcast{PlTerm}{term_t}
This cast is used for integration with the C-interface primitives.
\cppcast{PlTerm}{long}
Yields a \ctype{long} if the \ctype{PlTerm} is a Prolog integer or
float that can be converted without loss to a long. Throws a
\except{type_error} exception otherwise.
\cppcast{PlTerm}{int}
Same as for \ctype{long}, but might represent fewer bits.
\cppcast{PlTerm}{double}
Yields the value as a C double if \ctype{PlTerm} represents a
Prolog integer or float.
\cppcast{PlTerm}{wchar_t *}
\nodescription
\cppcast{PlTerm}{char *}
Converts the Prolog argument using PL_get_chars() using the flags
\const{CVT_ALL|CVT_WRITE|BUF_RING}, which implies Prolog atoms and
strings are converted to the represented text. All other data is
handed to write/1. If the text is static in Prolog, a direct pointer
to the string is returned. Otherwise the text is saved in a ring of
16 buffers and must be copied to avoid overwriting.
\cppcast{PlTerm}{void *}
Extracts pointer value from a term. The term should have been created
by PlTerm::PlTerm(void*).
\end{description}
In addition, the Prolog type (\const{PL_VARIABLE},
\const{PL_ATOM}, ... \const{PL_DICT})
can be determined using the type() method. There are also boolean
methods that check the type:
\begin{description}
\cfunction{int}{PlTerm::type}{} See PL_term_type()
\cfunction{bool}{PlTerm::is_variable}{} See PL_is_variable()
\cfunction{bool}{PlTerm::is_ground}{} See PL_is_ground()
\cfunction{bool}{PlTerm::is_atom} See PL_is_atom()
\cfunction{bool}{PlTerm::is_integer} See PL_is_integer()
\cfunction{bool}{PlTerm::is_string} See PL_is_string()
\cfunction{bool}{PlTerm::is_atom_or_string} Is true if either PlTerm::is_atom()
or PlTerm::is_string() is true.
\cfunction{bool}{PlTerm::is_float} See PL_is_float()
\cfunction{bool}{PlTerm::is_rational} See PL_is_rational()
\cfunction{bool}{PlTerm::is_compound} See PL_is_compound()
\cfunction{bool}{PlTerm::is_callable} See PL_is_callable()
\cfunction{bool}{PlTerm::is_list} See PL_is_list()
\cfunction{bool}{PlTerm::is_dict} See PL_is_dict()
\cfunction{bool}{PlTerm::is_pair} See PL_is_pair()
\cfunction{bool}{PlTerm::is_atomic} See PL_is_atomic()
\cfunction{bool}{PlTerm::is_number} See PL_is_number()
\cfunction{bool}{PlTerm::is_acyclic} See PL_is_acyclic()
\cfunction{bool}{PlTerm::is_functor}{PlFunctor} See PL_is_functor()
\end{description}
\subsection{Unification}
\label{sec:cpp2-plterm-unification}
See also \secref{cpp2-plframe}.
\begin{description}
\cfunction{bool}{PlTerm::unify_term}{PlTerm}
\nodescription
\cfunction{bool}{PlTerm::unify_atom}{PlAtom}
\nodescription
\cfunction{bool}{PlTerm::unify_atom}{string}
\nodescription
\cfunction{bool}{PlTerm::unify_list_codes}{string}
\nodescription
\cfunction{bool}{PlTerm::unify_list_chars}{string}
\nodescription
\cfunction{bool}{PlTerm::unify_integer}{int}
\nodescription
\cfunction{bool}{PlTerm::unify_float}{double}
\nodescription
\cfunction{bool}{PlTerm::unify_string}{string}
\nodescription
\cfunction{bool}{PlTerm::unify_functor}{PlFunctor}
\nodescription
\cfunction{bool}{PlTerm::unify_pointer}{void *}
\nodescription
\cfunction{bool}{PlTerm::unify_nil}{}
\nodescription
\cfunction{bool}{PlTerm::unify_blob}{PlBlob* blob}
\nodescription
\cfunction{bool}{PlTerm::unify_blob}{std::unique_ptr<PlBlob>* blob}
Does a call to PL_unify_blob() and, if successful, calls
std::unique_ptr<PlBlob>::release() to pass ownership to the Prolog blob;
on failure or error, deletes the pointer (ad calls its destructor).
After either success and failure, \exam{*blob==nullptr}.
\cfunction{bool}{PlTerm::unify_blob}{void *blob, size_t len, PL_blob_t *type}
\nodescription
\cfunction{bool}{PlTerm::unify_chars}{int flags, size_t len, const char *s}
A family of unification methods are defined for the various Prolog types and
C++ types. Wherever \ctype{string} is shown, you can use:
\begin{itemize}
\item \ctype{char*}
\item \ctype{whar_t*}
\item \ctype{std::string}
\item \ctype{std::wstring}
\end{itemize}
\end{description}
Here is an example:
\begin{code}
PREDICATE(hostname, 1)
{ char buf[256];
if ( gethostname(buf, sizeof buf) == 0 )
return A1.unify_atom(buf);
return false;
}
\end{code}
An alternative way of writing this would use the \cfuncref{PlCheckFail}{}
to raise an exception if the unification fails.
\begin{code}
PREDICATE(hostname2, 1)
{ char buf[256];
PlCheckFail(gethostname(buf, sizeof buf) == 0);
PlCheckFail(A1.unify_atom(buf));
return true;
}
\end{code}
Of course, in a real program, the failure of
\cfuncref{gethostname}{buf}{sizeof buf} should create an error term
than contains information from \const{errno}.
\subsection{Comparison}
\label{sec:cpp2-plterm-comparison}
\begin{description}
\cfunction{int}{PlTerm::compare}{const PlTerm \&t2}
\nodescription
\cfunction{bool}{PlTerm::operator ==}{const PlTerm \&t}
\nodescription
\cfunction{bool}{PlTerm::operator !=}{const PlTerm \&t}
\nodescription
\cfunction{bool}{PlTerm::operator $<$}{const PlTerm \&t}
\nodescription
\cfunction{bool}{PlTerm::operator $>$}{const PlTerm \&t}
\nodescription
\cfunction{bool}{PlTerm::operator $<=$}{const PlTerm \&t}
\nodescription
\cfunction{bool}{PlTerm::operator $>=$}{const PlTerm \&t}
Compare the instance with \arg{t} and return the result according to
the Prolog defined \jargon{standard order of terms}.
\cfunction{bool}{PlTerm::operator ==}{long num}
\nodescription
\cfunction{bool}{PlTerm::operator !=}{long num}
\nodescription
\cfunction{bool}{PlTerm::operator $<$}{long num}
\nodescription
\cfunction{bool}{PlTerm::operator $>$}{long num}
\nodescription
\cfunction{bool}{PlTerm::operator $<=$}{long num}
\nodescription
\cfunction{bool}{PlTerm::operator $>=$}{long num}
Convert \ctype{PlTerm} to a \ctype{long} and perform standard
C-comparison between the two long integers. If \ctype{PlTerm} cannot be
converted a \except{type_error} is raised.
\cfunction{bool}{PlTerm::operator ==}{const wchar_t *}
\nodescription
\cfunction{bool}{PlTerm::operator ==}{const char *}
\nodescription
\cfunction{bool}{PlTerm::operator ==}{std::wstring}
\nodescription
\cfunction{bool}{PlTerm::operator ==}{std::string}
Yields \const{true} if the \ctype{PlTerm} is an atom or string
representing the same text as the argument, \const{false} if the
conversion was successful, but the strings are not equal and an
\except{type_error} exception if the conversion failed.
\end{description}
Below are some typical examples. See \secref{cpp2-dirplatom} for direct
manipulation of atoms in their internal representation.
\begin{center}
\begin{tabularlp}{\tt A1 == PlCompound("a(1)")}
\hline
\tt A1 $<$ 0 & Test \arg{A1} to hold a Prolog integer or float
that can be transformed lossless to an integer
less than zero. \\
\tt A1 $<$ PlTerm(0) &
\arg{A1} is before the term `0' in the `standard
order of terms'. This means that if \arg{A1}
represents an atom, this test yields \const{true}. \\
\tt A1 == PlCompound("a(1)") &
Test \arg{A1} to represent the term
\exam{a(1)}. \\
\tt A1 == "now" &
Test \arg{A1} to be an atom or string holding the
text ``now''. \\
\hline
\end{tabularlp}
\end{center}
\subsection{Analysing compound terms}
\label{sec:cpp2-plterm-compound}
Compound terms can be viewed as an array of terms with a name and arity
(length). This view is expressed by overloading the \const{[]} operator.
A \except{type_error} is raised if the argument is not compound and a
\except{domain_error} if the index is out of range.
In addition, the following functions are defined:
\begin{description}
\cfunction{PlTerm}{PlTerm::operator []}{int arg}
If the \ctype{PlTerm} is a compound term and \arg{arg} is between 1 and
the arity of the term, return a new \ctype{PlTerm} representing the
arg-th argument of the term. If \ctype{PlTerm} is not compound, a
\except{type_error} is raised. Id \arg{arg} is out of range, a
\except{domain_error} is raised. Please note the counting from 1 which
is consistent to Prolog's arg/3 predicate, but inconsistent to C's
normal view on an array. See also class \ctype{PlCompound}. The
following example tests \arg{x} to represent a term with first-argument
an atom or string equal to \exam{gnat}.
\begin{code}
...,
if ( x[1] == "gnat" )
...
\end{code}
\cfunction{const char *}{PlTerm::name}{}
Return a \ctype{const char *} holding the name of the functor of the
compound term. Raises a \except{type_error} if the argument is not
compound.
\cfunction{size_t}{PlTerm::arity}{}
Returns the arity of the compound term. Raises a \except{type_error} if
the argument is not compound.
\end{description}
\subsection{Miscellaneous}
\label{sec:cpp2-plterm-misc}
\begin{description}
\cfunction{bool}{is_null}{}
\exam{t.is_null()} is the same as \exam{t.unwrap() == PlTerm::null}
\cfunction{bool}{not_null}{}
\exam{t.not_null()} is the same as \exam{t.unwrap() != PlTerm::null}
\cfunction{bool}{reset}{}
\exam{t.reset()} is the same as \exam{t.unwrap() = PlTerm::null}
\cfunction{bool}{reset}{term_t}
\exam{t.reset(x)} is the same as \exam{t.unwrap() = x}
\cfunction{int}{PlTerm::type}{}
Yields the actual type of the term as PL_term_type(). Return values are
\const{PL_VARIABLE}, \const{PL_FLOAT}, \const{PL_INTEGER},
\const{PL_ATOM}, \const{PL_STRING} or \const{PL_TERM}
\cfunction{std::string}{as_string}{PlEncoding enc=EncLocale}
Returns the string representation of the atom.
See PlAtom::as_string() for an explanation of the encodings
and caveats about std::string::c_str().
\cfunction{std::string}{atomic_as_string}{PlEncoding enc=EncLocale}
As PlTerm::as_string(), but throws an exception if the term
isn't atomic (see atomic/1).
\cfunction{std::string}{atom_or_string_as_string}{PlEncoding enc=EncLocale}
As PlTerm::as_string(), but throws an exception if the term
isn't an atom or a string.
\end{description}
To avoid very confusing combinations of constructors and therefore
possible undesirable effects a number of subclasses of \ctype{PlTerm}
have been defined that provide constructors for creating special Prolog
terms. These subclasses are defined below.
\subsection{The class PlTerm_string}
\label{sec:cpp2-plstring}
A SWI-Prolog string represents a byte-string on the global stack. Its
lifetime is the same as for compound terms and other data living on
the global stack. Strings are not only a compound representation of
text that is garbage-collected, but as they can contain 0-bytes, they
can be used to contain arbitrary C-data structures. However, it is
generally preferred to use blobs for storing arbitrary C-data structures
(see also \exam{PlTerm_pointer(void *ptr)}).
\begin{description}
\constructor{PlTerm_string}{const wchar_t *text}
\nodescription
\constructor{PlTerm_string}{const char *text}
Create a SWI-Prolog string object from a 0-terminated C-string. The
\arg{text} is copied.
\constructor{PlTerm_string}{const wchar_t *text, size_t len}
\nodescription
\constructor{PlTerm_string}{const char *text, size_t len}
Create a SWI-Prolog string object from a C-string with specified length.
The \arg{text} may contain 0-characters and is copied.
\end{description}
\subsection{The class PlCodeList}
\label{sec:cpp2-codelist}
\begin{description}
\constructor{PlCodeList}{const wchar_t *text}
\nodescription
\constructor{PlCodeList}{const char *text}
Create a Prolog list of ASCII codes from a 0-terminated C-string.
\end{description}
\subsection{The class PlCharList}
\label{sec:cpp2-plcharlist}
Character lists are compliant to Prolog's atom_chars/2 predicate.
\begin{description}
\constructor{PlCharList}{const wchar_t *text}
\nodescription
\constructor{PlCharList}{const char *text}
Create a Prolog list of one-character atoms from a 0-terminated
C-string.
\end{description}
\subsection{The class PlCompound}
\label{sec:cpp2-plcompound}
The \ctype{PlCompound} class is a convenience class for creating
a term from a string; it is similar to (=..)/2
\begin{description}
\constructor{PlCompound}{const wchar_t *text}
\nodescription
\constructor{PlCompound}{const char *text}
\nodescription
\constructor{PlCompound}{const std::wstring\& text}
\nodescription
\constructor{PlCompound}{const std::string\& text}{PlEncoding enc=ENC_INPUT}
Create a term by parsing (as read/1) the \arg{text}. If the \arg{text}
is not valid Prolog syntax, a \except{syntax_error} exception is raised.
Otherwise a new term-reference holding the parsed text is created.
\constructor{PlCompound}{const wchar_t *functor, PlTermv args}
\nodescription
\constructor{PlCompound}{const char *functor, PlTermv args}
Create a compound term with the given name from the given vector of
arguments. See \ctype{PlTermv} for details. The example below
creates the Prolog term \exam{hello(world)}.
\begin{code}
PlCompound("hello", PlTermv(PlAtom("world")))
\end{code}
\end{description}
\subsection{The class PlTerm_tail}
\label{sec:cpp2-pltail}
The class \ctype{PlTerm_tail}\footnote{This was named \ctype{PlTail}
in version 1 of the API.} is both for analysing and constructing
lists. It is called \ctype{PlTerm_tail} as enumeration-steps make the
term-reference follow the ``tail'' of the list.
\begin{description}
\constructor{PlTerm_tail}{PlTerm list}
A \ctype{PlTerm_tail} is created by making a new term-reference pointing to
the same object. As \ctype{PlTerm_tail} is used to enumerate or build a
Prolog list, the initial \arg{list} term-reference keeps pointing to
the head of the list.
\cfunction{int}{PlTerm_tail::append}{const PlTerm \&element}
Appends \arg{element} to the list and make the \ctype{PlTerm_tail} reference
point to the new variable tail. If \arg{A} is a variable, and this
function is called on it using the argument \exam{"gnat"}, a list of
the form \exam{[gnat|B]} is created and the \ctype{PlTerm_tail} object
now points to the new variable \arg{B}.
This function returns \const{true} if the unification succeeded and
\const{false} otherwise. No exceptions are generated.
The example below translates the main() argument vector to Prolog and
calls the prolog predicate entry/1 with it.
\begin{code}
int
main(int argc, char **argv)
{ PlEngine e(argv[0]);
PlTermv av(1);
PlTerm_tail l(av[0]);
for(int i=0; i<argc; i++)
PlCheckFail(l.append(argv[i]));
PlCheckFail(l.close());
PlQuery q("entry", av);
return q.next_solution() ? 0 : 1;
}
\end{code}
\cfunction{int}{PlTerm_tail::close}{}
Unifies the term with \const{[]} and returns the result of the
unification.
\cfunction{int}{PlTerm_tail::next}{PlTerm \&t}
Bind \arg{t} to the next element of the list \ctype{PlTerm_tail} and advance
\ctype{PlTerm_tail}. Returns \const{true} on success and \const{false} if
\ctype{PlTerm_tail} represents the empty list. If \ctype{PlTerm_tail} is neither a
list nor the empty list, a \except{type_error} is thrown. The example
below prints the elements of a list.
\begin{code}
PREDICATE(write_list, 1)
{ PlTerm_tail tail(A1);
PlTerm_var e;
while(tail.next(e))
cout << e.as_string() << endl;
return tail.close();
}
\end{code}
\end{description}
\subsection{The class PlTermv}
\label{sec:cpp2-pltermv}
The class \ctype{PlTermv} represents an array of term-references. This
type is used to pass the arguments to a foreign defined predicate,
construct compound terms (see
\cfuncref{PlTerm::PlTerm}{const char *name}{PlTermv arguments}),
and to create queries (see \ctype{PlQuery}).
The only useful member function is the overloading of \const{[]},
providing (0-based) access to the elements. Range checking is performed
and raises a \except{domain_error} exception.
The constructors for this class are below. Note that these can be
error-prone because there's no distinction between \ctype{term_t} and
\ctype{size_t}; the form of the constructor is determined by whether
the first argument is an integer (\ctype{term_t} or \ctype{size_t}) or
\ctype{PlTerm}.
\begin{description}
\constructor{PlTermv}{size_t size}
Create a new array of term-references, all holding variables.
\constructor{PlTermv}{size_t size, term_t t0}
Convert a C-interface defined term-array into an instance.
Typyically, \arg{t0} was created using Pl_new_term_refs(size).
\constructor{PlTermv}{PlTerm ...}
Create a vector from 1 to 5 initialising arguments. For example:
\begin{code}
load_file(const char *file)
{ return PlCall("compile", PlTermv(PlAtom(file)));
}
\end{code}
If the vector has to contain more than 5 elements, the following
construction should be used:
\begin{code}
{ PlTermv av(10);
av[0].put_term(PlTerm_atom("hello"));
av[1].put_term(PlTerm_integer(666));
...
}
\end{code}
\emph{Important}: be sure that all the arguments are of type
\ctype{PlTerm} - \exam{PlTermv(i)} is not the same as
\exam{PlTermv(PlTerm_integer(i))}, and will result in a runtime error.
\end{description}
\subsection{The class PlAtom - Supporting Prolog constants}
\label{sec:cpp2-prolog-constants}
Both for quick comparison as for quick building of lists of atoms, it
is desirable to provide access to Prolog's atom-table, mapping handles
to unique string-constants. If the handles of two atoms are different
it is guaranteed they represent different text strings.
Suppose we want to test whether a term represents a certain atom, this
interface presents a large number of alternatives:
\subsubsection{Direct comparision to char *}
\label{sec:cpp2-direct-commparison-to-char-star}
Example:
\begin{code}
PREDICATE(test, 1)
{ if ( A1 == "read" )
...;
}
\end{code}
This writes easily and is the preferred method is performance is not
critical and only a few comparisons have to be made. It validates
\arg{A1} to be a term-reference representing text (atom, string, integer
or float) extracts the represented text and uses strcmp() to match the
strings.
\subsubsection{Direct comparision to PlAtom} \label{sec:cpp2-dirplatom}
Example:
\begin{code}
static PlAtom ATOM_read("read");
PREDICATE(test, 1)
{ if ( A1 == ATOM_read )
...;
}
\end{code}
This case raises a \except{type_error} if \arg{A1} is not an atom.
Otherwise it extacts the atom-handle and compares it to the atom-handle
of the global \ctype{PlAtom} object. This approach is faster and
provides more strict type-checking.
\subsubsection{Extraction of the atom and comparison to PlAtom}
\label{sec:cpp2-extraction-comparison-atoms}
Example:
\begin{code}
static PlAtom ATOM_read("read");
PREDICATE(test, 1)
{ PlAtom a1(A1);
if ( a1 == ATOM_read )
...;
}
\end{code}
This approach is basically the same as \secref{cpp2-dirplatom}, but in
nested if-then-else the extraction of the atom from the term is
done only once.
\subsubsection{Extraction of the atom and comparison to char *}
\label{sec:cpp2-extraction-comparison-char-star}
Example:
\begin{code}
PREDICATE(test, 1)
{ PlAtom a1(A1);
if ( a1 == "read" )
...;
}
\end{code}
This approach extracts the atom once and for each test extracts
the represented string from the atom and compares it. It avoids
the need for global atom constructors.
\begin{description}
\constructor{PlAtom}{atom_t handle}
Create from C-interface atom handle (\ctype{atom_t}). Used internally and for
integration with the C-interface.
\constructor{PlAtom}{const char_t *text}
\nodescription
\constructor{PlAtom}{const wchar *text}
\nodescription
\constructor{PlAtom}{const std::string\& text}
\nodescription
\constructor{PlAtom}{const std::wstring\& text}
Create an atom from a string. The \arg{text} is copied if a new atom
is created. See PL_new_atom(), PL_new_atom_wchars(),
PL_new_atom_nchars(), PL_new_atom_wchars().
\constructor{PlAtom}{const PlTerm \&t}
If \arg{t} represents an atom, the new instance represents this
atom. Otherwise a \except{type_error} is thrown.
\cfunction{int}{PlAtom::operator ==}{const wchar_t *text}
\nodescription
\cfunction{int}{PlAtom::operator ==}{const char *text}
\nodescription
\cfunction{int}{PlAtom::operator ==}{const std::string\& text}
\nodescription
\cfunction{int}{PlAtom::operator ==}{const std::wstring\& text}
Yields \const{true} if the atom represents \arg{text}, \const{false}
otherwise. Performs a strcmp() or similar for this.
\cfunction{int}{PlAtom::operator ==}{const PlAtom \&a}
Compares the two atom-handles, returning \const{true} or
\const{false}. Because atoms are unique, there is no need
to use strcmp() for this.
\cfunction{int}{PlAtom::operator !=}{const wchar_t *text}
\nodescription
\cfunction{int}{PlAtom::operator !=}{const char *text}
\nodescription
\cfunction{int}{PlAtom::operator !=}{const std::string\& text}
\nodescription
\cfunction{int}{PlAtom::operator !=}{const std::wstring\& text}
\nodescription
\cfunction{int}{PlAtom::operator !=}{const PlAtom \&a}
The inverse of the \exam{==} operator.
\cfunction{bool}{is_valid}{}
Verifies that the handle is valid. This can be used after calling
a function that returns an atom handle, to check that a new atom
was created.
\cfunction{void}{reset}{}
Sets the handle to an invalid valid - a subsequent call to is_null()
will return \const{true}.
\cfunction{const std::string}{as_string}{PlEncoding enc=EncLocale}
Returns the string representation of the atom.\footnote{If you wish
to return a \ctype{char*} from a function, you should not do
\exam{return t.as_string().c_str()} because that will return a pointer
into the stack (Gnu C++ or Clang options \exam{-Wreturn-stack-address}
or \exam{-Wreturn-local-addr}) can \emph{sometimes} catch this, as can
the runtime address sanitizer when run with
\exam{detect_stack_use_after_return=1}.}
This does not
quote or escape any characters that would need to be escaped
if the atom were to be input to the Prolog parser. The possible values
for \exam{enc} are:
\begin{itemize}
\item \exam{EncLatin1} - throws an exception if cannot be represented in ASCII.
\item \exam{EncUTF8}
\item \exam{EncLocale} - uses the locale to determine the representation.
\end{itemize}
\cfunction{const std:wstring}{as_wstring}{}
Returns the string representation of the atom. This does not
quote or escape any characters that would need to be escaped
if the atom were to be input to the Prolog parser.
\cfunction{void}{register_atom}{}
See PL_register_atom().
\cfunction{void}{unregister_atom}{}
See PL_unregister_atom().
\cfunction{void*}{blob_data}{size_t *len, struct PL_blob_t **type}
See PL_blob_data().
\end{description}
\subsection{Classes for the recorded database: PlRecord and PlRecordExternalCopy}
\label{sec:cpp2-plrecord}
The
\href{https://www.swi-prolog.org/pldoc/man?section=foreign-recorded}{recorded
database} is has two wrappers, for supporting the \jargon{internal records}
and \jargon{external records}.
Currently, the interface to \jargon{internal records} requires that
the programmer explicitly call the dupicate() and erase() methods - in
future, it is intended that this will be done automatically by a new
\ctype{PlRecord} class, so that the internal records behave like
``smart pointers''; in the meantime, the \ctype{PlRecord} provides a
trivial wrapper around the various recorded database functions.
The class \ctype{PlRecord} supports the following methods:
\begin{description}
\cfunction{}{PlRecord}{PlTerm} Constructor.
\cfunction{}{PlRecord}{PlRecord} Copy and move
constructors. Currently these do not do any reference counting.
The assignment operator is currently not supported.
\cfunction{}{~PlRecord}{} Destructor. Currently this does not call PL_erase().
\cfunction{PlTerm}{term}{} creates a term from the record, using PL_recorded().
\cfunction{void}{erase}{} decrements the reference count of the
record and deletes it if the count goes to zero, using PL_erase().
It is safe to do this multiple times on the same
\ctype{PlRecord} object.
\cfunction{PlRecord}{duplicate}{} increments the reference count
of the record, using PL_duplicate_record().
\end{description}
The class \ctype{PlRecord} provides direct access to the reference
counting aspects of the recorded term (through the duplicate() and
erase() methods), but does \emph{not} connect these with C++'s copy
constructor, assignment operator, or destructor. If the recorded
term is encapsulated within an object, then the containing object
can use the duplicate() and erase() methods in its copy and
move constructors and assignment operators (and the erase() method
in the destructor).\footnote{The copy constructor and assignment use
the duplicate() method; the move constructor and assignment use
the duplicate() method to assign to the destination and the erase()
method on the source; and the destructor uses erase().}
Alternatively, the \ctype{std::shared_ptr} or \ctype{std::unique_ptr}
can be used with the supplied \ctype{PlrecordDeleter}, which calls the
erase() method when the \ctype{shared_ptr} reference count goes to
zero or when the \ctype{std::unique_ptr} goes out of scope.
For example:
\begin{code}
std::shared_ptr<PlRecord> r(new PlRecord(t.record()), PlRecordDeleter());
assert(t.unify_term(r->term()));
\end{code}
The class \ctype{PlRecordExternalCopy} keeps the \jargon{external record}
as an uninterpreted string (which may contain nulls).
It supports the following methods.
\begin{description}
\constructor{PlRecordExternalCopy}{PlTerm t}
Creates a string using Pl_record_external(), copies it into
the object then deletes the reference using PL_erase_external().
\constructor{PlRecordExternalCopy}{const std::string\& external}
Saves the \arg{external} string (which is assumed to have been
created using PL_record_external()).
\constructor{PlRecordExternalCopy}{const char* external, size_t len}
Saves the \arg{external} string (which is assumed to have been
created using PL_record_external()).
\cfunction{PlTerm}{term}{} creates a term from the saved external
record string, using PL_recorded_external()).
\cfunction{static PlTerm}{term}{const std::string\& external}
Creates a term from the external record string.
Equivalent to PlRecordExternalCopy(external).term().
\cfunction{static PlTerm}{term}{const char* external}
Creates a term from the external record string.
Equivalent to PlRecordExternalCopy(external,len).term()
except the length is inferred from \arg{external}'s contents.
\cfunction{const std::string\&}{data}{} Gets the external
string that was created by the constructor.
\end{description}
\section{The class PlRegister}
\label{sec:cpp2-plregister}
This class encapsulates PL_register_foreign(). It is defined as a class
rather then a function to exploit the C++ \jargon{global constructor}
feature. This class provides a constructor to deal with the PREDICATE()
way of defining foreign predicates as well as constructors to deal with
more conventional foreign predicate definitions.
\begin{description}
\constructor{PlRegister}{const char *module,
const char *name,
int arity,
foreign_t (f)(term_t t0, int a, control_t ctx)}
Register \arg{f} as a the implementation of the foreign predicate
<name>/<arity>. This interface uses the \const{PL_FA_VARARGS} calling
convention, where the argument list of the predicate is passed using an
array of \ctype{term_t} objects as returned by PL_new_term_refs(). This
interface poses no limits on the arity of the predicate and is faster,
especially for a large number of arguments.
\constructor{PlRegister}{const char *module,
const char *name,
foreign_t (*f)(PlTerm a0, \ldots)}
Registers functions for use with the traditional calling conventional,
where each positional argument to the predicate is passed as an argument
to the function \arg{f}. This can be used to define functions as
predicates similar to what is used in the C-interface:
\begin{code}
static foreign_t
pl_hello(PlTerm a1)
{ ...
}
PlRegister x_hello_1(NULL, "hello", 1, pl_hello);
\end{code}
This construct is currently supported upto 3 arguments.
\end{description}
\section{The class PlQuery}
\label{sec:cpp2-plquery}
This class encapsulates the call-backs onto Prolog.
\begin{description}
\constructor{PlQuery}{const char *name, const PlTermv \&av, int flags = PL_Q_PASS_EXCEPTION}
Create a query where \arg{name} defines the name of the predicate and
\arg{av} the argument vector. The arity is deduced from \arg{av}. The
predicate is located in the Prolog module \module{user}.
\constructor{PlQuery}{const char *module, const char *name,
const PlTermv \&av, int flags = PL_Q_PASS_EXCEPTION}
Same, but performs the predicate lookup in the indicated module.
\cfunction{int}{PlQuery::next_solution}{}
Provide the next solution to the query. Yields \const{true} if
successful and \const{false} if there are no (more) solutions.
Prolog exceptions are mapped to C++ exceptions.
If the \class{PlQuery} object was created with the \const{PL_Q_EXT_STATUS}
flag, the extended return codes can also be returned
(\const{TRUE},
\const{FALSE},
\const{PL_S_NOT_INNER},
\const{PL_S_EXCEPTION},
\const{PL_S_FALSE},
\const{PL_S_TRUE},
\const{PL_S_LAST}).
Because of this, you shouldn't use PlCheckFail() with PlQuery::next_solution()
in this situation.
\cfunction{void}{PlQuery::cut}{}
Discards the query, but does not delete an of the data created
by the query. If there is any pending Prolog exception, it is
mapped to a C++ exception and thrown.
The call to PlQuery::cut() is done implicitly by \ctype{PlQuery}'s destructor.
Below is an example listing the currently defined Prolog modules
to the terminal.
\begin{code}
PREDICATE(list_modules, 0)
{ PlTermv av(1);
PlQuery q("current_module", av);
while( q.next_solution() )
cout << av[0].as_string() << endl;
return true;
}
\end{code}
\end{description}
In addition to the above, the following functions have been defined.
\begin{description}
\cfunction{int}{PlCall}{const char *predicate, const PlTermv \&av}
Creates a \ctype{PlQuery} from the arguments generates the
first next_solution() and destroys the query. Returns the
result of next_solution() or an exception.
\cfunction{int}{PlCall}{const char *module, const char *predicate,
const PlTermv \&av}
Same, locating the predicate in the named module.
\cfunction{int}{PlCall}{const wchar_t *goal}
\nodescription
\cfunction{int}{PlCall}{const std::string\& goal}
Translates \arg{goal} into a term and calls this term as the other
PlCall() variations. Especially suitable for simple goals such as making
Prolog load a file.
\cfunction{bool}{PlTerm::call}{}
Wrapper for PL_call(), returning \const{true} or \const{false} for
the success/failure of the call; and throws an exception if there's an error.
\exam{t.call()} is essentially the same as \exam{PlCall(t)}.
\cfunction{bool}{PlTerm::call}{PlModule m}
Same as PlTerm::call() but specifying the module.
\end{description}
\subsection{The class PlFrame - Unification and foreign frames}
\label{sec:cpp2-plframe}
As documented with PL_unify(), if a unification call fails and control
isn't made immediately to Prolog, any changes made by unification must
be undone. The functions PL_open_foreign_frame(),
PL_rewind_foreign_frame(), PL_discard_foreign_frame(), and
PL_close_foreign_frame() are encapsulated in the class
\ctype{PlFrame}, whose destructor calls
PL_close_foreign_frame(). Using this, the example code with PL_unify()
can be written:
\begin{code}
PREDICATE(can_unify_ffi, 2)
{ fid_t fid = PL_open_foreign_frame();
int rval = PL_unify(A1.unwrap(), A2.unwrap());
PL_discard_foreign_frame(fid);
return rval;
}
\end{code}
\begin{code}
/* equivalent to the Prolog code
T1 = T2 -> do_one_thing ; do_another_thing */
{ PlFrame fr;
bool t1_t2_unified = A1.unify_term(A2);
if ( ! t1_t2_unified )
fr.rewind();
if ( t1_t2_unified )
do_one_thing(...);
else
do_another_thing(...);
}
\end{code}
The following is C++ version of the code example for
PL_open_foreign_frame(). The calls to PL_close_foreign_frame() and the
check for PL_exception(0) in the C code aren't needed in the C++ code:
\begin{code}
static std::vector<std::string> lookup_unifies =
{ "item(one, 1)", "item(two, 2)", "item(three, 3)" };
PREDICATE(lookup_unify, 1)
{ PlFrame fr;
for (auto& s : lookup_unifies )
{ PlCompound t(s);
if ( A1.unify_term(t) )
return true;
fr.rewind();
}
return false;
}
\end{code}
or using this convenience wrapper:
\begin{code}
if ( RewindOnFail([t1=A1,t2=A2]()->bool
{ return t1.unify_term(t2); }) )
do_one_thing(...);
else
do_another_thing(...);
\end{code}
Note that PlTerm::unify_term() checks for an error and
throws an exception to Prolog; if you wish to handle exceptions, you
must call \exam{PL_unify_term(t1.unwrap(),t2.unwrap())}.
The class \ctype{PlFrame} provides an interface to discard unused
term-references as well as rewinding unifications
(\jargon{data-backtracking}).
Reclaiming unused term-references is
automatically performed after a call to a C++-defined predicate has
finished and returns control to Prolog. In this scenario \ctype{PlFrame}
is rarely of any use. This class comes into play if the toplevel program
is defined in C++ and calls Prolog multiple times. Setting up arguments
to a query requires term-references and using \ctype{PlFrame} is the
only way to reclaim them.
Another use of of \ctype{PlFrame} is when multiple separate
unifications are done - if any of them fails, then the earlier
unifications must be undone before returning to Prolog.
\begin{description}
\constructor{PlFrame}{}
Creating an instance of this class marks all term-references created
afterwards to be valid only in the scope of this instance.
\destructor{PlFrame}
Reclaims all term-references created after constructing the instance.
If either close() or discard() have been called, the destructor does nothing.
\cfunction{void}{PlFrame::rewind}{}
Discards all term-references {\bf and} global-stack data created as well
as undoing all unifications after the instance was created.
\cfunction{void}{PlFrame::close}{}
Reclaims all term-references created after constructing the instance.
\cfunction{void}{PlFrame::discard}{}
Same as PlFrame::rewind() + PlFrame::close().
\cfunction{bool}{PlRewindOnFail}{std::function<bool()> f}
is a convenience function that does a
frame rewind if a function call fails (typically, failure due to
unification failure). It takes a std::function<bool>()> as an argument,
which is called in the context of a new \ctype{PlFrame}.
\end{description}
\index{assert}%
A typical use for \ctype{PlFrame} is the definition of C++ functions
that call Prolog and may be called repeatedly from C++. Consider the
definition of assertWord(), adding a fact to word/1; the \ctype{PlFrame}
removes the new term \exam{av[0]} from the stack, which prevents the stack
from growing each time assertWord() is called:
\begin{code}
void
assertWord(const char *word)
{ PlFrame fr;
PlTermv av(1);
av[0] = PlCompound("word", PlTermv(word));
PlQuery q("assert", av);
PlCheckFail(q.next_solution());
}
\end{code}
The following example uses \ctype{PlFrame} in the context of a foreign
predicate. The can_unify/2's truth-value is the same as for Prolog
unification (=/2), but has no side effects. In Prolog one would use
double negation to achieve this:
\begin{code}
PREDICATE(can_unify, 2)
{ PlFrame fr;
int rval = (A1=A2);
fr.discard(); // or, less efficiently: fr.rewindd();
return rval;
}
\end{code}
Here is an example of using PlRewindOnFail(),
where \exam{name_to_terms} contains a map from names to terms
(which are made global by using the PL_record() function). The frame
rewind is needed in the situation where the first unify_term() succeeds
and the second one fails.
\begin{code}
static const std::map<const std::string, PlRecord> name_to_term =
{ {"a", PlTerm(...).record(), PlTerm(...).record()},
... };
PREDICATE(name_to_terms, 3)
{ A1.must_be_atom_or_string();
const auto it = name_to_term.find(A1.as_string());
return it != name_to_term.cend() &&
PlRewindOnFail([t1=A2,t2=A3,&it]()
{ return t1.unify_term(it->second.first.term()) &&
t2.unify_term(it->second.second.term()); });
}
\end{code}
The equivalent code without using PlRewindOnFail() is:
\begin{code}
PREDICATE(name_to_terms, 3)
{ PlTerm key(A1), term1(A2), term2(A3);
const auto it = name_to_term.find(key.as_string());
if ( it == name_to_term.cend() )
return false;
if ( !term1.unify_term(it->second.first.term()) )
return false;
PlFrame fr;
if ( !term2.unify_term(it->second.second.term()) )
{ fr.discard();
return false;
}
return true;
}
\end{code}
\section{The PREDICATE and PREDICATE_NONDET macros}
\label{sec:cpp2-predicate-macro}
The PREDICATE macro is there to make your code look nice, taking care of
the interface to the C-defined SWI-Prolog kernel as well as mapping
exceptions. Using the macro
\begin{code}
PREDICATE(hello, 1)
\end{code}
is the same as writing:\footnote{There are a few more details,
such as catching \exam{std::bad_alloc}.}:
\begin{code}
static foreign_t pl_hello__1(PlTermv PL_av);
static foreign_t
_pl_hello__1(term_t t0, int arity, control_t ctx)
{ (void)arity; (void)ctx;
try
{ return pl_hello__1(PlTermv(1, t0));
} catch( PlFail& )
{ return false;
} catch ( PlException& ex )
{ return ex.plThrow();
}
}
static PlRegister _x_hello__1("hello", 1, _pl_hello__1);
static foreign_t
pl_hello__1(PlTermv PL_av)
\end{code}
The first function converts the parameters passed from the Prolog
kernel to a \ctype{PlTermv} instance and maps exceptions raised in the
body to simple failure or Prolog exceptions. The \ctype{PlRegister}
global constructor registers the predicate. Finally, the function
header for the implementation is created.
\subsection{Variations of the PREDICATE macro}
\label{sec:cpp2-predicate-macro-variations}
The PREDICATE() macros have a number of variations that deal with
special cases.
\begin{description}
\cmacro{}{PREDICATE}{name, arity}
Create a predicate with an automatically generated internal name, and
register it with Prolog. The various term arguments are accessible
as \exam{A1}, \exam{A2}, etc.
\cmacro{}{PREDICATE0}{name}
This is the same as PREDICATE(name, 0). It avoids a compiler warning
that \const{PL_av} is not used.
\cmacro{}{NAMED_PREDICATE}{plname, cname, arity}
This version can be used to create predicates whose name is not a valid
C++ identifier. Here is a ---hypothetical--- example, which unifies the
second argument with a stringified version of the first. The \arg{cname} is
used to create a name for the functions. The concrete name does not
matter, but must be unique. Typically it is a descriptive name using the
limitations imposed by C++ indentifiers.
\begin{code}
NAMED_PREDICATE("#", hash, 2)
{ return A2.unify_string(A1.as_string());
}
\end{code}
\cmacro{}{PREDICATE_NONDET}{name, arity}
Define a non-deterministic Prolog predicate in C++. See also
\secref{cpp2-nondet}.
\cmacro{}{NAMED_PREDICATE_NONDET}{plname, cname, arity}
Define a non-deterministic Prolog predicate in C++, whose name
is not a valid C++ identifier. See also \secref{cpp2-nondet}.
\end{description}
\subsection{Non-deterministic predicates}
\label{sec:cpp2-nondet}
Non-deterministic predicates are defined using
\cfuncref{PREDICATE_NONDET}{plname, cname, arity} or
\cfuncref{NAMED_PREDICATE_NONDET}{plname, cname, arity}.
A non-deterministic predicate returns a ``context'', which is passed to
a subsequent retry. Typically, this context is allocated on the first
call to the predicate and freed when the predicate either fails or
does its last successful return (the context is \const{nullptr} on the
first call). To simplify this, a template helper function
PlControl::context_unique_ptr<ContextType>() provides a ``smart
pointer'' that frees the context on normal return or an exception; when
used with PL_retry_address(), the context's
std:unique_ptr<ContextType>::release() is used to pass the context
to Prolog for the next retry, and to prevent the context
from being freed. If the predicate is called with \const{PL_PRUNE},
the normal \exam{return true} will implicitly free the context.
The skeleton for a typical non-deterministic predicate is as follows.
The test for \const{PL_PRUNED} is done first to avoid an unneeded
\ctype{PlFrame} and also to ensure that \arg{A1}, \arg{A2}, etc.
aren't used when they have the value
\const{PlTerm::null}.\footnote{This code could be structured as a
\exam{switch} statement, but typically the \const{PL_FIRST_CALL} case
falls through to the \const{PL_REDO} case.}
There are a number of examples of non-deterministic predicates in the
test code \file{test_cpp.cpp}.
\begin{code}
struct PredContext { ... }; // The "context" for retries
PREDICATE_NONDET(pred, <arity>)
{ // "ctxt" must be acquired so that the destructor deletes it
auto ctxt = handle.context_unique_ptr<PredContext>();
const auto control = handle.foreign_control();
if ( control == PL_PRUNED )
return true;
// Can use A1, A2, etc. after we know control != PL_PRUNED
if ( ... ) // deterministic result
{ assert(control == PL_FIRST_CALL);
if ( ... )
return true; // Success (and no more solutions)
else
return fase;
}
if ( control = PL_FIRST_CALL )
{ ctxt.reset(new PredContext(...));
...
} else
{ assert(control == PL_REDO);
}
PlFrame fr;
for ( ; ctxt->valid(...) ; ctxt->next() )
{ if ( ... unify a result ... )
{ ctxt->next();
if ( ctxt->valid(...) )
PL_retry_addresss(ctxt.release()); // Succeed with a choice point
else
return true; // deterministic success
}
fr.rewind();
}
return false;
}
\end{code}
\subsection{Controlling the Prolog destination module}
\label{sec:cpp2-module}
With no special precautions, the predicates are defined into the
module from which load_foreign_library/1 was called, or in the module
\const{user} if there is no Prolog context from which to deduce the
module such as while linking the extension statically with the Prolog
kernel.
Alternatively, {\em before} loading the SWI-Prolog include file, the
macro PROLOG_MODULE may be defined to a string containing the name of
the destination module. A module name may only contain alpha-numerical
characters (letters, digits, _). See the example below:
\begin{code}
#define PROLOG_MODULE "math"
#include <SWI-Prolog.h>
#include <math.h>
PREDICATE(pi, 1)
{ A1 = M_PI;
}
\end{code}
\begin{code}
?- math:pi(X).
X = 3.14159
\end{code}
\section{Exceptions}
\label{sec:cpp2-exceptions}
See also \href{https://www.swi-prolog.org/pldoc/man?section=foreign-exceptions}{Prolog exceptions in foreign code}.
Prolog exceptions are mapped to C++ exceptions using the class
\ctype{PlException} (a subclass of \ctype{PlExceptionBase} to
represent the Prolog exception term. All type-conversion functions of
the interface raise Prolog-compliant exceptions, providing decent
error-handling support at no extra work for the programmer.
For some commonly used exceptions, convenience functions have been
created to exploit both their constructors for easy creation of these
exceptions. If you wish to trap these, you should use
\ctype{PlException} or \ctype{PlExceptionBase} and then look for the
appropriate error name. For example, the following code catches
\exam{"type_error"} and passes all other exceptions:
\begin{code}
try
{ do_something(...);
} catch (const PlException& e)
{ PlTerm e_t = e.term();
PlAtom ATOM_type_error("type_error");
// e_t.name() == PlAtom("error") && e_t.arity() == 2
if ( e_t[1].name() == ATOM_type_error) )
{ ... // expected type and culprit are \exam{e_t[1][1]} and \exam{e_t[1][2]}
} else throw;
}
\end{code}
The convenience functions are PlTypeEror() and PlDomainError(),
PlDomainError(), PlInstantiationError(), PlExistenceError(),
PlUninstantiationError(), PlRepresentationError(),
PlPermissionError(), PlResourceError(), PlUnknownError(). There is
also a PlGeneralError(inside) that creates \exam{error(inside,_)}
terms and is used by the other error convience functions.
To throw an exception, create an instance of \ctype{PlException} and
use \exam{throw}. This is intercepted by the PREDICATE macro and
turned into a Prolog exception. See \secref{cpp2-exceptions-notes}.
\begin{code}
char *data = "users";
throw PlException(PlCompound("no_database", PlTerm(data)));
\end{code}
\subsection{The class PlException}
\label{sec:cpp2-plexception}
This subclass of \ctype{PlExceptionBase} is used to represent
exceptions. Currently defined methods are:
\begin{description}
\constructor{PlException}{const PlTerm \&t}
Create an exception from a general Prolog term. This provides the
interface for throwing any Prolog terms as an exception.
\cfunction{std::string}{as_string}{}
The exception is translated into a message as produced by
print_message/2. The character data is stored in a ring. Example:
\begin{code}
...;
try
{ PlCall("consult(load)");
} catch ( PlException& ex )
{ cerr << ex.as_string() << endl;
}
\end{code}
\cfunction{int}{plThrow}{}
Used in the PREDICATE() wrapper to pass the exception to Prolog. See
PL_raise_exeption().
\end{description}
\subsubsection{The function PlTypeError}
\label{sec:cpp2-pl-type-error}
A \jargon{type error} expresses that a term does not satisfy the
expected basic Prolog type.
\begin{description}
\constructor{PlTypeError}{const std::string\& expected, const PlTerm \&actual}
Creates an ISO standard Prolog error term expressing the
\arg{expected} type and \arg{actual} term that does not satisfy this
type.
\end{description}
\subsubsection{The function PlDomainError}
\label{sec:cpp2-pl-domain-error}
A \jargon{domain error} expresses that a term satisfies the basic
Prolog type expected, but is unacceptable to the restricted domain
expected by some operation. For example, the standard Prolog open/3
call expect an \const{io_mode} (read, write, append, ...). If an integer
is provided, this is a \jargon{type error}, if an atom other than one
of the defined io-modes is provided it is a \jargon{domain error}.
\begin{description}
\constructor{PlDomainError}{const std::string\& expected, const PlTerm \&actual}
Creates an ISO standard Prolog error term expressing a the
\arg{expected} domain and the \arg{actual} term found.
\end{description}
\section{Embedded applications}
\label{sec:cpp2-embedding}
Most of the above assumes Prolog is `in charge' of the application and
C++ is used to add functionality to Prolog, either for accessing
external resources or for performance reasons. In some applications,
there is a \jargon{main-program} and we want to use Prolog as a
\jargon{logic server}. For these applications, the class
\ctype{PlEngine} has been defined.
Only a single instance of this class can exist in a process. When used
in a multi-threading application, only one thread at a time may have
a running query on this engine. Applications should ensure this using
proper locking techniques.%
\footnote{For Unix, there is a multi-threaded version of SWI-Prolog.
In this version each thread can create and destroy a
thread-engine. There is currently no C++ interface defined
to access this functionality, though ---of course--- you
can use the C-functions.}
\begin{description}
\constructor{PlEngine}{int argc, char **argv}
Initialises the Prolog engine. The application should make sure to
pass \exam{argv[0]} from its main function, which is needed in the
Unix version to find the running executable. See PL_initialise()
for details.
\constructor{PlEngine}{char *argv0}
Simple constructure using the main constructor with the specified
argument for \exam{argv[0]}.
\destructor{PlEngine}
Calls PL_cleanup() to destroy all data created by the Prolog engine.
\end{description}
\Secref{cpp2-pltail} has a simple example using this class.
\section{Considerations}
\label{sec:cpp2-considerations}
\subsection{The C++ versus the C interface}
\label{sec:cpp2-vs-c}
Not all functionality of the C-interface is provided, but as
\ctype{PlTerm} and \ctype{term_t} are essentially the same thing with
type-conversion between the two (using the unwrap() method), this interface
can be freely mixed with the functions defined for plain C.
For checking return codes from C functions, it is recommended to
use \cfuncref{PlCheckFail}{} or PlCheck_PL().
Using this interface rather than the plain C-interface requires a little
more resources. More term-references are wasted (but reclaimed on return
to Prolog or using \ctype{PlFrame}). Use of some intermediate types
(\ctype{functor_t} etc.) is not supported in the current interface,
causing more hash-table lookups. This could be fixed, at the price of
slighly complicating the interface.
Global terms and atoms need to be handled slightly differently in C++
than in C - see \secref{cpp2-global}
\subsection{Notes on exceptions}
\label{sec:cpp2-exceptions-notes}
Exceptions are normal Prolog terms that are handled specially by the
PREDICATE macro when they are used by a C++ \exam{throw}, and
converted into Prolog exceptions. The exception term may not be
unbound; that is, throw(_) must raise an error. The C++ code and
underlying C code do not explicitly check for the term being a
variable, and behaviour of raising an exception that is an unbound
term is undefined, including the possibility of causing a crash or
corrupting data.
The Prolog exception term error(Formal, _) is special. If the 2nd
argument of error/2 is undefined, and the term is thrown, the system
finds the catcher (if any), and calls the hooks in
library(prolog_stack) to add the context and stack trace information
when appropriate. That is, \exam{throw PlDomainError(Domain,Culprit)}
ends up doing the same thing as calling
\exam{PL_domain_error(Domain,Culprit)} which internally calls
PL_raise_exception() and returns control back to Prolog.
The VM handling of calling to C finds the \const{FALSE} return code,
checks for the pending exception and propagates the exception into the
Prolog environment. As the term references (\ctype{term_t}) used to
create the exception are lost while returning from the foreign
function we need some way to protect them. That is done using a
global \ctype{term_t} handle that is allocated at the epoch of Prolog.
PL_raise_exception() sets this to the term using PL_put_term().
PL_exception(0) returns the global exception \ctype{term_t} if it is
bound and 0 otherwise.
Special care needs to be taken with data backtracking using
PL_discard_foreign_frame() or PL_close_query() because that will
invalidate the exception term. So, between raising the exception and
returning control back to Prolog we must make sure not to do anything
that invalidates the exception term. If you suspect something like
that to happen, use the debugger with a breakpoint on
__do_undo__LD() defined in \file{pl-wam.c}.
In order to always preserve Prolog exceptions and return as quickly as
possible to Prolog on an exception, some of the C++ classes can throw
an exception in their destructor. This is theoretically a dangerous
thing to do, and can lead to a crash or program termination if the
destructor is invoked as part of handling another exception.
\subsection{Global terms, atoms, and functors}
\label{sec:cpp2-global}
Sometimes it is convenient to put constant terms and atoms as global
variables in a file (with a \exam{static} qualifier), so that they are
only created (and looked up) cone. This is fine for atoms and
functors, which can be created by something like this:
\begin{code}
static PlAtom ATOM_foo("foo");
static PlFunctor FUNCTOR_ff_2("ff", 2);
\end{code}
C++ makes no guarantees about the order of creating global variables
across ``translation units'' (that is, individual C++ files), but the
Prolog runtime ensures that the necessary initialization has been done
to allow \ctype{PlAtom} and \ctype{PlFunctor} objects to be created.
However, to be safe, it is best to put such global variables
\emph{inside} functions - C++ will initialize them on their firstuse.
Global Terms need a bit of care. For one thing, terms are ephemeral,
so it is wrong to have a \ctype{PlTerm} static variable - instead, a
\ctype{PlRecord} must be used, which will provide a fresh copy of the
term using PlRecord::term(). There is no guarantee that the Prolog
runtime has initialized everything needed for creating entries in the
recorded database (see
\href{https://www.swi-prolog.org/pldoc/man?section=foreign-recorded}{Recorded database}).
Therefore, global recorded terms must be wrapped inside a function.
C++ will call the constructor upon first use. For example:
\begin{code}
static PlTerm
term_foo_bar()
{ static PlRecord r(PlCompound("foo", PlTermv(PlTerm_atom("bar"))).record());
return r.term();
}
\end{code}
\subsection{Atom map utilities}
\label{sec:cpp2-atom-map}
The include file \file{SWI-cpp2-atommap.h} contains a templated class
\ctype{AtomMap} for mapping atoms to atoms or terms. The typical use
case is for when it is desired to open a database or stream and, instead
of passing around the blob, an atom can be used to identify the blob.
The keys in the map must be standard Prolog atoms and not blobs - the
code depends on the fact that an atom has a unique ID.
The \ctype{AtomMap} is thread-safe (it contains a mutex). It also takes
care of reference counts for both the key and the value. Here is
a typical use case:
\begin{code}
static AtomMap<PlAtom, PlAtom> map_atom_my_blob("alias", "my_blob");
// look up an entry:
auto value = map_atom_my_blob(A1.as_atom());
PlCheckFail(value.not_null());
// insert an entry:
map_atom_my_blob.insert(A1.as_atom(), A2.as_atom());
// remove an entry:
map_atom_my_blob.erase(A1.as_atom());
\end{code}
The constructor and methods are as follows:
\begin{itemize}
\cfunction{}{template<ValueType, StoredValueType> AtomMap::AtomMap}{const std::string\& insert_op}{const std::string\& insert_type}
Construct an \ctype{AtomMap}. The \arg{ValueType} and \arg{StoredValueType} specify
what type you wish for the value. Currently, two value types are supported:
\begin{itemize}
\item \ctype{PlAtom} - the \arg{StoredValueType} should be \ctype{PlAtom}.
\item \ctype{PlTerm} - the \arg{StoredValueType} shoud be \ctype{PlRecord}
(because the term needs to be put on the global stack).
\end{itemize}
The \arg{insert_op} and \arg{insert_type} values are used in constructing
error terms - these correspond to the \arg{operation} and
\arg{type} arguments to Pl_permission_error().
\cfunction{insert}{PlAtom key, ValueType value}
Inserts a new value; raises a \exam{permission_error}
if the value is already in the map, unless the value is
identical to the value in the map. The insert() method
converts the value to the \ctype{StoredValueType}.
The insertion code takes care of atom reference counts.
\cfunction{ValueType}{find}{PlAtom key}
Look up an entry. Success/failure can be determined by
using ValueType::is_null() or ValueType::not_null().
The stored value is converted from \ctype{StoredValueType}
to \ctype{ValueType}.
\cfunction{erase}{PlAtom} removes the entry from
the map. If there was no entry in the map with that key,
this is a no-op. The erasure code takes care of atom
reference counts.
\end{itemize}
\subsection{Static linking and embedding}
\label{sec:cpp2-linking}
The mechanisms outlined in this document can be used for static linking
with the SWI-Prolog kernel using \manref{swipl-ld}{1}. In general the
C++ linker should be used to deal with the C++ runtime libraries and
global constructors.
\subsection{Status and compiler versions}
\label{sec:cpp2-status}
The current interface can be entirely defined in the \fileext{h} file
using inlined code. This approach has a few advantages: as no C++
code is in the Prolog kernel, different C++ compilers with different
name-mangling schemas can cooperate smoothly. However, inlining
everything can lead to code bloat, so the larger functions and methods
have been put into a \fileext{cpp} file that can be either compiled
separately (by the same compiler as used by the foreign predicate)
or inlined as if it were part of the \fileext{h} file.
Also, changes to the header file have no consequences to binary
compatibility with the SWI-Prolog kernel. This makes it possible to
have different versions of the header file with few compatibility
consequences.
As of 2023-04, some details remain to be decided, mostly to do
with encodings. A few methods have a \ctype{PlEncoding} optional
parameter (e.g., PlTerm::as_string()), but this hasn't yet been
extended to all methods that take or return a string. Also, the
details of how the default encoding is set have not yet been decided.
As of 2023-04, the various error convenience classes do not fully
match what the equivalent C functions do. That is, \exam{throw
PlInstantiationError(A1)} does not result in the same context and
traceback information that would happen from
\exam{Plx_instantiation_error(A1.unwrap()); throw PlFail()}. See
\secref{cpp2-exceptions-notes}.
The Plx_*() wrappers may require small adjustments in whether their
return values require \exam{[[nodiscard]]} or whether their return
values should be treated as an error.
The implementation of \ctype{PlException} is likely to change somewhat
in the future. Currently, to ensure that the exception term has a
sufficient lifetime, it is serialized using PL_record_external(). In
future, if this proves unnecessary, the term will be stored as-is.
The API will not change if this implementation detail changes.
\section{Conclusions}
\label{sec:cpp2-conclusions}
In this document, we presented a high-level interface to Prolog
exploiting automatic type-conversion and exception-handling defined in
C++.
Programming using this interface is much more natural and requires only
little extra resources in terms of time and memory.
Especially the smooth integration between C++ and Prolog exceptions
reduce the coding effort for type checking and reporting in foreign
predicates.
\printindex
\end{document}
|