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_, const 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, 49 CppDtor; 50 51 // dfmt off 52 return method.visit!((const CppMethod a) => true, 53 (const CppMethodOp a) => true, 54 (const CppCtor a) => false, 55 (const CppDtor a) => false); 56 // dfmt on 57 } 58 59 CppClass.CppFunc[] getMethods(const ref CppClass c, const 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 std.variant : visit; 90 import cpptooling.data : CppMethod, CppMethodOp, CppCtor, CppDtor; 91 92 // dfmt off 93 return method.visit!((CppMethod a) => a.id, 94 (CppMethodOp a) => a.id, 95 (CppCtor a) => a.id, 96 (CppDtor a) => a.id); 97 // dfmt on 98 } 99 100 //TODO inefficient, lots of intermittent arrays and allocations. 101 // Convert to a range based no-allocation. 102 103 auto arr = methods.map!(a => getUniqeId(a)).array(); 104 105 auto index = new size_t[arr.length]; 106 // sorting the indexes 107 makeIndex(arr, index); 108 109 // dfmt off 110 // contains a list of indexes into methods 111 auto deduped_methods = 112 index 113 // dedup the sorted index 114 .uniq!((a,b) => arr[a] == arr[b]) 115 .array(); 116 117 // deterministic sorting by function signature 118 deduped_methods.sort!((a,b) { return methods[a].funcToString < methods[b].funcToString; }); 119 120 return deduped_methods 121 // reconstruct an array from the sorted indexes 122 .map!(a => methods[a]) 123 .array(); 124 // dfmt on 125 }