File: genns.cpp

package info (click to toggle)
cppgir 2.0%2Bgit20240928.c8bb1c6%2Breally2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,228 kB
  • sloc: cpp: 14,307; ansic: 339; makefile: 11; sh: 9
file content (1521 lines) | stat: -rw-r--r-- 52,175 bytes parent folder | download | duplicates (2)
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
#include "genns.hpp"
#include "fs.hpp"
#include "function.hpp"
#include "genbase.hpp"

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/split.hpp>

#include <boost/property_tree/xml_parser.hpp>

#include <iostream>
#include <ostream>
#include <sstream>
#include <tuple>

namespace
{
class File : public std::ofstream
{
  std::string ns;
  std::string fname;
  bool preamble;
  bool guard;
  NamespaceGuard nsg;

  static std::string root;

public:
  // ok, single threaded processing
  static void set_root(const std::string &dir) { root = dir; }

  static std::string prepdirs(const std::string &_ns, const std::string &_fname)
  {
    fs::path p(root);
    p /= tolower(_ns);
    fs::create_directories(p);
    p /= tolower(_fname);
    return p.native();
  }

  File(const std::string &_ns, const std::string _fname, bool _need_ns = true,
      bool _need_guard = true)
      : std::ofstream(prepdirs(_ns, _fname)), ns(_ns), fname(_fname),
        preamble(_need_ns), guard(_need_guard), nsg(*this)
  {
    write_pre();
  }

  std::string get_rel_path() const
  {
    return (fs::path(tolower(ns)) / tolower(fname)).native();
  }

  void write_pre()
  {
    *this << "// AUTO-GENERATED\n\n";
    auto definc = fmt::format("_GI_{}_{}_", toupper(ns), toupper(fname));
    for (auto &v : definc)
      if (!isalnum(v))
        v = '_';
    if (guard) {
      *this << "#ifndef " << definc << std::endl;
      *this << "#define " << definc << std::endl;
      *this << std::endl;
    }
    if (preamble)
      nsg.push(ns);
  }

  void write_post()
  {
    if (preamble)
      nsg.pop();
    if (guard)
      *this << "#endif" << std::endl;
  }

  ~File() { write_post(); }
};

std::string File::root;

class NamespaceGeneratorImpl : private GeneratorBase, public NamespaceGenerator
{
  std::string version_;
  std::vector<std::string> deps_;
  pt::ptree root_;
  pt::ptree tree_;
  // helper exp
  std::regex re_unqualify_;

public:
  NamespaceGeneratorImpl(GeneratorContext &ctx, const std::string &filename)
      : GeneratorBase(ctx, "")
  {
    logger(Log::INFO, "reading {}", filename);
    pt::read_xml(filename, root_);
    // extract dependencies
    for (auto &&n : root_.get_child("repository")) {
      if (n.first == "include") {
        auto &&name = get_name(n.second);
        auto &&version = get_attribute(n.second, AT_VERSION);
        deps_.emplace_back(name + "-" + version);
        logger(Log::INFO, indent + "dependency " + deps_.back());
      }
    }
    // all else is in a subtree
    tree_ = root_.get_child("repository.namespace");
    ns = get_name(tree_);
    version_ = get_attribute(tree_, AT_VERSION);
    re_unqualify_ =
        std::regex(fmt::format("^{}(::|\\.)", ns), std::regex::optimize);
  }

  std::string get_ns() const { return ns + "-" + version_; }

  std::vector<std::string> get_dependencies() const { return deps_; }

private:
  // in most cases no problems arise if both M1.SomeName and M2.SomeOtherName
  // both map to GSomeName
  // but some generated code, like declare_XXX template specialization assumes
  // a 1-to-1 mapping, as should be the case in with sane GIR
  // however, if the latter are somehow not sane, then duplicate definitions
  // might arise (ODR and all that), although it *really is* a GIR bug
  //
  // so try to mitigate that here
  // check that only 1 (qualified) girname/cppname claims/maps to a ctype
  void check_odr(const std::string &cpptype, const std::string &ctype) const
  {
    // verify this ctype has not been seen before
    // a particular ctype can only be defined/claimed by one GIR symbol
    // (in one module), otherwise lots of things will go wrong
    // GIRs also have an "alias" concept, so that should be used instead
    auto conflict = ctx.repo.check_odr(cpptype, ctype);
    if (!conflict.empty()) {
      throw std::runtime_error(
          fmt::format("{} maps to {}, already claimed by {};\n"
                      "Please verify/fix GIRs and/or add ignore as needed.",
              cpptype, ctype, conflict));
    }
  }

  std::string make_declare(bool decl_ctype, const std::string &cpptype,
      const std::string &ctype) const
  {
    check_odr(cpptype, ctype);
    return fmt::format(
        "template<> struct declare_{}_of<{}>\n{{ typedef {} type; }}; ",
        (decl_ctype ? "ctype" : "cpptype"), (decl_ctype ? cpptype : ctype),
        (decl_ctype ? ctype : cpptype));
  }

  void process_element_alias(const pt::ptree &node, std::ostream &out) const
  {
    std::ostringstream oss;
    auto name = get_name(node);
    // skip some stuff
    if (name.find("autoptr") != name.npos)
      throw skip("autoptr", skip::OK);
    auto ctype = get_attribute(node, AT_CTYPE);

    std::string deftype;
    TypeInfo tinfo;
    auto tnode = node.get_child(EL_TYPE);
    auto btype = get_name(tnode);
    parse_typeinfo(btype, tinfo);
    if (tinfo.flags & TYPE_BASIC) {
      // if typedef refers to a basic type, let's refer here to (global
      // scope) C type (this is mostly the case, e.g. GstClockTime, etc)
      deftype = GI_SCOPE + ctype;
    } else if (tinfo.flags) {
      assert(tinfo.cpptype.size());
      // otherwise we alias to the corresponding wrapped type
      // (e.g. GtkAllocation)
      deftype = tinfo.cpptype;
    }
    auto aliasfmt = "typedef {} {};\n";
    if (deftype.size())
      out << fmt::format(aliasfmt, deftype, name);
    // also mind ref type
    if (tinfo.flags & TYPE_BOXED) {
      assert(deftype.size());
      out << fmt::format(
          aliasfmt, deftype + GI_SUFFIX_REF, name + GI_SUFFIX_REF);
    }
    out << std::endl;
  }

  void process_element_enum(const pt::ptree::value_type &n, std::ostream &out,
      std::ostream *_out_impl) const
  {
    auto &kind = n.first;
    auto &node = n.second;
    std::ostringstream oss;
    auto name = get_name(node);
    auto ctype = get_attribute(node, AT_CTYPE);

    if (_out_impl) {
      auto &out_impl = *_out_impl;
      // need to generate namespaced function members
      std::ostringstream oss_decl;
      std::ostringstream oss_impl;
      std::set<std::string> dummy;
      for (const auto &n : node) {
        if (n.first == EL_FUNCTION) {
          process_element_function(n, oss_decl, oss_impl, "", "", dummy);
        }
      }
      if (oss_decl.tellp()) {
        auto enumns = name + "NS_";
        NamespaceGuard ns_d(out);
        ns_d.push(enumns, false);
        out << oss_decl.str();

        NamespaceGuard ns_i(out_impl);
        ns_i.push(enumns, false);
        out_impl << oss_impl.str();
      }
      return;
    }

    // enum name might end up matching (uppercase) define
    name = unreserve(name);

    // otherwise generate enum
    NamespaceGuard nsg(oss);
    nsg.push(ns);
    // auto-adjust underlying type to avoid warnings;
    // outside the range of underlying type ‘int’
    // (no forward declare of enum in C, so type should be complete)
    oss << fmt::format(
        "enum class {} : std::underlying_type<{}>::type {{\n", name, ctype);
    std::set<std::string> names;
    for (const auto &n : node) {
      if (n.first == EL_MEMBER) {
        auto value = get_attribute(n.second, AT_CIDENTIFIER);
        auto name = get_name(n.second);
        // some enums/flags declare different names with same value
        // which seems to confuse gobject-introspection
        // (e.g. GstVideoFrameFlags, GstVideoBufferFlags, etc)
        // and then they end up with repeated/duplicate name in GIR
        // so let's only pick the first one of those
        if (!std::get<1>(names.insert(name))) {
          logger(Log::WARNING, "{} skipping duplicate {}", ctype, value);
          continue;
        }
        oss << indent
            << fmt::format("{} = {},\n", unreserve(toupper(name)), value);
      }
    }
    oss << "};\n\n";
    // add operators in case of flag = bitfield
    if (kind == EL_FLAGS)
      oss << fmt::format("GI_FLAG_OPERATORS({})\n\n", name);
    nsg.pop();

    // declare ctype info
    nsg.push(GI_REPOSITORY_NS);
    name = ns + "::" + name;
    oss << make_declare(true, name, ctype) << std::endl;
    oss << make_declare(false, name, ctype) << std::endl;
    oss << std::endl;

    // declare gtype info
    auto gtype = get_attribute(node, AT_GLIB_GET_TYPE, "");
    if (gtype.size()) {
      oss << fmt::format("template<> struct declare_gtype_of<{}>\n"
                         "{{ static GType get_type() {{ return {}(); }} }};",
                 name, gtype)
          << std::endl;
      oss << std::endl;
    }

    // in case of a flag = bitfield, also mark it as such
    if (kind == EL_FLAGS) {
      oss << fmt::format(
                 "template<> struct is_bitfield<{}> : public std::true_type\n"
                 "{{}};",
                 name)
          << std::endl;
      oss << std::endl;
    }

    oss << std::endl;
    nsg.pop();
    out << oss.str() << std::endl;
  }

  void process_element_const(const pt::ptree &node, std::ostream &out) const
  {
    auto name = get_name(node);
    std::ostringstream oss;
    try {
      ArgInfo tinfo = parse_arginfo(node);
      auto cpptype = tinfo.cpptype;

      auto value = get_attribute(node, AT_CTYPE);
      std::string cast;
      std::string namesuffix;
      if (tinfo.flags & TYPE_BASIC) {
        if (tinfo.flags & TYPE_CLASS) {
          // special string case
          // need some suffix to make the variable (pointer) const
          cpptype = "gchar";
          namesuffix = "[]";
        } else {
          // normalize back to C (e.g. char*)
          cpptype = tinfo.ctype;
        }
      } else {
        // need to cast to enum type (flags/enum)
        if (tinfo.flags & TYPE_ENUM) {
          cast = fmt::format("({}) ", cpptype);
        } else if (!(tinfo.flags & TYPE_VALUE)) {
          throw skip("constant type " + cpptype + " not supported");
        }
        // accept alias typedef
      }
      // avoid clashes with existing defines
      name = unreserve(name);
      // NOTE what about an inline var in C++17
      // recall that const implies static in C++
      oss << fmt::format(
          "const {} {}{} = {}{};", cpptype, name, namesuffix, cast, value);
    } catch (skip &ex) {
      oss << "// SKIP constant " << name << "; " << ex.what();
    }
    out << oss.str() << std::endl << std::endl;
  }

  void process_element_property(const pt::ptree::value_type &entry,
      std::ostream &out, std::ostream &impl, const std::string &klass,
      std::set<std::string> &deps) const
  {
    auto &node = entry.second;

    ArgInfo tinfo;
    auto name = get_name(node);
    auto read = get_attribute<int>(node, AT_READABLE, 1);
    auto write = get_attribute<int>(node, AT_WRITABLE, 1);
    tinfo = parse_arginfo(node);

    auto &&cpptype = tinfo.cpptype;
    if (!tinfo.flags)
      throw skip("unknown type " + cpptype);
    // let's not go here for now
    if (tinfo.flags & TYPE_CONTAINER)
      throw skip("container property not supported", skip::TODO);

    track_dependency(deps, tinfo);

    // directly write all in decl
    auto &oss = out;
    (void)impl;

    auto decl_name = name;
    std::replace(decl_name.begin(), decl_name.end(), '-', '_');
    std::string proptype = "property_proxy";
    if (!read) {
      proptype = "property_proxy_write";
    } else if (!write) {
      proptype = "property_proxy_read";
    }
    proptype = GI_NS_SCOPED + proptype;

    auto qklass = qualify(klass, TYPE_OBJECT);
    if (write)
      oss << fmt::format("{3}<{0}, {4}> property_{2}()\n"
                         "{{ return {3}<{0}, {4}> (*this, \"{1}\"); }}",
                 cpptype, name, decl_name, proptype, qklass)
          << std::endl;
    if (read)
      oss << fmt::format("const {3}<{0}, {4}> property_{2}() const\n"
                         "{{ return {3}<{0}, {4}> (*this, \"{1}\"); }}",
                 cpptype, name, decl_name, proptype, qklass)
          << std::endl;
    oss << std::endl;
  }

  void process_element_field(const pt::ptree::value_type &entry,
      std::ostream &out, std::ostream &impl, const std::string &klass,
      const std::string &klasstype, std::set<std::string> &deps) const
  {
    auto &node = entry.second;

    auto name = get_name(node);
    auto readable = get_attribute<int>(node, AT_READABLE, 1);
    auto writable = get_attribute<int>(node, AT_WRITABLE, 1);
    auto priv = get_attribute<int>(node, AT_PRIVATE, 0);

    ArgInfo tinfo;
    try {
      tinfo = parse_arginfo(node);

      // only very plain basic fields
      // type must be known
      // no private
      // no int* cookies or other strange things
      if (!tinfo.flags || priv ||
          get_pointer_depth(tinfo.ctype) != tinfo.pdepth ||
          is_volatile(tinfo.ctype))
        return;
    } catch (...) {
      // simply fail silently here and never mind
      return;
    }

    if (!(tinfo.flags & (TYPE_VALUE | TYPE_CLASS)))
      return;

    // unconditionally reserve this to avoid clash with non-field member
    ElementFunction func;
    func.kind = EL_METHOD;
    func.name = unreserve(name, true);
    func.c_id = klasstype + "::" + name;

    Parameter instance;
    // real one, no base
    parse_typeinfo(klasstype, instance.tinfo);
    instance.instance = true;
    instance.name = "obj";
    instance.direction = DIR_IN;

    // field parameter
    Parameter param;
    param.tinfo = tinfo;
    param.name = "_value";
    param.transfer = TRANSFER_NOTHING;

    // common helper lambda for below
    auto make_wrapper = [&](const std::string &funcname,
                            const std::string funcdef,
                            const std::vector<Parameter> &params) {
      func.functionexp = funcname;
      // buffer result
      std::ostringstream oss;
      auto def = ::process_element_function(
          ctx, ns, func, params, out, oss, klass, klasstype, deps);
      // now write all if wrapping accessor did not fail
      if (def.name.size())
        impl << funcdef << std::endl << oss.str();
    };

    // a static helper is used in the following,
    // rather than a compact local lambda,
    // as the latter does not always cast to any function type
    // also, internal casts are applied in the helper to handle enum/int
    // and pointer type conversions

    // also, the ctype info is derived from the cpptype as with func
    // argument due to either
    // + ctype missing, e.g. enum,
    // + or minor discrepancies, e.g. gpointer vs gconstpointer

    if (readable) {
      param.direction = DIR_RETURN;
      param.tinfo.ctype = make_ctype(param.tinfo, param.direction, false);
      instance.tinfo.ctype = fmt::format("const {}*", instance.tinfo.dtype);
      // define helper function
      auto funcname = "_field_" + name + "_get";
      auto funcdef =
          fmt::format("static {} {} ({} {}) {{ return ({}) obj->{}; }}",
              param.tinfo.ctype, funcname, instance.tinfo.ctype, instance.name,
              param.tinfo.ctype, name);
      make_wrapper(funcname, funcdef, {param, instance});
    }

    // not safe to assume any particular ownership of some struct field
    // so let's not meddle with it other than the very basic cases
    if (writable && (tinfo.flags & TYPE_VALUE)) {
      param.direction = DIR_IN;
      param.tinfo.ctype = make_ctype(param.tinfo, param.direction, false);
      instance.tinfo.ctype = instance.tinfo.dtype + "*";
      // define helper function
      auto funcname = "_field_" + name + "_set";
      auto funcdef = fmt::format("static void {} ({} {}, {} {}) {{ "
                                 "obj->{} = (decltype(obj->{})) {}; }}",
          funcname, instance.tinfo.ctype, instance.name, param.tinfo.ctype,
          param.name, name, name, param.name);
      // void return
      Parameter vparam;
      parse_typeinfo(GIR_VOID, vparam.tinfo);
      vparam.direction = DIR_RETURN;
      make_wrapper(funcname, funcdef, {vparam, param, instance});
    }
  }

  FunctionDefinition process_element_function(
      const pt::ptree::value_type &entry, std::ostream &out, std::ostream &impl,
      const std::string &klass, const std::string &klasstype,
      std::set<std::string> &deps) const
  {
    return ::process_element_function(
        ctx, ns, entry, out, impl, klass, klasstype, deps);
  }

  // unqualify (current ns qualifed) type
  std::string unqualify(const std::string &name) const
  {
    return std::regex_replace(name, re_unqualify_, "");
  }

  static std::string get_record_filename(const std::string &rname, bool impl)
  {
    return tolower(rname) + (impl ? "_impl" : "") + ".hpp";
  }

  static std::string make_include(const std::string &hname, bool local)
  {
    if (hname.empty())
      return hname;
    char open = local ? '"' : '<';
    char close = local ? '"' : '>';
    std::ostringstream oss;
    oss << "#include " << open << hname << close;
    return oss.str();
  }

  // make include for a qualified dependency
  // (not in current namespace)
  std::string make_dep_include(const std::string &girname) const
  {
    auto lname = unqualify(girname);
    return lname.size() && !is_qualified(lname)
               ? make_include(get_record_filename(lname, false), true) + '\n'
               : EMPTY;
  }

  std::string make_dep_declare(const std::set<std::string> &deps) const
  {
    std::ostringstream oss;
    for (const auto &d : deps) {
      auto c = unqualify(d);
      if (!is_qualified(c))
        oss << "class " << c << ";" << std::endl;
    }
    return oss.str();
  }

  static std::string make_conditional_include(
      const std::string &hname, bool local)
  {
    if (hname.empty())
      return hname;

    auto templ = R"|(
#if defined(__has_include)
#if __has_include({}{}{})
#include {}{}{}
#endif
#endif
)|";
    char open = local ? '"' : '<';
    char close = local ? '"' : '>';
    return fmt::format(templ, open, hname, close, open, hname, close);
  }

  static constexpr const char *const CLASS_PLACEHOLDER{"CLASS_PLACEHOLDER"};

  // minor convencience helper type
  struct TypeClassInfo
  {
    // (optionally) gir qualified
    std::string parentgir;
    // info on type-struct
    TypeInfo ti;
  };

  TypeClassInfo collect_type_class_info(const std::string &girname) const
  {
    // class struct is skipped above,
    // but we do look for it here to find *Class/*Interface struct
    TypeClassInfo result;
    auto &repo = ctx.repo;
    auto &node = repo.tree(girname).second;
    result.parentgir = get_attribute(node, AT_PARENT, "");
    // NOTE parent might be in different ns
    if (result.parentgir.size())
      result.parentgir = repo.qualify(result.parentgir, girname);
    auto cpptype = get_attribute(node, AT_GLIB_TYPE_STRUCT, "");
    if (cpptype.empty())
      throw skip(girname + " missing type-struct info");
    cpptype = repo.qualify(cpptype, girname);
    parse_typeinfo(cpptype, result.ti);
    // more useful in this setting
    result.ti.cpptype = unqualify(result.ti.cpptype);
    if (result.ti.dtype.empty())
      throw skip(girname + " missing C type-struct info");
    return result;
  }

  typedef std::vector<std::pair<std::string, FunctionDefinition>>
      VirtualMethods;
  void process_element_record_class(const pt::ptree::value_type &entry,
      const std::vector<TypeInfo> &interfaces, const std::string &decl,
      const std::string &impl, const VirtualMethods &methods,
      std::ostream &out_decl, std::ostream &out_impl) const
  {
    auto &node = entry.second;
    auto name = get_name(node);
    bool interface = (entry.first == EL_INTERFACE);

    // run up parent hierarchy to check if all those can be properly
    // generated
    auto class_info = collect_type_class_info(name);
    TypeClassInfo parent_class_info;
    // no parent for interface
    if (class_info.parentgir.size()) {
      parent_class_info = collect_type_class_info(class_info.parentgir);
      auto rec_info = parent_class_info;
      while (rec_info.parentgir.size())
        rec_info = collect_type_class_info(rec_info.parentgir);
    }

    // collect ok interfaces
    std::vector<TypeInfo> itfs_info;
    for (auto &&itf : interfaces) {
      try {
        itfs_info.emplace_back(collect_type_class_info(itf.girname).ti);
        // base class needs to be declared
        out_decl << make_dep_include(itf.girname);
      } catch (...) {
      }
    }
    out_decl << std::endl;

    // put into inner namespace to avoid name clash
    // declaration part
    NamespaceGuard ns_decl(out_decl);
    ns_decl.push(ns);

    ns_decl.push(GI_NS_IMPL);
    ns_decl.push(GI_NS_INTERNAL);

    // class definition
    // explicitly specify a non-public non-virtual destructor
    auto def_templ = R"|(
class {0}
{{
typedef {0} self;
public:
typedef {1} instance_type;
typedef {2} {3}_type;

{6}
struct TypeInitData;

protected:
{5} ~{0}() = default;
static {5} void {3}_init (gpointer {3}_struct, gpointer );

{4}
}};

)|";

    std::string kind = interface ? "interface" : "class";
    const auto klassnamedef = class_info.ti.cpptype + "Def";
    // used for interfaces
    const std::string suffix_class_impl = "ClassImpl";
    const auto klassname =
        class_info.ti.cpptype + (interface ? suffix_class_impl : "");

    // conflict check and type init lists
    std::string conflict_check, type_init, type_init_calc;
    conflict_check.reserve(1024);
    type_init.reserve(1024);
    type_init_calc.reserve(1024);
    std::string calc_indent = indent + indent;
    for (auto &&method : methods) {
      auto &&n = method.first;
      conflict_check +=
          fmt::format("using GI_MEMBER_CHECK_CONFLICT({}) = self;\n", n);
      type_init +=
          fmt::format("{}GI_MEMBER_DEFINE({}, {})\n", indent, klassname, n);
      type_init_calc +=
          fmt::format("{}{}GI_MEMBER_HAS_DEFINITION(SubClass, DefData, {})",
              type_init_calc.empty() ? "" : ",\n", calc_indent, n);
    }

    // (note that e.g. xlib cases prefix might not help, or ns interference)
    // drop inline mark on pure virtual interface functions;
    // does not quite make sense and might otherwise lead to compiler
    // warnings
    static const std::regex re_inline(GI_INLINE + ' ', std::regex::optimize);
    out_decl << fmt::format(def_templ, klassnamedef, qualify(name, TYPE_OBJECT),
        class_info.ti.dtype, kind, std::regex_replace(decl, re_inline, ""),
        GI_INLINE, conflict_check);
    if (interface)
      out_decl << fmt::format("using {}Impl = detail::InterfaceImpl<{}>;", name,
                      klassnamedef)
               << std::endl;

    // avoid ambiguous unqualified name lookup of friend class below
    // (as the scopes of parent classes are involved then as well)
    auto class_templ = R"|(
class {0}: public {1}
{{
friend class internal::{4};
typedef {0} self;
typedef {1} super;

protected:
using super::super;
{2}
{3}
}};

)|";

    auto classextra = R"|(
private:
// make local helpers private
using super::get_struct_;
using super::gobj_;

protected:
// disambiguation helper types
{}
)|";

    // qualification helper; ensure impl::internal qualified
    // (also adds current ns if needed)
    auto implqualify = [&](const std::string &cpptype) {
      auto result = cpptype;
      auto pos = result.find(GI_SCOPE.c_str());
      auto insert = GI_SCOPE + GI_NS_IMPL + GI_SCOPE + GI_NS_INTERNAL;
      if (pos != result.npos) {
        result.insert(pos, insert);
      } else {
        insert = ns + insert + GI_SCOPE;
        result.insert(0, insert);
      }
      return result;
    };

    // collect interface types and helper types
    std::ostringstream oss_types;
    std::vector<std::string> itfs;
    for (auto &&itf : itfs_info) {
      auto kname = itf.cpptype + suffix_class_impl;
      kname = implqualify(kname);
      itfs.push_back(kname);
      // fall back to ctype as it needs to be fully qualified
      static const std::regex re_descope(GI_SCOPE, std::regex::optimize);
      auto tprefix = std::regex_replace(itf.dtype, re_descope, "");
      oss_types << fmt::format("typedef {} {}_type;\n", kname, tprefix);
    }
    auto extra = interface ? EMPTY : fmt::format(classextra, oss_types.str());
    // determine baseclasses
    // qualified klassnamedef to avoid ambiguous parent class lookup
    // (when used as a typedef within class)
    std::string superclass =
        interface ? fmt::format("detail::InterfaceClassImpl<{}>", name + "Impl")
                  : fmt::format("detail::ClassTemplate<{}, {}{}{}>",
                        implqualify(klassnamedef),
                        implqualify(parent_class_info.ti.cpptype),
                        itfs.size() ? ", " : "",
                        boost::algorithm::join(itfs, ", "));
    static const std::regex re_virtual("virtual ", std::regex::optimize);
    static const std::regex re_pure("= 0", std::regex::optimize);
    auto idecl = std::regex_replace(decl, re_virtual, "");
    idecl = std::regex_replace(idecl, re_pure, "override");
    // add macro for optional warning suppression
    if (!interface)
      out_decl << GI_CLASS_IMPL_BEGIN << std::endl << std::endl;
    out_decl << fmt::format(
        class_templ, klassname, superclass, extra, idecl, klassnamedef);

    // type init
    // at this later stage as it contains template definitions
    // that refer to the above class
    auto type_init_templ = R"|(
struct {0}::TypeInitData
{{
{1}
template<typename SubClass>
constexpr static TypeInitData factory()
{{
  {3}using DefData = detail::DefinitionData<SubClass, TypeInitData>;
  return {{
{2}
  }};
}}
}};
)|";
    out_decl << fmt::format(type_init_templ, klassnamedef, type_init,
        type_init_calc, methods.empty() ? "// " : "");
    // end internal ns
    ns_decl.pop(1);

    if (!interface)
      out_decl << GI_CLASS_IMPL_END << std::endl
               << std::endl
               << fmt::format("using {}Impl = detail::ObjectImpl<{}, {}::{}>;",
                      name, name, GI_NS_INTERNAL, klassname)
               << std::endl
               << std::endl;

    // implementation part
    NamespaceGuard ns_impl(out_impl);
    ns_impl.push(ns);

    ns_impl.push(GI_NS_IMPL);
    ns_impl.push(GI_NS_INTERNAL);

    auto &ctype = class_info.ti.dtype;
    out_impl << fmt::format(
        "void {}::{}_init (gpointer {}_struct, gpointer factory)\n{{\n",
        klassnamedef, kind, kind);
    out_impl << indent
             << fmt::format(
                    "{} *methods = ({} *) {}_struct;\n", ctype, ctype, kind);
    // avoid warning if no methods
    out_impl << indent << "(void) methods;" << std::endl << std::endl;
    // init data from factory
    out_impl
        << indent
        << "auto init_data = GI_MEMBER_INIT_DATA(TypeInitData, factory);\n";
    out_impl << indent << "(void) init_data;" << std::endl << std::endl;

    for (auto &&method : methods) {
      auto &&n = method.first;
      auto &&def = method.second;
      std::vector<std::string> args, transfers;
      for (auto &&arg : def.cpp_decl)
        args.push_back(arg.second);
      auto cpp_return =
          def.cpp_outputs.size() ? def.cpp_outputs[0].type : CPP_VOID;
      auto sig = fmt::format(
          "{} (*) ({})", cpp_return, boost::algorithm::join(args, ", "));
      std::string transferargs, precheck;
      if (def.arg_traits.empty()) {
        // only arrange to call the raw methods
        // if new-style detection (or explicit specification) is in place
        precheck = " && factory";
        transferargs = "std::nullptr_t";
      } else {
        transferargs = make_arg_traits(def.arg_traits, def.c_sig);
      }
      // add a hard cast to deal with const differences (e.g. string vs
      // const char*)
      out_impl << indent
               << fmt::format("if (init_data.{0}{3}) methods->{0} = (decltype "
                              "(methods->{0})) "
                              "detail::method_wrapper<self, {1}, "
                              "{2}>::wrapper<&self::{0}_>;",
                      n, sig, transferargs, precheck)
               << std::endl;
    }
    out_impl << "}" << std::endl << std::endl;

    auto iimpl =
        std::regex_replace(impl, std::regex(CLASS_PLACEHOLDER), klassname);
    out_impl << iimpl;
  }

  std::vector<TypeInfo> record_collect_interfaces(
      const pt::ptree::value_type &entry, const TypeInfo &current,
      const TypeInfo &parent, std::set<std::string> &deps) const
  {
    auto &repo = ctx.repo;
    auto &node = entry.second;
    auto name = get_name(node);

    // listed interfaces also include parent's interfaces
    // so we should subtract those for good measure

    auto collect_interfaces = [&](const pt::ptree &node,
                                  const std::string &basename) {
      std::set<std::string> result;
      auto p = node.equal_range(EL_IMPLEMENTS);
      assert(basename.size());
      for (auto &q = p.first; q != p.second; ++q) {
        auto n = get_name(q->second, std::nothrow);
        if (n.size()) {
          result.insert(repo.qualify(n, basename));
        }
      }
      return result;
    };

    std::vector<std::string> itfs;
    // ensure all GIR names fully qualified
    auto itfs_local = collect_interfaces(node, current.girname);
    if (parent.girname.size()) {
      // qualify relative to parent
      auto itfs_parent =
          collect_interfaces(repo.tree(parent.girname).second, parent.girname);
      // only keep those not matching a parent's interface (both fully
      // qualified)
      for (auto &&n : itfs_local) {
        if (itfs_parent.find(n) == itfs_parent.end())
          itfs.push_back(n);
      }
    } else {
      std::copy(itfs_local.begin(), itfs_local.end(), std::back_inserter(itfs));
    }

    std::vector<TypeInfo> interfaces;
    for (auto &n : itfs) {
      TypeInfo itf;
      parse_typeinfo(n, itf);
      // should know about this interface by now
      // but let's continue anyway
      if (!itf.flags) {
        logger(Log::WARNING, "{} implements unknown interface {}", name, n);
        continue;
      }
      deps.insert(itf.cpptype);
      interfaces.push_back(itf);
    }

    return interfaces;
  }

  // returns (decl header name, impl header name)
  std::tuple<std::string, std::string> process_element_record(
      const pt::ptree::value_type &entry, bool onlyverify) const
  {
    auto &kind = entry.first;
    auto &node = entry.second;
    auto name = get_name(node);

    logger(Log::LOG, "checking {} {} {}", kind, name, onlyverify);

    TypeInfo current;
    parse_typeinfo(name, current);
    // only consider real type, but keep info around
    if (!current.flags)
      return std::make_tuple("", "");
    auto &ctype = current.dtype;
    // could happen for a GType only case (e.g. GstFraction)
    if (!ctype.size())
      throw skip("no c:type info");
    bool is_object_base = (current.girname == GIR_GOBJECT);

    // check parent
    TypeInfo parent;
    auto &repo = ctx.repo;
    // 1 special case here
    if (kind == EL_OBJECT && !is_object_base) {
      auto parentgir = get_attribute(node, AT_PARENT, "");
      if (parentgir.empty())
        throw skip("missing parent class");
      parse_typeinfo(parentgir, parent);
      if (!parent.flags)
        throw skip("unknown parent " + parentgir);
      // parent might be found, but might have problems of its own
      // so that needs to be checked recursively
      // (at least if within current ns)
      if (onlyverify && !is_qualified(parentgir)) {
        try {
          process_element_record(repo.tree(parentgir), onlyverify);
        } catch (const skip &ex) {
          throw skip(std::string("parent problem; ") + ex.what());
        }
      }
    }

    if (onlyverify || is_object_base)
      return std::make_tuple("", "");

    // no throwing or giving up after this point
    // we have now committed to coming up with a class
    // declaration/definition (even if it is a pretty empty one)
    logger(Log::LOG, "processing {} {}", kind, name);

    // generate classes in subnamespace
    const std::string nsbase("base");
    auto namebase = name + "Base";
    // avoid qualification later on with standard ns (e.g. Gst)
    auto qnamebase = nsbase + "::" + namebase;

    // collect output of members and their dependencies
    std::ostringstream oss_decl, oss_class_decl;
    std::ostringstream oss_impl, oss_class_impl;
    VirtualMethods vmethods;
    std::set<std::string> deps;
    for (const auto &n : node) {
      auto el = n.first;
      int introspectable = get_attribute<int>(n.second, AT_INTROSPECTABLE, 1);
      try {
        // try even if marked introspectable
        // some are useful in non-runtime setting
        if (el == EL_FUNCTION || el == EL_CONSTRUCTOR || el == EL_METHOD ||
            el == EL_SIGNAL) {
          process_element_function(
              n, oss_decl, oss_impl, qnamebase, name, deps);
        } else if (el == EL_VIRTUAL_METHOD && introspectable &&
                   ctx.options.classimpl) {
          // placeholder replaced suitably later on
          auto def = process_element_function(
              n, oss_class_decl, oss_class_impl, CLASS_PLACEHOLDER, name, deps);
          if (def.name.size())
            vmethods.push_back({def.name, def});
        } else if (el == EL_FIELD && introspectable) {
          process_element_field(n, oss_decl, oss_impl, qnamebase, name, deps);
        } else if (el == EL_PROPERTY && introspectable) {
          process_element_property(n, oss_decl, oss_impl, qnamebase, deps);
        }
      } catch (std::runtime_error &ex) {
        handle_exception(n, ex);
      }
    }

    // actual output to file
    auto fname_decl = get_record_filename(name, false);
    File out_decl(ns, fname_decl, false);
    auto fname_impl = get_record_filename(name, true);
    File out_impl(ns, fname_impl, false);

    // a superclass needs full declaration (not only forward declaration)
    out_decl << make_dep_include(parent.girname);
    out_decl << std::endl;

    // implemented interfaces are also dependency
    std::vector<TypeInfo> interfaces =
        record_collect_interfaces(entry, current, parent, deps);

    // namespace in decl before forward class decl
    NamespaceGuard ns_decl(out_decl);
    ns_decl.push(ns);

    // all declarations are included prior to implementation
    // forward class declarations in declaration
    deps.erase(current.cpptype);
    out_decl << make_dep_declare(deps);
    out_decl << std::endl;

    // also forward declare 'oneself' since only base is defined below
    out_decl << "class " << name << ";" << std::endl;
    out_decl << std::endl;

    // namespace in impl following includes
    NamespaceGuard ns_impl(out_impl);
    ns_impl.push(ns);

    // base class subnamespace
    ns_decl.push(nsbase);
    ns_impl.push(nsbase);

    // class definition
    auto obj_templ = R"|(
#define {3} {4}::{0}
class {0} : public {2}
{{
typedef {2} super_type;
public:
typedef {1} BaseObjectType;

{0} (std::nullptr_t = nullptr) : super_type() {{}}

BaseObjectType *gobj_() {{ return (BaseObjectType*) super_type::gobj_(); }}
const BaseObjectType *gobj_() const {{ return (const BaseObjectType*) super_type::gobj_(); }}
BaseObjectType *gobj_copy_() const {{ return (BaseObjectType*) super_type::gobj_copy_(); }}

)|";

    auto boxed_templ = R"|(
#define {2} {3}::{0}
class {0} : public {1}
{{
typedef {1} super_type;
public:

{0} (std::nullptr_t = nullptr) : super_type() {{}}

)|";

    auto gtype = get_attribute(node, AT_GLIB_GET_TYPE, "");
    auto basedef = toupper(fmt::format("GI_{}_{}_BASE", ns, name));
    // class definition
    bool is_variant = false;
    if (kind == EL_OBJECT) {
      out_decl << fmt::format(
          obj_templ, namebase, ctype, parent.cpptype, basedef, nsbase);
    } else if (kind == EL_INTERFACE) {
      out_decl << fmt::format(
          obj_templ, namebase, ctype, "gi::InterfaceBase", basedef, nsbase);
    } else {
      auto tmpl = GI_NS_DETAIL_SCOPED +
                  (gtype.size() ? "GBoxedWrapperBase" : "CBoxedWrapperBase");
      auto parent = fmt::format("{}<{}, {}>", tmpl, namebase, ctype);
      // override if special fundamental boxed-like case
      if ((is_variant = (current.girname == GIR_GVARIANT))) {
        parent = "detail::VariantWrapper";
        gtype.clear();
      }
      out_decl << fmt::format(boxed_templ, namebase, parent, basedef, nsbase);
    }

    if (gtype.size()) {
      out_decl << fmt::format(
          "static GType get_type_ () G_GNUC_CONST {{ return {}(); }} ", gtype);
      out_decl << std::endl << std::endl;
    }

    // add some helpers to obtain known implemented interfaces
    for (auto &&_itf : interfaces) {
      // temp extend for wrapping below
      ArgInfo itf;
      (TypeInfo &)itf = _itf;
      auto decl_fmt = "{0} {1}interface_ (gi::interface_tag<{0}>)";
      auto impl_fmt = decl_fmt;
      auto decl = fmt::format(decl_fmt, itf.cpptype, "");
      out_decl << GI_INLINE << ' ' << decl << ";" << std::endl << std::endl;
      // wrap a properly casted extra reference (ensure no sink'ing)
      auto impl = fmt::format(impl_fmt, itf.cpptype, (namebase + "::"));
      out_impl << impl << std::endl;
      auto towrap =
          fmt::format("({}::BaseObjectType*) gobj_copy_()", itf.cpptype);
      auto w =
          fmt::format(make_wrap_format(ArgInfo{itf}, TRANSFER_FULL), towrap);
      out_impl << fmt::format("{{ return {}; }}", w) << std::endl << std::endl;
      // conversion operator
      auto op_decl = fmt::format("operator {} ()", itf.cpptype);
      auto op_impl = fmt::format(
          "{}::{}\n{{ return interface_ (gi::interface_tag<{}>()); }}",
          namebase, op_decl, itf.cpptype);
      out_decl << GI_INLINE << ' ' << op_decl << ';' << std::endl << std::endl;
      out_impl << op_impl << std::endl << std::endl;
    }

    out_decl << oss_decl.str();
    out_decl << "}; // class" << std::endl;
    out_decl << std::endl;
    ns_decl.pop();

    out_impl << oss_impl.str();
    out_impl << std::endl;
    ns_impl.pop();

    // optionally include supplements/overrides for this class
    auto include_extra = [&](File &out, bool impl) {
      for (auto &&suffix : {"_extra_def", "_extra"}) {
        auto header =
            (fs::path(tolower(ns)) / get_record_filename(name + suffix, impl))
                .native();
        out << make_conditional_include(header, false) << std::endl;
      }
    };
    include_extra(out_decl, false);
    include_extra(out_impl, true);

    ns_decl.push(GI_REPOSITORY_NS);
    // make fragment to define final class
    {
      NamespaceGuard nst(out_decl);
      nst.push(ns, false);
      auto supertype = basedef;
      auto reftype = name + GI_SUFFIX_REF;
      if (kind != EL_OBJECT && kind != EL_INTERFACE && !is_variant) {
        // boxed base templates define a few members with CppType return type
        // so in the CppTypeBase defined above that is still CppTypeBase
        // again add the template as subclass but now with final CppType
        auto tmpl = GI_NS_DETAIL_SCOPED +
                    (gtype.size() ? "GBoxedWrapper" : "CBoxedWrapper");
        supertype = fmt::format(
            "{}<{}, {}, {}, {}>", tmpl, name, ctype, basedef, reftype);
        // forward declaration of corresponding ref type
        out_decl << fmt::format("class {};", reftype) << std::endl << std::endl;
      }
      auto fmtclass =
          "class {0} : public {1}\n"
          "{{ typedef {1} super_type; using super_type::super_type; }};\n";
      out_decl << fmt::format(fmtclass, name, supertype) << std::endl;

      // in case of boxed, also define ref type
      if (kind != EL_OBJECT && kind != EL_INTERFACE && !is_variant) {
        auto tmpl = GI_NS_DETAIL_SCOPED +
                    (gtype.size() ? "GBoxedRefWrapper" : "CBoxedRefWrapper");
        supertype = fmt::format("{}<{}, {}, {}>", tmpl, name, ctype, basedef);
        out_decl << std::endl
                 << fmt::format(fmtclass, reftype, supertype) << std::endl;
      }
    }
    // GInitiallyUnowned is typedef'ed to GObject
    // so we have to avoid duplicate definition
    // FIXME generally no way to check for that using Gir, provide some
    // override here ??
    if (current.girname != GIR_GINITIALLYUNOWNED) {
      // declare type info
      out_decl << make_declare(false, ns + "::" + name, ctype) << std::endl;
      out_decl << std::endl;
    }
    ns_decl.pop();

    // now process the virtual method class parts
    if (ctx.options.classimpl && (kind == EL_OBJECT || kind == EL_INTERFACE)) {
      try {
        process_element_record_class(entry, interfaces, oss_class_decl.str(),
            oss_class_impl.str(), vmethods, out_decl, out_impl);
      } catch (const skip &ex) {
        logger(Log::WARNING, "skipping class generation for {}; {}", name,
            ex.what());
      }
    }

    return std::make_tuple(fname_decl, fname_impl);
  }

  std::string handle_exception(
      const pt::ptree::value_type &n, const std::runtime_error &ex) const
  {
    auto &el = n.first;
    const auto &name = get_name(n.second, std::nothrow);
    auto ex_skip = dynamic_cast<const skip *>(&ex);
    if (!ex_skip || ex_skip->cause == skip::INVALID) {
      auto msg = fmt::format("{} {} {}; {}",
          (ex_skip ? "skipping" : "EXCEPTION processing"), el, name, ex.what());
      Log level = ex_skip ? Log::WARNING : Log::ERROR;
      if (check_suppression(ns, el, name))
        level = Log::DEBUG;
      logger(level, msg);
    } else {
      logger(Log::DEBUG, "discarding {} {}; {}", el, name, ex.what());
    }
    return name;
  }

  typedef std::function<void(const pt::ptree::value_type &)> entry_processor;
  void process_entries(const pt::ptree &node, const entry_processor &proc)
  {
    for (auto it = node.begin(); it != node.end(); ++it) {
      auto &&n = *it;
      if (n.first == PT_ATTR)
        continue;
      try {
        proc(n);
      } catch (std::runtime_error &ex) {
        auto name = handle_exception(n, ex);
        // in any case give up on this name
        ctx.repo.discard(name);
      }
    }
  }

  bool visit_ok(const pt::ptree::value_type &n) const
  {
    const auto &girname = get_name(n.second, std::nothrow);
    auto ninfo = ctx.repo.lookup(girname);
    if (ninfo && !(ninfo->info && (ninfo->info->flags & TYPE_PREDEFINED))) {
      logger(Log::LOG, "visiting {} {}", n.first, girname);
      return true;
    } else {
      return false;
    }
  }

  const char *process_libs()
  {
    // also find needed shared libraries in case of dlopen
    auto sl = get_attribute(tree_, AT_SHARED_LIBRARY, "");
    std::vector<std::string> shlibs, qshlibs;
    boost::split(shlibs, sl, boost::is_any_of(","));
    for (auto &l : shlibs)
      if (l.size())
        qshlibs.push_back(fmt::format("\"{}\"", l));

    auto h_libs = "_libs.hpp";
    File libs(ns, h_libs);

    auto libs_templ = R"|(
namespace internal {{

GI_INLINE_DECL std::vector<const char*> _libs()
{{ return {{{0}}}; }}

}} // internal
)|";
    libs << fmt::format(libs_templ, boost::join(qshlibs, ","));

    return h_libs;
  }

public:
  std::string process_tree(const std::vector<std::string> &dep_headers)
  {
    logger(Log::INFO, "processing namespace {}", ns);
    // set state for ns processing
    ctx.repo.set_ns(ns);
    File::set_root(ctx.options.rootdir);

    // optionally process libs
    auto h_libs = ctx.options.dl ? process_libs() : "";

    auto h_types = "_types.hpp";
    File types(ns, h_types);
    // index run
    // collect info by name
    // also gather alias/type info
    entry_processor proc_index = [&](const pt::ptree::value_type &n) {
      const auto &name = get_name(n.second, std::nothrow);
      auto &el = n.first;
      // redirect to oblivion
      // empty name might originate from a glib:boxed with glib:name attribute
      // discard those as well, as that is a glib type without C-type
      // (which are not really useful in our situation)
      if (name.empty() || ctx.match_ignore.matches(ns, el, {name})) {
        logger(Log::INFO, "ignoring {} {}", el, name);
      } else {
        ctx.repo.add(name, n);
        if (n.first == EL_ALIAS && visit_ok(n)) {
          process_element_alias(n.second, types);
        }
      }
    };
    process_entries(tree_, proc_index);

    // process basic types and callbacks
    auto h_enums = "_enums.hpp";
    auto h_flags = "_flags.hpp";
    auto h_constants = "_constants.hpp";
    auto h_callbacks = "_callbacks.hpp";
    auto h_callbacks_impl = "_callbacks_impl.hpp";
    auto h_functions = "_functions.hpp";
    auto h_functions_impl = "_functions_impl.hpp";

    File enums(ns, h_enums, false);
    File flags(ns, h_flags, false);
    File constants(ns, h_constants);
    File callbacks(ns, h_callbacks);
    File callbacks_impl(ns, h_callbacks_impl);
    File functions(ns, h_functions);
    File functions_impl(ns, h_functions_impl);

    entry_processor proc_pass_1 = [&](const pt::ptree::value_type &n) {
      auto deprecated = get_attribute<int>(n.second, AT_DEPRECATED, 0);
      if (deprecated)
        return;
      auto &el = n.first;
      if (el == EL_ENUM && visit_ok(n)) {
        process_element_enum(n, enums, nullptr);
      } else if (el == EL_FLAGS && visit_ok(n)) {
        process_element_enum(n, flags, nullptr);
      } else if (el == EL_CONST && visit_ok(n)) {
        process_element_const(n.second, constants);
      }
    };
    process_entries(tree_, proc_pass_1);

    // check class types we can handle and will provide a definition for
    entry_processor proc_pass_class = [&](const pt::ptree::value_type &n) {
      auto &el = n.first;
      if ((el == EL_RECORD || el == EL_OBJECT || el == EL_INTERFACE) &&
          visit_ok(n)) {
        process_element_record(n, true);
      }
    };
    process_entries(tree_, proc_pass_class);

    // so the known classes will be declared/defined
    // check what callback typedefs that allows for
    // check class types we can handle and will provide a definition for
    std::set<std::string> cb_deps;
    std::ostringstream cb_decl;
    entry_processor proc_pass_callbacks = [&](const pt::ptree::value_type &n) {
      auto &el = n.first;
      if (el == EL_CALLBACK && visit_ok(n)) {
        std::ostringstream null;
        process_element_function(n, cb_decl, callbacks_impl, "", "", cb_deps);
      }
    };
    process_entries(tree_, proc_pass_callbacks);
    // write callbacks
    // need to declare deps first
    callbacks << make_dep_declare(cb_deps);
    callbacks << std::endl;
    callbacks << cb_decl.str();

    // now we know all supported types and supported callbacks
    // pass over class types again and fill in
    std::set<std::pair<std::string, std::string>> includes;
    std::set<std::string> dummy;
    entry_processor proc_pass_2 = [&](const pt::ptree::value_type &n) {
      auto &el = n.first;
      if ((el == EL_RECORD || el == EL_OBJECT || el == EL_INTERFACE) &&
          visit_ok(n)) {
        auto &&res = process_element_record(n, false);
        includes.insert({std::get<0>(res), std::get<1>(res)});
      } else if (el == EL_FUNCTION && visit_ok(n)) {
        process_element_function(n, functions, functions_impl, "", "", dummy);
      } else if ((el == EL_ENUM || el == EL_FLAGS) && visit_ok(n)) {
        process_element_enum(n, functions, &functions_impl);
      }
    };
    process_entries(tree_, proc_pass_2);

    auto add_stub_include = [this](const std::string &suffix) {
      auto fpath = (fs::path(tolower(ns)) / (tolower(ns) + suffix)).native();
      return make_conditional_include(fpath, false);
    };

    auto h_ns = tolower(ns) + ".hpp";
    auto h_ns_impl = tolower(ns) + "_impl.hpp";

    // generate overall includes
    File nsh(ns, h_ns, false);
    // enable dl load coding
    if (h_libs && *h_libs) {
      nsh << "#ifndef GI_DL" << std::endl;
      nsh << "#define GI_DL 1" << std::endl;
      nsh << "#endif" << std::endl;
    }
    if (ctx.options.expected) {
      nsh << "#ifndef GI_EXPECTED" << std::endl;
      nsh << "#define GI_EXPECTED 1" << std::endl;
      nsh << "#endif" << std::endl;
    }
    nsh << make_include("gi/gi.hpp", false) << std::endl;
    nsh << std::endl;
    // include gi deps
    for (auto &&d : dep_headers)
      nsh << make_include(d, false) << std::endl;
    nsh << std::endl;
    // package includes
    // in some cases, these are totally missing
    // so the headers will have to be supplied by extra header override
    // repo supplied
    nsh << add_stub_include("_setup_pre_def.hpp") << std::endl;
    // user supplied
    nsh << add_stub_include("_setup_pre.hpp") << std::endl;
    // include the above before the includes
    // (so as to allow tweaking some defines in the overrides)
    auto node = root_.get_child(EL_REPOSITORY);
    auto p = node.equal_range(EL_CINCLUDE);
    for (auto &q = p.first; q != p.second; ++q) {
      // in some buggy cases, additional headers may have a full path
      // let's only keep the last part
      auto name = get_name(q->second);
      if (name.size() && name[0] == '/') {
        auto pos = name.rfind('/');
        pos = (pos != name.npos && pos) ? name.rfind('/', pos - 1) : pos;
        if (pos != name.npos)
          name = name.substr(pos + 1);
      }
      nsh << make_include(name, false) << std::endl;
    }
    // we may also need to tweak or fix things after usual includes
    // repo supplied
    nsh << add_stub_include("_setup_post_def.hpp") << std::endl;
    // user supplied
    nsh << add_stub_include("_setup_post.hpp") << std::endl;
    nsh << std::endl;

    // no deprecated warning floods
    nsh << GI_DISABLE_DEPRECATED_WARN_BEGIN << std::endl;
    nsh << std::endl;

    // various basic declaration parts
    for (auto &h : {h_types, h_enums, h_flags, h_constants, h_callbacks})
      nsh << make_include(h, true) << std::endl;
    nsh << std::endl;

    // declarations
    for (auto &h : includes)
      nsh << make_include(h.first, true) << std::endl;
    nsh << std::endl;
    // global functions when we have seen all else
    nsh << make_include(h_functions, true) << std::endl;
    nsh << std::endl;
    // allow for override/supplements
    // repo supplied
    nsh << add_stub_include("_extra_def.hpp") << std::endl;
    // user supplied
    nsh << add_stub_include("_extra.hpp") << std::endl;
    // end deprecated warn
    nsh << GI_DISABLE_DEPRECATED_WARN_END << std::endl;
    nsh << std::endl;
    // include implementation header in inline case
    nsh << "#if defined(GI_INLINE) || defined(GI_INCLUDE_IMPL)" << std::endl;
    nsh << make_include(h_ns_impl, true) << std::endl;
    nsh << "#endif" << std::endl;
    nsh << std::endl;

    File nsh_impl(ns, h_ns_impl, false);
    // include declaration
    nsh_impl << make_include(h_ns, true) << std::endl;
    nsh_impl << std::endl;
    // warn disable begin
    nsh_impl << GI_DISABLE_DEPRECATED_WARN_BEGIN << std::endl;
    nsh_impl << std::endl;
    // lib helper for symbol load
    nsh_impl << make_include(h_libs, true) << std::endl;
    nsh_impl << std::endl;
    // implementations
    for (auto &h : includes)
      nsh_impl << make_include(h.second, true) << std::endl;
    nsh_impl << std::endl;
    nsh_impl << make_include(h_callbacks_impl, true) << std::endl;
    nsh_impl << make_include(h_functions_impl, true) << std::endl;
    nsh_impl << std::endl;
    // likewise for override/supplement implementation
    // repo supplied
    nsh_impl << add_stub_include("_extra_def_impl.hpp") << std::endl;
    // user supplied
    nsh_impl << add_stub_include("_extra_impl.hpp") << std::endl;
    nsh_impl << std::endl;
    // warn disable end
    nsh_impl << GI_DISABLE_DEPRECATED_WARN_END << std::endl;
    nsh_impl << std::endl;

    // a convenience cpp for non-inline
    auto cpp_ns = tolower(ns) + ".cpp";
    File cpp(ns, cpp_ns, false, false);
    cpp << make_include(h_ns_impl, true) << std::endl;

    return nsh.get_rel_path();
  }
};

} // namespace

std::shared_ptr<NamespaceGenerator>
NamespaceGenerator::new_(GeneratorContext &ctx, const std::string &filename)
{
  return std::make_shared<NamespaceGeneratorImpl>(ctx, filename);
}