cpp port 1/2

This commit is contained in:
lovebird 2026-03-23 15:32:00 +01:00
parent a0b182ea51
commit 60de297bd1
121 changed files with 19160 additions and 25 deletions

5842
cache/gadm/boundary_BDI_0.json vendored Normal file

File diff suppressed because it is too large Load Diff

1
cache/gadm/boundary_BDI_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_BDI_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_BDI_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_BDI_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_BDI_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_BFA_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_BFA_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_BFA_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CAF_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CAF_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CAF_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CAF_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CAF_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CAF_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CMR_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CMR_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CMR_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CMR_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CMR_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CMR_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COD_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COD_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COD_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COD_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COD_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COD_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COG_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COG_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COG_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COG_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COG_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COG_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COM_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COM_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COM_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COM_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COM_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_COM_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CPV_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CPV_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CPV_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CPV_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CPV_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_CPV_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_DJI_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_DJI_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_DJI_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_DJI_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_DJI_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_DJI_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_EGY_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_EGY_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_EGY_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_EGY_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_EGY_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_EGY_5.json vendored Normal file

File diff suppressed because one or more lines are too long

4904
cache/gadm/boundary_ESH_0.json vendored Normal file

File diff suppressed because it is too large Load Diff

6369
cache/gadm/boundary_ESH_1.json vendored Normal file

File diff suppressed because it is too large Load Diff

1
cache/gadm/boundary_ESH_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_ESH_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_ESH_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_ESH_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_GNQ_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_GNQ_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_GNQ_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_GNQ_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_GNQ_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_GNQ_5.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_NGA_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_TCD_0.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_TCD_1.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_TCD_2.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_TCD_3.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_TCD_4.json vendored Normal file

File diff suppressed because one or more lines are too long

1
cache/gadm/boundary_TCD_5.json vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"NAME_0":"Spain","GID_0":"ESP","NAME_1":"Cataluña","GID_1":"ESP.6_1","NAME_2":"Barcelona","GID_2":"ESP.6.1_1","NAME_3":"n.a. (36)","GID_3":"ESP.6.1.10_1","NAME_4":"Polinyà","GID_4":"ESP.6.1.10.11_1","isOuter":true,"ghsPopulation":9935,"ghsPopMaxDensity":174,"ghsPopCenter":[2.156823035103133,41.5532654239705],"ghsPopCenters":[[2.1493676642280555,41.546350425100364,174],[2.129559729880891,41.57071884217888,18]],"ghsBuiltWeight":1494490,"ghsBuiltMax":9969,"ghsBuiltCenter":[2.157869498118767,41.55169241209427],"ghsBuiltCenters":[[2.1599882881662746,41.540033247506365,9969],[2.1437876291314844,41.5653033417111,4817]],"population":9935},"geometry":{"type":"MultiPolygon","coordinates":[[[[2.1499299990001646,41.576156616000105],[2.1572461120000526,41.56534957900004],[2.1647050390001823,41.56524658200016],[2.168349982000052,41.56241989200004],[2.1709270480001237,41.55504226700009],[2.174143076000121,41.555412294000064],[2.1735479830001623,41.547824861000095],[2.169739962000108,41.54800415100004],[2.167515040000069,41.54505157600005],[2.168987990000062,41.541278839000086],[2.1582510470001353,41.53675460900001],[2.1561241150000114,41.53972244300013],[2.1501779560001637,41.541023255000084],[2.1449520580001717,41.55155944800009],[2.140552997000043,41.55442047200006],[2.140965939000125,41.558849335000104],[2.1388120660001846,41.559200286000134],[2.1357309810000515,41.563278198000035],[2.1312859060000733,41.56256103599998],[2.1286358840001753,41.56632614200015],[2.126349927000149,41.575153351000154],[2.1369979380001496,41.57410812500018],[2.1499299990001646,41.576156616000105]]]]}}]}

View File

@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"NAME_0":"Spain","GID_0":"ESP","NAME_1":"Cataluña","GID_1":"ESP.6_1","NAME_2":"Barcelona","GID_2":"ESP.6.1_1","NAME_3":"n.a. (36)","GID_3":"ESP.6.1.10_1","NAME_4":"Sabadell","GID_4":"ESP.6.1.10.14_1","isOuter":true,"ghsPopulation":246556,"ghsPopMaxDensity":464,"ghsPopCenter":[2.1034993639754624,41.54892545067783],"ghsPopCenters":[[2.0970918043778557,41.560790554182866,464],[2.1181003006537495,41.53642353524491,438],[2.0961389077736143,41.513864538277495,92],[2.1341717956122084,41.56259565506555,79],[2.067634894654706,41.58155035196711,30]],"ghsBuiltWeight":6756523,"ghsBuiltMax":9772,"ghsBuiltCenter":[2.1039612600195503,41.54677372106804],"ghsBuiltCenters":[[2.108506309226966,41.534618707338204,9772],[2.0728126105825035,41.54183813186422,9354],[2.0949219310237477,41.57162144207828,7663],[2.1365056082475937,41.55988801080525,4793],[2.090112253750799,41.5111576560613,2103]],"population":246556},"geometry":{"type":"MultiPolygon","coordinates":[[[[2.1126289360000214,41.51788330000011],[2.1129229070000974,41.51402664200003],[2.1087799080000877,41.5081062320001],[2.1047799580000515,41.50674819900013],[2.09962391900018,41.510181427000134],[2.091586113000062,41.50925064100005],[2.088428975000113,41.51113510200008],[2.093581914000083,41.51285934500004],[2.096229075000167,41.51982879600018],[2.0998981000000754,41.51900100800003],[2.096834898000111,41.52481460500013],[2.099737882000113,41.52441787700019],[2.1005339620000996,41.526893615000176],[2.0969810480000888,41.53000640800008],[2.1017079350001495,41.532691956000065],[2.0976030820000346,41.53442382800017],[2.0912330160000465,41.542549134000126],[2.0835859780000874,41.538421630000016],[2.082221031000188,41.541309358000035],[2.080183029000125,41.54085159400017],[2.078201055000136,41.54323577800011],[2.071203947000072,41.541107178000175],[2.069530964000137,41.543304444000114],[2.066044093000187,41.54254150300011],[2.060966015000133,41.55284881600005],[2.0659050940001293,41.55362701500019],[2.065020085000185,41.555351258000144],[2.06695795100012,41.556217194],[2.0638189320000038,41.55791473400012],[2.0688359740000237,41.56074523900014],[2.0674951090001628,41.56261444100005],[2.0630440700000463,41.56362915099999],[2.065831901000024,41.56817245400015],[2.061136961000045,41.57050323600009],[2.065834046000134,41.57552719100016],[2.060509919000083,41.57765960600011],[2.062654972000189,41.57917785700005],[2.052241087000141,41.58125686700015],[2.0469100470000967,41.587165832000096],[2.0506730080001603,41.589584349999996],[2.0652298930001507,41.58787155200008],[2.069236993000061,41.585308075000114],[2.0831460950001883,41.5830192570001],[2.0854530330000784,41.58046341000011],[2.0875840180000864,41.58346939100005],[2.1052520279999953,41.58521270800014],[2.1049230100001637,41.586524964000034],[2.1067900649999842,41.58661270100009],[2.1058669089999853,41.590576172],[2.117471933000104,41.59022522000009],[2.1175539490001256,41.57869720500008],[2.125983000000076,41.57640075700016],[2.1286358840001753,41.56632614200015],[2.1312859060000733,41.56256103599998],[2.1357309810000515,41.563278198000035],[2.1388120660001846,41.559200286000134],[2.140965939000125,41.558849335000104],[2.140552997000043,41.55442047200006],[2.1449520580001717,41.55155944800009],[2.1501779560001637,41.541023255000084],[2.145934104000048,41.541019440000014],[2.1454439159999765,41.53830337500011],[2.1403069500000242,41.53744506900017],[2.138531923000187,41.531681062000075],[2.134005070000171,41.531558991000054],[2.1320540910001,41.53445434700012],[2.12661910200012,41.532691956000065],[2.123852015000068,41.53346252500012],[2.1242370600000413,41.52862930399999],[2.126241921999963,41.5274848950001],[2.1096909040001037,41.521175385000106],[2.1126289360000214,41.51788330000011]]]]}}]}

View File

@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"NAME_0":"Spain","GID_0":"ESP","NAME_1":"Cataluña","GID_1":"ESP.6_1","NAME_2":"Barcelona","GID_2":"ESP.6.1_1","NAME_3":"n.a. (36)","GID_3":"ESP.6.1.10_1","NAME_4":"Sentmenat","GID_4":"ESP.6.1.10.19_1","isOuter":true,"ghsPopulation":10045,"ghsPopMaxDensity":122,"ghsPopCenter":[2.1336400135606084,41.60596751957313],"ghsPopCenters":[[2.135481020951598,41.625786064873786,122],[2.126253821221073,41.58425833545556,58],[2.162412766419732,41.59870163073304,12],[2.10373313584779,41.6519717250279,0]],"ghsBuiltWeight":1322074,"ghsBuiltMax":9564,"ghsBuiltCenter":[2.1362730693886283,41.60761050165163],"ghsBuiltCenters":[[2.1435311737162492,41.610437697302835,9564],[2.132099113734979,41.57793977330435,4844],[2.1102607169794267,41.62036883815591,4770],[2.10373313584779,41.6519717250279,36]],"population":10045},"geometry":{"type":"MultiPolygon","coordinates":[[[[2.095913888000098,41.65895462100008],[2.1018829350000487,41.65824127200011],[2.102020024000126,41.65677642900016],[2.108120918000054,41.65452575799998],[2.112935066000148,41.64529800400015],[2.1164240840001867,41.64480972300015],[2.116020918000004,41.64366531400003],[2.1214590070001123,41.6454658510001],[2.1237599850000493,41.64106750500014],[2.1295359130001543,41.64035034200003],[2.133135080000102,41.63731765700004],[2.135119915000132,41.63948059100011],[2.139610053000183,41.63708496100014],[2.1413280960001657,41.63364029000013],[2.1459391120001214,41.63261413600014],[2.149935962000029,41.629631042000085],[2.160909891000074,41.61396789500003],[2.164199113000052,41.59973144600008],[2.154676914000106,41.59239959700017],[2.1517870430001835,41.583812714000146],[2.1533460620001392,41.582458496000186],[2.1513330950000977,41.580539704000046],[2.1526420110000686,41.57800674500015],[2.1491549009999744,41.57711792100014],[2.1499299990001646,41.576156616000105],[2.1369979380001496,41.57410812500018],[2.126349927000149,41.575153351000154],[2.125983000000076,41.57640075700016],[2.1175539490001256,41.57869720500008],[2.1180379400001357,41.60462188700018],[2.112162114000114,41.61101150400009],[2.113347053000041,41.61310196000005],[2.118716954000149,41.61470031700003],[2.1041231150001636,41.623283387000015],[2.0980908870001826,41.63500976600011],[2.1000850210001545,41.64353942900004],[2.097717047000174,41.647598266000045],[2.0986559390000252,41.64868927000009],[2.096810102000177,41.65193176400015],[2.091331006000132,41.65490341100008],[2.090886115000046,41.65961456400004],[2.095913888000098,41.65895462100008]]]]}}]}

View File

@ -0,0 +1,170 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"NAME_0": "Spain",
"GID_0": "ESP",
"NAME_1": "Cataluña",
"GID_1": "ESP.6_1",
"NAME_2": "Barcelona",
"GID_2": "ESP.6.1_1",
"NAME_3": "n.a. (36)",
"GID_3": "ESP.6.1.10_1",
"NAME_4": "Barberà del Vallès",
"GID_4": "ESP.6.1.10.2_1",
"isOuter": true,
"ghsPopulation": 38935,
"ghsPopMaxDensity": 405,
"ghsPopCenter": [
2.129788080182382,
41.51761673511018
],
"ghsPopCenters": [
[
2.1175453262324213,
41.509353091409906,
405
],
[
2.1535256169275745,
41.51657146278524,
88
]
],
"ghsBuiltWeight": 2527777,
"ghsBuiltMax": 9756,
"ghsBuiltCenter": [
2.1366126125083746,
41.51798885634436
],
"ghsBuiltCenters": [
[
2.132119295458612,
41.521083097626835,
9756
]
],
"population": 38935
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[
2.12543606700018,
41.50658035300006
],
[
2.1227169040001854,
41.50552749600007
],
[
2.113327026000036,
41.512413024000125
],
[
2.115808963000177,
41.515300752000144
],
[
2.1096909040001037,
41.521175385000106
],
[
2.126241921999963,
41.5274848950001
],
[
2.1242370600000413,
41.52862930399999
],
[
2.123852015000068,
41.53346252500012
],
[
2.12661910200012,
41.532691956000065
],
[
2.1320540910001,
41.53445434700012
],
[
2.134005070000171,
41.531558991000054
],
[
2.138531923000187,
41.531681062000075
],
[
2.149759054000185,
41.52490997400014
],
[
2.1530909550000388,
41.51936721700008
],
[
2.15890193000007,
41.51953888000014
],
[
2.161067963000164,
41.51604843199999
],
[
2.1626769650000597,
41.516227854000135
],
[
2.160716999999977,
41.508320000000026
],
[
2.1590280710001366,
41.50741586600003
],
[
2.1547319890001404,
41.511379243000135
],
[
2.1496078970001804,
41.50870514000013
],
[
2.1492280960001153,
41.510089875000006
],
[
2.1459391120001214,
41.51061248700012
],
[
2.13905096000002,
41.50743866100004
],
[
2.1358830930001886,
41.5085449230001
],
[
2.1346299650001015,
41.50664520300012
],
[
2.12543606700018,
41.50658035300006
]
]
]
]
}
}
]
}

View File

@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"NAME_0":"Spain","GID_0":"ESP","NAME_1":"Cataluña","GID_1":"ESP.6_1","NAME_2":"Barcelona","GID_2":"ESP.6.1_1","NAME_3":"n.a. (36)","GID_3":"ESP.6.1.10_1","NAME_4":"Castellar del Vallès","GID_4":"ESP.6.1.10.4_1","isOuter":true,"ghsPopulation":27766,"ghsPopMaxDensity":238,"ghsPopCenter":[2.085216509033442,41.610168841270294],"ghsPopCenters":[[2.1062675908905466,41.60050712753617,238],[2.067815986692841,41.59057712859825,64],[2.072182801153131,41.629397643810066,59],[2.088240384014767,41.65468081331711,6]],"ghsBuiltWeight":2151587,"ghsBuiltMax":9997,"ghsBuiltCenter":[2.0852015483210837,41.60646652277981],"ghsBuiltCenters":[[2.0918863135624823,41.59870163073304,9997],[2.07830729663122,41.63662102843702,3518],[2.0548300417222327,41.59870163073304,2267]],"population":27766},"geometry":{"type":"MultiPolygon","coordinates":[[[[2.0441300870001555,41.60391616900006],[2.0462679870001352,41.60871887200011],[2.042983055000093,41.61059188900009],[2.0423889150001173,41.61464309700011],[2.0392909050001435,41.61494064300007],[2.0350658890001228,41.62459945600011],[2.0286750800000846,41.62649917700003],[2.0253291130001116,41.629753113000106],[2.025146962000065,41.63235473700007],[2.031199933000039,41.636112213000104],[2.0406999580001184,41.65294265700004],[2.0496680730000776,41.65665435800008],[2.066256046000035,41.65981292800012],[2.071317911000108,41.66342163000007],[2.080017090000183,41.66640472500012],[2.085911036000084,41.66639709499998],[2.0886569030001283,41.663600921000125],[2.0882859230001714,41.661178590000134],[2.090886115000046,41.65961456400004],[2.091331006000132,41.65490341100008],[2.096810102000177,41.65193176400015],[2.0986559390000252,41.64868927000009],[2.097717047000174,41.647598266000045],[2.1000850210001545,41.64353942900004],[2.0980908870001826,41.63500976600011],[2.1041231150001636,41.623283387000015],[2.118716954000149,41.61470031700003],[2.113347053000041,41.61310196000005],[2.112162114000114,41.61101150400009],[2.1180379400001357,41.60462188700018],[2.117471933000104,41.59022522000009],[2.1058669089999853,41.590576172],[2.1067900649999842,41.58661270100009],[2.1049230100001637,41.586524964000034],[2.1052520279999953,41.58521270800014],[2.0875840180000864,41.58346939100005],[2.0854530330000784,41.58046341000011],[2.0831460950001883,41.5830192570001],[2.0799300670000207,41.582817078000176],[2.0775599480001006,41.58483886800013],[2.069236993000061,41.585308075000114],[2.0652298930001507,41.58787155200008],[2.062088966000033,41.587745667000036],[2.05925011700009,41.595256806000066],[2.0441300870001555,41.60391616900006]]]]}}]}

29
cpp/.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
# Build output
/build/
# Compiled objects
*.o
*.obj
*.exe
*.out
*.app
config/
# CMake generated
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
Makefile
# IDE / Editor
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Logs
*.log

69
cpp/CMakeLists.txt Normal file
View File

@ -0,0 +1,69 @@
cmake_minimum_required(VERSION 3.20)
project(polymech-cli
VERSION 0.1.0
DESCRIPTION "Polymech C++ CLI"
LANGUAGES CXX C
)
# C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Dependencies
include(FetchContent)
FetchContent_Declare(
cli11
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
GIT_TAG v2.4.2
GIT_SHALLOW TRUE
)
FetchContent_Declare(
tomlplusplus
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
GIT_TAG v3.4.0
GIT_SHALLOW TRUE
)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.7.1
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(cli11 tomlplusplus Catch2)
# Packages
add_subdirectory(packages/logger)
add_subdirectory(packages/html)
add_subdirectory(packages/postgres)
add_subdirectory(packages/http)
add_subdirectory(packages/json)
add_subdirectory(packages/polymech)
# Sources
add_executable(${PROJECT_NAME}
src/main.cpp
)
target_link_libraries(${PROJECT_NAME} PRIVATE CLI11::CLI11 tomlplusplus::tomlplusplus logger html postgres http json polymech)
# Compiler warnings
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /permissive-)
else()
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic)
endif()
# Install
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION bin
)
# Tests
enable_testing()
add_subdirectory(tests)

36
cpp/CMakePresets.json Normal file
View File

@ -0,0 +1,36 @@
{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "dev",
"displayName": "Dev (Debug)",
"binaryDir": "${sourceDir}/build/dev",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "release",
"displayName": "Release",
"binaryDir": "${sourceDir}/build/release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
],
"buildPresets": [
{
"name": "dev",
"configurePreset": "dev"
},
{
"name": "release",
"configurePreset": "release"
}
]
}

9
cpp/LICENSE Normal file
View File

@ -0,0 +1,9 @@
Copyright (c) <year> <owner> All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

33
cpp/README.md Normal file
View File

@ -0,0 +1,33 @@
# polymech-cli
Cross-platform C++ CLI built with CMake.
## Prerequisites
| Tool | Version |
|------|---------|
| CMake | ≥ 3.20 |
| C++ compiler | C++17 (MSVC, GCC, or Clang) |
## Build
```bash
# Debug
cmake --preset dev
cmake --build --preset dev
# Release
cmake --preset release
cmake --build --preset release
```
## Usage
```bash
polymech-cli --help
polymech-cli --version
```
## License
BSD-3-Clause

12
cpp/config.toml Normal file
View File

@ -0,0 +1,12 @@
[project]
name = "polymech"
version = "0.1.0"
description = "Polymech C++ CLI"
[database]
host = "localhost"
port = 5432
name = "polymech"
[logging]
level = "debug"

6
cpp/package-lock.json generated Normal file
View File

@ -0,0 +1,6 @@
{
"name": "mono-cpp",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

28
cpp/package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "mono-cpp",
"version": "1.0.0",
"description": "Cross-platform C++ CLI built with CMake.",
"directories": {
"test": "tests"
},
"scripts": {
"config": "cmake --preset dev",
"config:release": "cmake --preset release",
"build": "cmake --build --preset dev",
"build:release": "cmake --build --preset release",
"test": "ctest --test-dir build/dev -C Debug --output-on-failure",
"test:release": "ctest --test-dir build/release -C Release --output-on-failure",
"clean": "cmake -E rm -rf build/dev",
"clean:release": "cmake -E rm -rf build/release",
"clean:all": "cmake -E rm -rf build",
"rebuild": "npm run clean && npm run config && npm run build",
"run": ".\\build\\dev\\Debug\\polymech-cli.exe --help"
},
"repository": {
"type": "git",
"url": "https://git.polymech.info/polymech/mono-cpp.git"
},
"keywords": [],
"author": "",
"license": "ISC"
}

View File

@ -0,0 +1,26 @@
include(FetchContent)
FetchContent_Declare(
lexbor
GIT_REPOSITORY https://github.com/lexbor/lexbor.git
GIT_TAG v2.4.0
GIT_SHALLOW TRUE
)
# Build lexbor as static
set(LEXBOR_BUILD_SHARED OFF CACHE BOOL "" FORCE)
set(LEXBOR_BUILD_STATIC ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(lexbor)
add_library(html STATIC
src/html.cpp
)
target_include_directories(html
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(html
PUBLIC lexbor_static
)

View File

@ -0,0 +1,21 @@
#pragma once
#include <string>
#include <vector>
namespace html {
/// Parsed element — tag name + text content.
struct Element {
std::string tag;
std::string text;
};
/// Parse an HTML string and return all elements with their text content.
std::vector<Element> parse(const std::string &html_str);
/// Extract the text content of all elements matching a CSS selector.
std::vector<std::string> select(const std::string &html_str,
const std::string &selector);
} // namespace html

View File

@ -0,0 +1,129 @@
#include "html/html.h"
#include <lexbor/css/css.h>
#include <lexbor/html/html.h>
#include <lexbor/selectors/selectors.h>
namespace html {
// ── helpers ─────────────────────────────────────────────────────────────────
static std::string node_text(lxb_dom_node_t *node) {
size_t len = 0;
lxb_char_t *text = lxb_dom_node_text_content(node, &len);
if (!text)
return {};
std::string result(reinterpret_cast<const char *>(text), len);
lxb_dom_document_destroy_text(node->owner_document, text);
return result;
}
static std::string tag_name(lxb_dom_element_t *el) {
size_t len = 0;
const lxb_char_t *name = lxb_dom_element_qualified_name(el, &len);
if (!name)
return {};
return std::string(reinterpret_cast<const char *>(name), len);
}
// ── walk tree recursively ───────────────────────────────────────────────────
static void walk(lxb_dom_node_t *node, std::vector<Element> &out) {
if (!node)
return;
if (node->type == LXB_DOM_NODE_TYPE_ELEMENT) {
auto *el = lxb_dom_interface_element(node);
auto txt = node_text(node);
if (!txt.empty()) {
out.push_back({tag_name(el), txt});
}
}
auto *child = node->first_child;
while (child) {
walk(child, out);
child = child->next;
}
}
// ── public API ──────────────────────────────────────────────────────────────
std::vector<Element> parse(const std::string &html_str) {
auto *doc = lxb_html_document_create();
if (!doc)
return {};
auto status = lxb_html_document_parse(
doc, reinterpret_cast<const lxb_char_t *>(html_str.c_str()),
html_str.size());
std::vector<Element> result;
if (status == LXB_STATUS_OK) {
auto *body = lxb_dom_interface_node(lxb_html_document_body_element(doc));
walk(body, result);
}
lxb_html_document_destroy(doc);
return result;
}
// ── CSS selector callback ───────────────────────────────────────────────────
struct SelectCtx {
std::vector<std::string> *out;
};
static lxb_status_t select_cb(lxb_dom_node_t *node,
lxb_css_selector_specificity_t spec, void *ctx) {
(void)spec;
auto *sctx = static_cast<SelectCtx *>(ctx);
auto txt = node_text(node);
if (!txt.empty()) {
sctx->out->push_back(txt);
}
return LXB_STATUS_OK;
}
std::vector<std::string> select(const std::string &html_str,
const std::string &selector) {
std::vector<std::string> result;
// Parse document
auto *doc = lxb_html_document_create();
if (!doc)
return result;
auto status = lxb_html_document_parse(
doc, reinterpret_cast<const lxb_char_t *>(html_str.c_str()),
html_str.size());
if (status != LXB_STATUS_OK) {
lxb_html_document_destroy(doc);
return result;
}
// Set up CSS parser + selectors engine
auto *css_parser = lxb_css_parser_create();
lxb_css_parser_init(css_parser, nullptr);
auto *selectors = lxb_selectors_create();
lxb_selectors_init(selectors);
auto *list = lxb_css_selectors_parse(
css_parser, reinterpret_cast<const lxb_char_t *>(selector.c_str()),
selector.size());
if (list) {
SelectCtx ctx{&result};
lxb_selectors_find(
selectors, lxb_dom_interface_node(lxb_html_document_body_element(doc)),
list, select_cb, &ctx);
lxb_css_selector_list_destroy_memory(list);
}
lxb_selectors_destroy(selectors, true);
lxb_css_parser_destroy(css_parser, true);
lxb_html_document_destroy(doc);
return result;
}
} // namespace html

View File

@ -0,0 +1,43 @@
include(FetchContent)
# Work around curl's old cmake_minimum_required for CMake 4.x
set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "" FORCE)
FetchContent_Declare(
CURL
URL https://github.com/curl/curl/releases/download/curl-8_12_1/curl-8.12.1.tar.xz
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
# Minimal curl build static, SChannel TLS, no optional deps
set(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
# TLS backend: Windows native SChannel
set(CURL_USE_OPENSSL OFF CACHE BOOL "" FORCE)
set(CURL_USE_SCHANNEL ON CACHE BOOL "" FORCE)
# Disable optional compression/protocol deps
set(CURL_ZLIB OFF CACHE BOOL "" FORCE)
set(CURL_BROTLI OFF CACHE BOOL "" FORCE)
set(CURL_ZSTD OFF CACHE BOOL "" FORCE)
set(USE_NGHTTP2 OFF CACHE BOOL "" FORCE)
set(CURL_USE_LIBSSH2 OFF CACHE BOOL "" FORCE)
set(CURL_USE_LIBPSL OFF CACHE BOOL "" FORCE)
set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE)
set(CURL_DISABLE_LDAPS ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(CURL)
add_library(http STATIC
src/http.cpp
)
target_include_directories(http
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(http
PUBLIC CURL::libcurl
)

View File

@ -0,0 +1,19 @@
#pragma once
#include <string>
namespace http {
struct Response {
long status_code;
std::string body;
};
/// Perform an HTTP GET request. Returns the response body and status code.
Response get(const std::string &url);
/// Perform an HTTP POST request with a body. Returns the response and status.
Response post(const std::string &url, const std::string &body,
const std::string &content_type = "application/json");
} // namespace http

View File

@ -0,0 +1,77 @@
#include "http/http.h"
#include <curl/curl.h>
namespace http {
static size_t write_cb(void *contents, size_t size, size_t nmemb, void *userp) {
auto *out = static_cast<std::string *>(userp);
out->append(static_cast<char *>(contents), size * nmemb);
return size * nmemb;
}
Response get(const std::string &url) {
Response resp{};
CURL *curl = curl_easy_init();
if (!curl) {
resp.status_code = -1;
resp.body = "curl_easy_init failed";
return resp;
}
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp.body);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
resp.status_code = -1;
resp.body = curl_easy_strerror(res);
} else {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &resp.status_code);
}
curl_easy_cleanup(curl);
return resp;
}
Response post(const std::string &url, const std::string &body,
const std::string &content_type) {
Response resp{};
CURL *curl = curl_easy_init();
if (!curl) {
resp.status_code = -1;
resp.body = "curl_easy_init failed";
return resp;
}
struct curl_slist *headers = nullptr;
headers =
curl_slist_append(headers, ("Content-Type: " + content_type).c_str());
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp.body);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
resp.status_code = -1;
resp.body = curl_easy_strerror(res);
} else {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &resp.status_code);
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return resp;
}
} // namespace http

View File

@ -0,0 +1,28 @@
include(FetchContent)
# RapidJSON use master for CMake 4.x compatibility (v1.1.0 is from 2016)
FetchContent_Declare(
rapidjson
GIT_REPOSITORY https://github.com/Tencent/rapidjson.git
GIT_TAG master
GIT_SHALLOW TRUE
)
set(RAPIDJSON_BUILD_DOC OFF CACHE BOOL "" FORCE)
set(RAPIDJSON_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(RAPIDJSON_BUILD_TESTS OFF CACHE BOOL "" FORCE)
FetchContent_GetProperties(rapidjson)
if(NOT rapidjson_POPULATED)
FetchContent_Populate(rapidjson)
# Don't add_subdirectory just use the headers
endif()
add_library(json STATIC
src/json.cpp
)
target_include_directories(json
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
PUBLIC ${rapidjson_SOURCE_DIR}/include
)

View File

@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <vector>
namespace json {
/// Parse a JSON string and return a pretty-printed version.
std::string prettify(const std::string &json_str);
/// Extract a string value by key from a JSON object (top-level only).
std::string get_string(const std::string &json_str, const std::string &key);
/// Extract an int value by key from a JSON object (top-level only).
int get_int(const std::string &json_str, const std::string &key);
/// Check if a JSON string is valid.
bool is_valid(const std::string &json_str);
/// Get all top-level keys from a JSON object.
std::vector<std::string> keys(const std::string &json_str);
} // namespace json

View File

@ -0,0 +1,62 @@
#include "json/json.h"
#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
#include <rapidjson/stringbuffer.h>
namespace json {
std::string prettify(const std::string &json_str) {
rapidjson::Document doc;
doc.Parse(json_str.c_str());
if (doc.HasParseError()) {
return {};
}
rapidjson::StringBuffer buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
doc.Accept(writer);
return std::string(buffer.GetString(), buffer.GetSize());
}
std::string get_string(const std::string &json_str, const std::string &key) {
rapidjson::Document doc;
doc.Parse(json_str.c_str());
if (doc.HasParseError() || !doc.IsObject())
return {};
auto it = doc.FindMember(key.c_str());
if (it == doc.MemberEnd() || !it->value.IsString())
return {};
return std::string(it->value.GetString(), it->value.GetStringLength());
}
int get_int(const std::string &json_str, const std::string &key) {
rapidjson::Document doc;
doc.Parse(json_str.c_str());
if (doc.HasParseError() || !doc.IsObject())
return 0;
auto it = doc.FindMember(key.c_str());
if (it == doc.MemberEnd() || !it->value.IsInt())
return 0;
return it->value.GetInt();
}
bool is_valid(const std::string &json_str) {
rapidjson::Document doc;
doc.Parse(json_str.c_str());
return !doc.HasParseError();
}
std::vector<std::string> keys(const std::string &json_str) {
std::vector<std::string> result;
rapidjson::Document doc;
doc.Parse(json_str.c_str());
if (doc.HasParseError() || !doc.IsObject())
return result;
for (auto it = doc.MemberBegin(); it != doc.MemberEnd(); ++it) {
result.emplace_back(it->name.GetString(), it->name.GetStringLength());
}
return result;
}
} // namespace json

View File

@ -0,0 +1,21 @@
include(FetchContent)
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.15.1
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(spdlog)
add_library(logger STATIC
src/logger.cpp
)
target_include_directories(logger
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(logger
PUBLIC spdlog::spdlog
)

View File

@ -0,0 +1,16 @@
#pragma once
#include <string>
namespace logger {
/// Initialize the default logger (call once at startup).
void init(const std::string &app_name = "polymech");
/// Log at various levels.
void info(const std::string &msg);
void warn(const std::string &msg);
void error(const std::string &msg);
void debug(const std::string &msg);
} // namespace logger

Some files were not shown because too many files have changed in this diff Show More