1 /** 2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved. 3 License: MPL-2 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This Source Code Form is subject to the terms of the Mozilla Public License, 7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 8 one at http://mozilla.org/MPL/2.0/. 9 */ 10 module cpptooling.data.class_merge; 11 12 import my.sumtype; 13 14 import cpptooling.data.representation : CppClass; 15 import cpptooling.data.symbol : Container; 16 import cpptooling.data.symbol.types : FullyQualifiedNameType; 17 18 import std.typecons : NullableRef; 19 import std.range : only; 20 21 @safe: 22 23 alias LookupT = typeof(only(NullableRef!(CppClass).init)) delegate(FullyQualifiedNameType a); 24 25 /** Create a merged class with methods from all those classes it inherits from. 26 * 27 * Params: 28 * LookupT = a callback that takes a FullyQualifiedNameType and returns a 29 * range with zero or one elements of type NullableRef!CppClass. 30 */ 31 CppClass mergeClassInherit(ref CppClass class_, ref Container container, LookupT lookup) { 32 import std.algorithm : each; 33 34 if (class_.inheritRange.length == 0) { 35 return class_; 36 } 37 38 auto methods = dedup(getMethods(class_, container, lookup)); 39 40 auto c = CppClass(class_.name, class_.inherits, class_.resideInNs); 41 () @trusted { methods.each!(a => c.put(a)); }(); 42 43 return c; 44 } 45 46 private: 47 48 bool isMethodOrOperator(T)(T method) @trusted { 49 import cpptooling.data.representation : CppMethod, CppMethodOp, CppCtor, CppDtor; 50 51 // dfmt off 52 return method.match!((CppMethod a) => true, 53 (CppMethodOp a) => true, 54 (CppCtor a) => false, 55 (CppDtor a) => false); 56 // dfmt on 57 } 58 59 CppClass.CppFunc[] getMethods(ref CppClass c, ref Container container, LookupT lookup) @safe { 60 import std.array : array, appender; 61 import std.algorithm : cache, copy, each, filter, joiner, map; 62 import std.range : chain; 63 64 // dfmt off 65 auto local_methods = c.methodRange 66 .filter!(a => isMethodOrOperator(a)); 67 68 auto inherit_methods = c.inheritRange 69 .map!(a => lookup(a.fullyQualifiedName)) 70 // some classes do not exist in AST thus no methods returned 71 .joiner 72 .map!(a => getMethods(a, container, lookup)); 73 // dfmt on 74 75 auto methods = appender!(CppClass.CppFunc[])(); 76 () @trusted { local_methods.copy(methods); inherit_methods.copy(methods); }(); 77 78 return methods.data; 79 } 80 81 //TODO this function is inefficient. So many allocations... 82 auto dedup(CppClass.CppFunc[] methods) @trusted { 83 import std.array : array; 84 import std.algorithm : makeIndex, uniq, map, sort; 85 import cpptooling.utility.dedup : dedup; 86 import cpptooling.data.representation : funcToString; 87 88 static auto getUniqeId(T)(ref T method) { 89 import cpptooling.data : CppMethod, CppMethodOp, CppCtor, CppDtor; 90 91 // dfmt off 92 return method.match!((CppMethod a) => a.id, 93 (CppMethodOp a) => a.id, 94 (CppCtor a) => a.id, 95 (CppDtor a) => a.id); 96 // dfmt on 97 } 98 99 //TODO inefficient, lots of intermittent arrays and allocations. 100 // Convert to a range based no-allocation. 101 102 auto arr = methods.map!(a => getUniqeId(a)).array(); 103 104 auto index = new size_t[arr.length]; 105 // sorting the indexes 106 makeIndex(arr, index); 107 108 // dfmt off 109 // contains a list of indexes into methods 110 auto deduped_methods = 111 index 112 // dedup the sorted index 113 .uniq!((a,b) => arr[a] == arr[b]) 114 .array(); 115 116 // deterministic sorting by function signature 117 deduped_methods.sort!((a,b) { return methods[a].funcToString < methods[b].funcToString; }); 118 119 return deduped_methods 120 // reconstruct an array from the sorted indexes 121 .map!(a => methods[a]) 122 .array(); 123 // dfmt on 124 }