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