1 /** 2 Date: 2015-2017, Joakim Brännström 3 License: MPL-2, Mozilla Public License 2.0 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 */ 6 module dextool.plugin.backend.cpptestdouble.adapter; 7 8 import std.typecons : Yes, No; 9 import logger = std.experimental.logger; 10 11 import dsrcgen.cpp : CppModule; 12 13 import dextool.type : MainNs, MainInterface; 14 15 // TODO this is a mega include. Reduce it. 16 import cpptooling.data; 17 18 import cpptooling.data : CppClass, CppNamespace, CppClassName, CppMethodName, 19 USRType; 20 21 // dfmt off 22 @safe: 23 private struct BuildAdapter { 24 BuildAdapter makeTestDouble(bool value) { 25 this.hasTestDouble = value; 26 return this; 27 } 28 29 BuildAdapter makeInitGlobals(bool value) { 30 this.hasGlobalInitializer = value; 31 return this; 32 } 33 34 CppClass finalize() { 35 import cpptooling.data : SimpleFmt, TypeId, PtrFmt; 36 37 auto c = CppClass(className); 38 39 CxParam[] params; 40 41 if (hasTestDouble) { 42 auto attr = TypeAttr.init; 43 attr.isRef = Yes.isRef; 44 auto kind = TypeKind(TypeKind.PointerInfo(PtrFmt(TypeId(interfaceName)), 45 USRType(interfaceName ~ "&"), [attr])); 46 47 params ~= makeCxParam(TypeKindVariable(TypeKindAttr(kind, 48 TypeAttr.init), CppVariable("inst"))); 49 } 50 51 if (hasGlobalInitializer) { 52 auto kind = TypeKind(TypeKind.SimpleInfo(SimpleFmt(TypeId(interfaceInitGlobal)))); 53 params ~= makeCxParam(TypeKindVariable(TypeKindAttr(kind, 54 TypeAttr.init), CppVariable("init_globals"))); 55 } 56 57 c.comment("Adapter connecting an interface with an implementation."); 58 c.comment("The lifetime of the connection is the same as the instance of the adapter."); 59 60 c.put(CppCtor(makeUniqueUSR, classCtor, params, CppAccess(AccessType.Public))); 61 c.put(CppDtor(makeUniqueUSR, classDtor, CppAccess(AccessType.Public), 62 CppVirtualMethod(MemberVirtualType.Normal))); 63 64 return c; 65 } 66 67 private: 68 CppClassName className; 69 CppClassName interfaceName; 70 CppClassName interfaceInitGlobal; 71 CppMethodName classCtor; 72 CppMethodName classDtor; 73 bool hasTestDouble; 74 bool hasGlobalInitializer; 75 } 76 // dfmt on 77 78 /// Make a C++ adapter for an interface. 79 BuildAdapter makeAdapter(InterfaceT)(InterfaceT interface_name) { 80 return BuildAdapter(CppClassName("Adapter"), CppClassName(cast(string) interface_name), 81 CppClassName(cast(string) interface_name ~ "_InitGlobals"), 82 CppMethodName("Adapter"), CppMethodName("~Adapter")); 83 } 84 85 /// make an anonymous namespace containing a ptr to an instance of a test 86 /// double that implement the interface needed. 87 CppNamespace makeSingleton(MainNs main_ns, MainInterface main_if) { 88 import cpptooling.data : CppVariable, CxGlobalVariable, makeUniqueUSR; 89 import cpptooling.data : TypeId, PtrFmt; 90 91 auto attr = TypeAttr.init; 92 attr.isPtr = Yes.isPtr; 93 auto kind = TypeKind(TypeKind.PointerInfo(PtrFmt(TypeId(main_ns ~ "::" ~ main_if)), 94 USRType(main_ns ~ "::" ~ main_if ~ "*"), [attr])); 95 96 auto v = CxGlobalVariable(makeUniqueUSR, TypeKindAttr(kind, TypeAttr.init), 97 CppVariable("test_double_inst")); 98 auto ns = CppNamespace.makeAnonymous(); 99 ns.put(v); 100 101 return ns; 102 } 103 104 /** Generate an adapter implementation. 105 * 106 * The global is expected to be named test_double_inst. 107 */ 108 void generateImpl(CppClass c, CppModule impl) { 109 import std.variant : visit; 110 import cpptooling.data; 111 import dsrcgen.c : E; 112 113 // C'tor is expected to have N params. 114 // One of them must be named inst. 115 static void genCtor(const ref CppClass c, const ref CppCtor m, CppModule impl) { 116 // dfmt off 117 TypeKindVariable p0 = () @trusted { 118 import std.array; 119 120 return m.paramRange().front.visit!( 121 (TypeKindVariable tkv) => tkv, 122 (TypeKindAttr tk) => TypeKindVariable(tk, CppVariable("inst")), 123 (VariadicType vt) { 124 logger.error("Variadic c'tor not supported:", m.toString); 125 return TypeKindVariable(makeSimple("not supported"), CppVariable("not supported")); 126 })(); 127 }(); 128 // dfmt on 129 130 with (impl.ctor_body(m.name, E(p0.type.toStringDecl(p0.name)))) { 131 stmt(E("test_double_inst") = E("&" ~ p0.name)); 132 } 133 impl.sep(2); 134 } 135 136 static void genOp(const ref CppClass c, const ref CppMethodOp m, CppModule impl) { 137 // not applicable 138 } 139 140 static void genDtor(const ref CppClass c, const ref CppDtor m, CppModule impl) { 141 with (impl.dtor_body(c.name)) { 142 stmt("test_double_inst = 0"); 143 } 144 impl.sep(2); 145 } 146 147 static void genMethod(const ref CppClass c, const ref CppMethod m, CppModule impl) { 148 import std.range : takeOne; 149 150 string params = m.paramRange().joinParams(); 151 auto b = impl.method_body(m.returnType.toStringDecl, c.name, m.name, 152 m.isConst ? Yes.isConst : No.isConst, params); 153 with (b) { 154 auto p = m.paramRange().joinParamNames(); 155 stmt(E("test_double_inst") = E("&" ~ p)); 156 } 157 impl.sep(2); 158 } 159 160 foreach (m; c.methodPublicRange()) { 161 // dfmt off 162 () @trusted{ 163 m.visit!( 164 (const CppMethod m) => genMethod(c, m, impl), 165 (const CppMethodOp m) => genOp(c, m, impl), 166 (const CppCtor m) => genCtor(c, m, impl), 167 (const CppDtor m) => genDtor(c, m, impl)); 168 }(); 169 // dfmt on 170 } 171 } 172 173 /// A singleton to allow the adapter to setup "a" connection. 174 void generateSingleton(CppNamespace in_ns, CppModule impl) { 175 import std.ascii : newline; 176 import cpptooling.data; 177 import dsrcgen.cpp : E; 178 179 auto ns = impl.namespace("")[$.begin = "{" ~ newline]; 180 ns.suppressIndent(1); 181 impl.sep(2); 182 183 foreach (g; in_ns.globalRange()) { 184 auto stmt = E(g.type.toStringDecl(g.name)); 185 if (g.type.kind.info.kind == TypeKind.Info.Kind.pointer) { 186 stmt = E("0"); 187 } 188 ns.stmt(stmt); 189 } 190 }