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 cpptooling.data.representation : CppClass; 13 import cpptooling.data.symbol : Container; 14 import cpptooling.data.symbol.types : FullyQualifiedNameType; 15 16 import std.typecons : NullableRef; 17 import std.range : only; 18 19 @safe: 20 21 alias LookupT = typeof(only(NullableRef!(CppClass).init)) delegate(FullyQualifiedNameType a); 22 23 /** Create a merged class with methods from all those classes it inherits from. 24 * 25 * Params: 26 * LookupT = a callback that takes a FullyQualifiedNameType and returns a 27 * range with zero or one elements of type NullableRef!CppClass. 28 */ 29 CppClass mergeClassInherit(ref CppClass class_, ref Container container, LookupT lookup) { 30 import std.algorithm : each; 31 32 if (class_.inheritRange.length == 0) { 33 return class_; 34 } 35 36 auto methods = dedup(getMethods(class_, container, lookup)); 37 38 auto c = CppClass(class_.name, class_.inherits, class_.resideInNs); 39 () @trusted { methods.each!(a => c.put(a)); }(); 40 41 return c; 42 } 43 44 private: 45 46 bool isMethodOrOperator(T)(T method) @trusted { 47 import std.variant : visit; 48 import cpptooling.data.representation : CppMethod, CppMethodOp, CppCtor, CppDtor; 49 50 // dfmt off 51 return method.visit!((CppMethod a) => true, 52 (CppMethodOp a) => true, 53 (CppCtor a) => false, 54 (CppDtor a) => false); 55 // dfmt on 56 } 57 58 CppClass.CppFunc[] getMethods(ref CppClass c, ref Container container, LookupT lookup) @safe { 59 import std.array : array, appender; 60 import std.algorithm : cache, copy, each, filter, joiner, map; 61 import std.range : chain; 62 63 // dfmt off 64 auto local_methods = c.methodRange 65 .filter!(a => isMethodOrOperator(a)); 66 67 auto inherit_methods = c.inheritRange 68 .map!(a => lookup(a.fullyQualifiedName)) 69 // some classes do not exist in AST thus no methods returned 70 .joiner 71 .map!(a => getMethods(a, container, lookup)); 72 // dfmt on 73 74 auto methods = appender!(CppClass.CppFunc[])(); 75 () @trusted { local_methods.copy(methods); inherit_methods.copy(methods); }(); 76 77 return methods.data; 78 } 79 80 //TODO this function is inefficient. So many allocations... 81 auto dedup(CppClass.CppFunc[] methods) @trusted { 82 import std.array : array; 83 import std.algorithm : makeIndex, uniq, map, sort; 84 import cpptooling.utility.dedup : dedup; 85 import cpptooling.data.representation : funcToString; 86 87 static auto getUniqeId(T)(ref T method) { 88 import std.variant : visit; 89 import cpptooling.data : CppMethod, CppMethodOp, CppCtor, CppDtor; 90 91 // dfmt off 92 return method.visit!((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 }