File indexing completed on 2022-02-21 23:14:16
0001 #include "RecoTracker/MkFitCore/interface/IterationConfig.h"
0002 #include "RecoTracker/MkFitCore/interface/Config.h"
0003 #include "RecoTracker/MkFitCore/interface/Track.h"
0004
0005 #include "nlohmann/json.hpp"
0006
0007 #include <fstream>
0008 #include <regex>
0009 #include <iostream>
0010 #include <iomanip>
0011
0012
0013 #define ITCONF_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
0014 inline void to_json(nlohmann::json &nlohmann_json_j, const Type &nlohmann_json_t) { \
0015 NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) \
0016 } \
0017 inline void from_json(const nlohmann::json &nlohmann_json_j, Type &nlohmann_json_t) { \
0018 NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) \
0019 } \
0020 inline void to_json(nlohmann::ordered_json &nlohmann_json_j, const Type &nlohmann_json_t) { \
0021 NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) \
0022 } \
0023 inline void from_json(const nlohmann::ordered_json &nlohmann_json_j, Type &nlohmann_json_t) { \
0024 NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) \
0025 }
0026
0027 namespace mkfit {
0028
0029
0030
0031 ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::LayerControl,
0032 m_layer)
0033
0034 ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::SteeringParams,
0035 m_layer_plan,
0036 m_region,
0037 m_fwd_search_pickup,
0038 m_bkw_fit_last,
0039 m_bkw_search_pickup
0040
0041 )
0042
0043 ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::IterationLayerConfig,
0044 m_select_min_dphi,
0045 m_select_max_dphi,
0046 m_select_min_dq,
0047 m_select_max_dq,
0048 c_dp_sf,
0049 c_dp_0,
0050 c_dp_1,
0051 c_dp_2,
0052 c_dq_sf,
0053 c_dq_0,
0054 c_dq_1,
0055 c_dq_2,
0056 c_c2_sf,
0057 c_c2_0,
0058 c_c2_1,
0059 c_c2_2)
0060
0061 ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::IterationParams,
0062 nlayers_per_seed,
0063 maxCandsPerSeed,
0064 maxHolesPerCand,
0065 maxConsecHoles,
0066 chi2Cut_min,
0067 chi2CutOverlap,
0068 pTCutOverlap,
0069 c_ptthr_hpt,
0070 c_drmax_bh,
0071 c_dzmax_bh,
0072 c_drmax_eh,
0073 c_dzmax_eh,
0074 c_drmax_bl,
0075 c_dzmax_bl,
0076 c_drmax_el,
0077 c_dzmax_el,
0078 minHitsQF,
0079 fracSharedHits,
0080 drth_central,
0081 drth_obarrel,
0082 drth_forward
0083
0084 )
0085
0086 ITCONF_DEFINE_TYPE_NON_INTRUSIVE(
0087 mkfit::IterationConfig,
0088 m_iteration_index,
0089 m_track_algorithm,
0090 m_requires_seed_hit_sorting,
0091 m_requires_quality_filter,
0092 m_requires_dupclean_tight,
0093 m_backward_search,
0094 m_backward_drop_seed_hits,
0095 m_backward_fit_min_hits,
0096 m_params,
0097 m_backward_params,
0098 m_n_regions,
0099 m_region_order,
0100 m_steering_params,
0101 m_layer_configs
0102
0103 )
0104
0105 ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::IterationsInfo,
0106 m_iterations)
0107
0108
0109
0110
0111
0112
0113
0114 ConfigJsonPatcher::ConfigJsonPatcher(bool verbose) : m_verbose(verbose) {}
0115
0116 ConfigJsonPatcher::~ConfigJsonPatcher() = default;
0117
0118 std::string ConfigJsonPatcher::get_abs_path() const {
0119 std::string s;
0120 s.reserve(64);
0121 for (auto &p : m_path_stack)
0122 s += p;
0123 return s;
0124 }
0125
0126 std::string ConfigJsonPatcher::exc_hdr(const char *func) const {
0127 std::string s;
0128 s.reserve(128);
0129 s = "ConfigJsonPatcher";
0130 if (func) {
0131 s += "::";
0132 s += func;
0133 }
0134 s += " '";
0135 s += get_abs_path();
0136 s += "' ";
0137 return s;
0138 }
0139
0140 template <class T>
0141 void ConfigJsonPatcher::load(const T &o) {
0142 m_json = std::make_unique<nlohmann::json>();
0143 *m_json = o;
0144 cd_top();
0145 }
0146 template void ConfigJsonPatcher::load<IterationsInfo>(const IterationsInfo &o);
0147 template void ConfigJsonPatcher::load<IterationConfig>(const IterationConfig &o);
0148
0149 template <class T>
0150 void ConfigJsonPatcher::save(T &o) {
0151 from_json(*m_json, o);
0152 }
0153 template void ConfigJsonPatcher::save<IterationConfig>(IterationConfig &o);
0154
0155
0156
0157 template <>
0158 void ConfigJsonPatcher::save<IterationsInfo>(IterationsInfo &o) {
0159 auto &itc_arr = m_json->at("m_iterations");
0160 for (int i = 0; i < o.size(); ++i) {
0161 from_json(itc_arr[i], o[i]);
0162 }
0163 }
0164
0165 void ConfigJsonPatcher::cd(const std::string &path) {
0166 nlohmann::json::json_pointer jp(path);
0167 m_json_stack.push_back(m_current);
0168 m_path_stack.push_back(path);
0169 m_current = &m_current->at(jp);
0170 }
0171
0172 void ConfigJsonPatcher::cd_up(const std::string &path) {
0173 if (m_json_stack.empty())
0174 throw std::runtime_error("JSON stack empty on cd_up");
0175
0176 m_current = m_json_stack.back();
0177 m_json_stack.pop_back();
0178 m_path_stack.pop_back();
0179 if (!path.empty())
0180 cd(path);
0181 }
0182
0183 void ConfigJsonPatcher::cd_top(const std::string &path) {
0184 m_current = m_json.get();
0185 m_json_stack.clear();
0186 m_path_stack.clear();
0187 if (!path.empty())
0188 cd(path);
0189 }
0190
0191 template <typename T>
0192 void ConfigJsonPatcher::replace(const std::string &path, T val) {
0193 nlohmann::json::json_pointer jp(path);
0194 m_current->at(jp) = val;
0195 }
0196 template void ConfigJsonPatcher::replace<int>(const std::string &path, int val);
0197 template void ConfigJsonPatcher::replace<float>(const std::string &path, float val);
0198 template void ConfigJsonPatcher::replace<double>(const std::string &path, double val);
0199
0200 template <typename T>
0201 void ConfigJsonPatcher::replace(int first, int last, const std::string &path, T val) {
0202 nlohmann::json::json_pointer jp(path);
0203 for (int i = first; i <= last; ++i) {
0204 m_current->at(i).at(jp) = val;
0205 }
0206 }
0207 template void ConfigJsonPatcher::replace<int>(int first, int last, const std::string &path, int val);
0208 template void ConfigJsonPatcher::replace<float>(int first, int last, const std::string &path, float val);
0209 template void ConfigJsonPatcher::replace<double>(int first, int last, const std::string &path, double val);
0210
0211 nlohmann::json &ConfigJsonPatcher::get(const std::string &path) {
0212 nlohmann::json::json_pointer jp(path);
0213 return m_current->at(jp);
0214 }
0215
0216 int ConfigJsonPatcher::replace(const nlohmann::json &j) {
0217 if (j.is_null())
0218 throw std::runtime_error(exc_hdr(__func__) + "null not expected");
0219
0220 if (j.is_boolean() || j.is_number() || j.is_string()) {
0221 throw std::runtime_error(exc_hdr(__func__) + "value not expected on this parsing level");
0222 }
0223
0224 int n_replaced = 0;
0225
0226 if (j.is_object()) {
0227 static const std::regex index_range_re("^\\[(\\d+)..(\\d+)\\]$", std::regex::optimize);
0228
0229 for (auto &[key, value] : j.items()) {
0230 std::smatch m;
0231 std::regex_search(key, m, index_range_re);
0232
0233 if (m.size() == 3) {
0234 if (!m_current->is_array())
0235 throw std::runtime_error(exc_hdr(__func__) + "array range encountered when current json is not an array");
0236 int first = std::stoi(m.str(1));
0237 int last = std::stoi(m.str(2));
0238 for (int i = first; i <= last; ++i) {
0239 std::string s("/");
0240 s += std::to_string(i);
0241 cd(s);
0242 if (value.is_array()) {
0243 for (auto &el : value)
0244 n_replaced += replace(el);
0245 } else {
0246 n_replaced += replace(value);
0247 }
0248 cd_up();
0249 }
0250 } else if (value.is_array() || value.is_object()) {
0251 std::string s("/");
0252 s += key;
0253 cd(s);
0254 n_replaced += replace(value);
0255 cd_up();
0256 } else if (value.is_number() || value.is_boolean() || value.is_string()) {
0257 std::string s("/");
0258 s += key;
0259 nlohmann::json::json_pointer jp(s);
0260 if (m_current->at(jp) != value) {
0261 if (m_verbose)
0262 std::cout << " " << get_abs_path() << s << ": " << m_current->at(jp) << " -> " << value << "\n";
0263
0264 m_current->at(jp) = value;
0265 ++n_replaced;
0266 }
0267 } else {
0268 throw std::runtime_error(exc_hdr(__func__) + "unexpected value type");
0269 }
0270 }
0271 } else if (j.is_array() && j.empty()) {
0272 } else if (j.is_array()) {
0273
0274
0275
0276
0277
0278
0279
0280
0281
0282
0283
0284
0285 static const std::regex index_re("^(?:\\[\\d+..\\d+\\]|\\d+(?:/.*)?)$", std::regex::optimize);
0286
0287 bool has_index = false, has_plain = false;
0288 for (int i = 0; i < (int)j.size(); ++i) {
0289 const nlohmann::json &el = j[i];
0290
0291 if (!el.is_object())
0292 throw std::runtime_error(exc_hdr(__func__) + "array elements expected to be objects");
0293
0294 for (nlohmann::json::const_iterator it = el.begin(); it != el.end(); ++it) {
0295 if (std::regex_search(it.key(), index_re)) {
0296 has_index = true;
0297 if (has_plain)
0298 throw std::runtime_error(exc_hdr(__func__) + "indexed array entry following plain one");
0299 } else {
0300 has_plain = true;
0301 if (has_index)
0302 throw std::runtime_error(exc_hdr(__func__) + "plain array entry following indexed one");
0303 }
0304 }
0305 }
0306 if (has_index) {
0307 for (auto &element : j) {
0308 n_replaced += replace(element);
0309 }
0310 } else {
0311 if (m_current && !m_current->is_array())
0312 throw std::runtime_error(exc_hdr(__func__) + "plain array detected when current is not an array");
0313 if (m_current->size() != j.size())
0314 throw std::runtime_error(exc_hdr(__func__) + "plain array of different size than at current pos");
0315
0316 std::string s;
0317 for (int i = 0; i < (int)j.size(); ++i) {
0318 s = "/";
0319 s += std::to_string(i);
0320 cd(s);
0321 n_replaced += replace(j[i]);
0322 cd_up();
0323 }
0324 }
0325 } else {
0326 throw std::runtime_error(exc_hdr(__func__) + "unexpected json type");
0327 }
0328
0329 return n_replaced;
0330 }
0331
0332 std::string ConfigJsonPatcher::dump(int indent) { return m_json->dump(indent); }
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351 namespace {
0352
0353 void open_ofstream(std::ofstream &ofs, const std::string &fname, const char *pfx = nullptr) {
0354 ofs.open(fname, std::ofstream::trunc);
0355 if (!ofs) {
0356 char m[2048];
0357 snprintf(m, 2048, "%s%sError opening %s for write: %m", pfx ? pfx : "", pfx ? " " : "", fname.c_str());
0358 throw std::runtime_error(m);
0359 }
0360 }
0361
0362
0363 void open_ifstream(std::ifstream &ifs, const std::string &fname, const char *pfx = nullptr) {
0364 ifs.open(fname);
0365 if (!ifs) {
0366 char m[2048];
0367 snprintf(m, 2048, "%s%sError opening %s for read: %m", pfx ? pfx : "", pfx ? " " : "", fname.c_str());
0368 throw std::runtime_error(m);
0369 }
0370 }
0371
0372
0373 bool skipws_ifstream(std::ifstream &ifs) {
0374 while (std::isspace(ifs.peek()))
0375 ifs.get();
0376 return !ifs.eof();
0377 }
0378 }
0379
0380 void ConfigJson::patch_Files(IterationsInfo &its_info,
0381 const std::vector<std::string> &fnames,
0382 ConfigJsonPatcher::PatchReport *report) {
0383 ConfigJsonPatcher cjp(m_verbose);
0384 cjp.load(its_info);
0385
0386 ConfigJsonPatcher::PatchReport rep;
0387
0388 for (auto &fname : fnames) {
0389 std::ifstream ifs;
0390 open_ifstream(ifs, fname, __func__);
0391
0392 if (m_verbose) {
0393 printf("%s begin reading from file %s.\n", __func__, fname.c_str());
0394 }
0395
0396 int n_read = 0, n_tot_replaced = 0;
0397 while (skipws_ifstream(ifs)) {
0398 nlohmann::json j;
0399 ifs >> j;
0400 ++n_read;
0401
0402 if (m_verbose) {
0403 std::cout << " Read JSON entity " << n_read << " -- applying patch:\n";
0404
0405 }
0406
0407 int n_replaced = cjp.replace(j);
0408
0409 if (m_verbose) {
0410 std::cout << " Replaced " << n_replaced << " entries.\n";
0411 }
0412 cjp.cd_top();
0413 n_tot_replaced += n_replaced;
0414 }
0415
0416 if (m_verbose) {
0417 printf("%s read %d JSON entities from file %s, replaced %d parameters.\n",
0418 __func__,
0419 n_read,
0420 fname.c_str(),
0421 n_tot_replaced);
0422 }
0423
0424 ifs.close();
0425
0426 rep.inc_counts(1, n_read, n_tot_replaced);
0427 }
0428
0429 if (rep.n_replacements > 0) {
0430 cjp.save(its_info);
0431 }
0432
0433 if (report)
0434 report->inc_counts(rep);
0435 }
0436
0437 std::unique_ptr<IterationConfig> ConfigJson::patchLoad_File(const IterationsInfo &its_info,
0438 const std::string &fname,
0439 ConfigJsonPatcher::PatchReport *report) {
0440 ConfigJsonPatcher::PatchReport rep;
0441
0442 std::ifstream ifs;
0443 open_ifstream(ifs, fname, __func__);
0444
0445 if (m_verbose) {
0446 printf("%s begin reading from file %s.\n", __func__, fname.c_str());
0447 }
0448
0449 if (!skipws_ifstream(ifs))
0450 throw std::runtime_error("empty file");
0451
0452 nlohmann::json j;
0453 ifs >> j;
0454 int track_algo = j["m_track_algorithm"];
0455
0456 int iii = -1;
0457 for (int i = 0; i < its_info.size(); ++i) {
0458 if (its_info[i].m_track_algorithm == track_algo) {
0459 iii = i;
0460 break;
0461 }
0462 }
0463 if (iii == -1)
0464 throw std::runtime_error("matching IterationConfig not found");
0465
0466 if (m_verbose) {
0467 std::cout << " Read JSON entity, Iteration index is " << iii << " -- cloning and applying JSON patch:\n";
0468 }
0469
0470 IterationConfig *icp = new IterationConfig(its_info[iii]);
0471 IterationConfig &ic = *icp;
0472
0473 ConfigJsonPatcher cjp(m_verbose);
0474 cjp.load(ic);
0475
0476 int n_replaced = cjp.replace(j);
0477
0478 cjp.cd_top();
0479
0480 if (m_verbose) {
0481 printf("%s read 1 JSON entity from file %s, replaced %d parameters.\n", __func__, fname.c_str(), n_replaced);
0482 }
0483
0484 ifs.close();
0485
0486 rep.inc_counts(1, 1, n_replaced);
0487
0488 if (rep.n_replacements > 0) {
0489 cjp.save(ic);
0490 }
0491
0492 if (report)
0493 report->inc_counts(rep);
0494
0495 return std::unique_ptr<IterationConfig>(icp);
0496 }
0497
0498 std::unique_ptr<IterationConfig> ConfigJson::load_File(const std::string &fname) {
0499 std::ifstream ifs;
0500 open_ifstream(ifs, fname, __func__);
0501
0502 if (m_verbose) {
0503 printf("%s begin reading from file %s.\n", __func__, fname.c_str());
0504 }
0505
0506 if (!skipws_ifstream(ifs))
0507 throw std::runtime_error("empty file");
0508
0509 nlohmann::json j;
0510 ifs >> j;
0511
0512 if (m_verbose) {
0513 std::cout << " Read JSON entity, iteration index is " << j["m_iteration_index"] << ", track algorithm is "
0514 << j["m_track_algorithm"] << ". Instantiating IterationConfig object and over-laying it with JSON.\n";
0515 }
0516
0517 IterationConfig *icp = new IterationConfig();
0518
0519 from_json(j, *icp);
0520
0521 return std::unique_ptr<IterationConfig>(icp);
0522 }
0523
0524
0525
0526
0527
0528 void ConfigJson::save_Iterations(IterationsInfo &its_info,
0529 const std::string &fname_fmt,
0530 bool include_iter_info_preamble) {
0531 bool has_pct_d = fname_fmt.find("%d") != std::string::npos;
0532 bool has_pct_s = fname_fmt.find("%s") != std::string::npos;
0533
0534 assert((has_pct_d || has_pct_s) && "JSON save filename-format must include a %d or %s substring");
0535 assert(!(has_pct_d && has_pct_s) && "JSON save filename-format must include only one of %d or %s substrings");
0536
0537 for (int ii = 0; ii < its_info.size(); ++ii) {
0538 const IterationConfig &itconf = its_info[ii];
0539
0540 char fname[1024];
0541 if (has_pct_d)
0542 snprintf(fname, 1024, fname_fmt.c_str(), ii);
0543 else
0544 snprintf(fname, 1024, fname_fmt.c_str(), TrackBase::algoint_to_cstr(itconf.m_track_algorithm));
0545
0546 std::ofstream ofs;
0547 open_ofstream(ofs, fname, __func__);
0548
0549 if (include_iter_info_preamble) {
0550 ofs << "{ \"m_iterations/" << ii << "\": ";
0551 }
0552
0553 nlohmann::ordered_json j;
0554 to_json(j, itconf);
0555
0556 ofs << std::setw(1);
0557 ofs << j;
0558
0559 if (include_iter_info_preamble) {
0560 ofs << " }";
0561 }
0562
0563 ofs << "\n";
0564 ofs.close();
0565 }
0566 }
0567
0568 void ConfigJson::dump(IterationsInfo &its_info) {
0569 nlohmann::ordered_json j = its_info;
0570 std::cout << j.dump(3) << "\n";
0571 }
0572
0573
0574
0575
0576
0577 void ConfigJson::test_Direct(IterationConfig &it_cfg) {
0578 using nlohmann::json;
0579
0580 std::string lojz("/m_select_max_dphi");
0581
0582 json j = it_cfg;
0583 std::cout << j.dump(1) << "\n";
0584
0585 std::cout << "Layer 43, m_select_max_dphi = " << j["/m_layer_configs/43/m_select_max_dphi"_json_pointer] << "\n";
0586 std::cout << "Patching it to pi ...\n";
0587 json p = R"([
0588 { "op": "replace", "path": "/m_layer_configs/43/m_select_max_dphi", "value": 3.141 }
0589 ])"_json;
0590 j = j.patch(p);
0591 std::cout << "Layer 43, m_select_max_dphi = " << j["/m_layer_configs/43/m_select_max_dphi"_json_pointer] << "\n";
0592
0593 auto &jx = j["/m_layer_configs/60"_json_pointer];
0594
0595 json::json_pointer jp(lojz);
0596 jx[jp] = 99.876;
0597
0598
0599
0600 from_json(j, it_cfg);
0601 printf("Layer 43 : m_select_max_dphi = %f, size_of_layer_vec=%d, m_n_regions=%d, size_of_steering_params=%d\n",
0602 it_cfg.m_layer_configs[43].m_select_max_dphi,
0603 (int)it_cfg.m_layer_configs.size(),
0604 it_cfg.m_n_regions,
0605 (int)it_cfg.m_steering_params.size());
0606
0607 printf("Layer 60 : m_select_max_dphi = %f, size_of_layer_vec=%d, m_n_regions=%d, size_of_steering_params=%d\n",
0608 it_cfg.m_layer_configs[60].m_select_max_dphi,
0609 (int)it_cfg.m_layer_configs.size(),
0610 it_cfg.m_n_regions,
0611 (int)it_cfg.m_steering_params.size());
0612
0613
0614
0615
0616
0617 auto &x = j["/m_layer_configs"_json_pointer];
0618 std::cout << "Typename /m_layer_configs " << x.type_name() << "\n";
0619 auto &y = j["/m_layer_configs/143"_json_pointer];
0620 std::cout << "Typename /m_layer_configs/143 " << y.type_name() << ", is_null=" << y.is_null() << "\n";
0621 }
0622
0623 void ConfigJson::test_Patcher(IterationConfig &it_cfg) {
0624 ConfigJsonPatcher cjp;
0625 cjp.load(it_cfg);
0626
0627 std::cout << cjp.dump(1) << "\n";
0628
0629 {
0630 cjp.cd("/m_layer_configs/43/m_select_max_dphi");
0631 std::cout << "Layer 43, m_select_max_dphi = " << cjp.get("") << "\n";
0632 std::cout << "Setting it to pi ...\n";
0633 cjp.replace("", 3.141);
0634 cjp.cd_top();
0635 std::cout << "Layer 43, m_select_max_dphi = " << cjp.get("/m_layer_configs/43/m_select_max_dphi") << "\n";
0636 }
0637 {
0638 std::cout << "Replacing layer 60 m_select_max_dphi with full path\n";
0639 cjp.replace("/m_layer_configs/60/m_select_max_dphi", 99.876);
0640 }
0641 try {
0642 std::cout << "Trying to replace an non-existent array entry\n";
0643 cjp.replace("/m_layer_configs/1460/m_select_max_dphi", 666.666);
0644 } catch (std::exception &exc) {
0645 std::cout << "Caugth exception: " << exc.what() << "\n";
0646 }
0647 try {
0648 std::cout << "Trying to replace an non-existent object entry\n";
0649 cjp.replace("/m_layer_configs/1/moo_select_max_dphi", 666.666);
0650 } catch (std::exception &exc) {
0651 std::cout << "Caugth exception: " << exc.what() << "\n";
0652 }
0653 {
0654 std::cout << "Replacing m_select_max_dphi on layers 1 to 3 to 7.7\n";
0655 cjp.cd("/m_layer_configs");
0656 cjp.replace(1, 3, "/m_select_max_dphi", 7.7);
0657 cjp.cd_top();
0658 }
0659
0660
0661
0662 cjp.save(it_cfg);
0663
0664 printf("Layer 43: m_select_max_dphi = %f, size_of_layer_vec=%d, m_n_regions=%d, size_of_steering_params=%d\n",
0665 it_cfg.m_layer_configs[43].m_select_max_dphi,
0666 (int)it_cfg.m_layer_configs.size(),
0667 it_cfg.m_n_regions,
0668 (int)it_cfg.m_steering_params.size());
0669
0670 printf("Layer 60: m_select_max_dphi = %f\n", it_cfg.m_layer_configs[60].m_select_max_dphi);
0671 for (int i = 0; i < 5; ++i)
0672 printf("Layer %2d: m_select_max_dphi = %f\n", i, it_cfg.m_layer_configs[i].m_select_max_dphi);
0673
0674
0675
0676
0677
0678 auto &j = cjp.get("");
0679
0680 auto &x = j["/m_layer_configs"_json_pointer];
0681 std::cout << "Typename /m_layer_configs " << x.type_name() << "\n";
0682 auto &y = j["/m_layer_configs/143"_json_pointer];
0683 std::cout << "Typename /m_layer_configs/143 " << y.type_name() << ", is_null=" << y.is_null() << "\n";
0684 }
0685
0686 }