Re: Using templates with interfaces
On Sunday, 25 June 2017 at 17:30:58 UTC, Petar Kirov [ZombineDev] wrote: On Sunday, 25 June 2017 at 13:32:57 UTC, Andrew Chapman wrote: I think you've answered the question with "You cannot have unimplemented templates in interfaces". Thanks for the answer. I'll rethink the way I'm doing this. Cheers. In your case you can probably use something along the lines of: interface RelationalDBInterface { // You can even make this protected Varaint loadUntypedRow(string sql, Variant[] params); final T loadRow(T)(string sql, Variant[] params) { auto row = loadUntypedRow(sql, params); enforce(row.hasValue, this.classID ~ "::loadRow - Query returned an empty row"); return row.toStruct!T; } } Amazing, thank you!
Re: Using templates with interfaces
On Sunday, 25 June 2017 at 13:32:57 UTC, Andrew Chapman wrote: I think you've answered the question with "You cannot have unimplemented templates in interfaces". Thanks for the answer. I'll rethink the way I'm doing this. Cheers. In your case you can probably use something along the lines of: interface RelationalDBInterface { // You can even make this protected Varaint loadUntypedRow(string sql, Variant[] params); final T loadRow(T)(string sql, Variant[] params) { auto row = loadUntypedRow(sql, params); enforce(row.hasValue, this.classID ~ "::loadRow - Query returned an empty row"); return row.toStruct!T; } }
Re: Using templates with interfaces
On Sunday, 25 June 2017 at 13:32:57 UTC, Andrew Chapman wrote: I think you've answered the question with "You cannot have unimplemented templates in interfaces". Thanks for the answer. I'll rethink the way I'm doing this. Cheers. Yes, function templates in classes or interfaces are implicitly marked as final - meaning that you have to provide an implementation. However that is still useful. For example in one my projects I wanted to support configuration files written in either JSON or SDLang, however since std.json and sdlang-d had (as expected) different APIs, I decided to wrap them in classes implementing a common interface, so I could solve this in one place and be done with it. SDLang and JSON have a tree like structure and a common task is that I needed to interpret the value at a particular node as a scalar. Here's how I had done it: interface Node { final T get(T)() const { static if (isBoolean!T) return getBool(); else static if (isIntegral!T) return to!T(getInt()); else static if (isFloatingPoint!T) return to!T(getFloat()); else static if (isSomeString!T) return to!T(getString()); else static assert(0, "Type not supported: " ~ T.stringof); } const(Node) getChild(string name) const; protected: bool getBool() const; long getInt() const; double getFloat() const; string getString() const; } That way I could write a generic deserializer that iterates over the fields of the object and called the get method like this: foreach (idx, member; obj.tupleof) obj.tupleof[idx] = node.getChild( typeof(obj).tupleof[idx].stringof) .get!(typeof(member));
Re: Using templates with interfaces
I think you've answered the question with "You cannot have unimplemented templates in interfaces". Thanks for the answer. I'll rethink the way I'm doing this. Cheers.
Re: Using templates with interfaces
On Sunday, 25 June 2017 at 13:04:32 UTC, Nicholas Wilson wrote: On Sunday, 25 June 2017 at 11:39:27 UTC, Andrew Chapman wrote: Hi guys, I'm a little confused as to whether D supports interfaces with templates. I can compile OK, but linking reports an error like this: Error 42: Symbol Undefined _D12relationaldb10interfaces21RÇëÜDBIÇêÿ35__T7loadRowTS6prefix÷6P¶ZÇêáMFAyaAS3std7variant18Çâ└8VÇåìNVki20ZÇëÅZÇûð Essentially my desired interface looks like this: interface RelationalDBInterface { public T loadRow(T)(string sql, Variant[] params); } An example implementation: public T loadRow(T)(string sql, Variant[] params) { Prepared prepared = prepare(this.conn, sql); prepared.setArgs(params); auto row = prepared.queryRow(); if (row.isNull()) { throw new Exception(this.classID ~ "::loadRow - Query returned an empty row"); } T item; return row.toStruct!T(item); } And I would try to call it like this: auto user = this.loadRow!User(sql, params); Is it possible, or do I need to rethink the solution? The idea is to pass around a RelationalDBInterface so I can later switch from MySQL to Postgres or SQLite or whatever. You cannot have unimplemented templates in interfaces (where would they go in the virtual function table?), just return a variant. Implementations of interfaces must be classes not free functions so class MyDB : IRelationalDB { // implementation ... } which you then need to create a instance of auto mydb = MyDB(...); // connection auto user = mydb.loadRow!User(sql, params); 'this' is only valid inside an aggregate (struct or class). Sorry I wasn't very clear. My "this" was infact inside a class. Here's a more complete example of attempting to use the intertace: class MySQLRelationalDB : RelationalDBInterface { private Connection conn; private const string classID = "MySQLRelationalDB"; this(Connection conn) { this.conn = conn; } public T loadRow(T)(string sql, Variant[] params) { Prepared prepared = prepare(this.conn, sql); prepared.setArgs(params); auto row = prepared.queryRow(); if (row.isNull()) { throw new Exception(this.classID ~ "::loadRow - Query returned an empty row"); } T item; row.toStruct!T(item); return item; } } Then I use it within another class like this: class UserQuery { protected RelationalDBInterface relationalDb; this(RelationalDBInterface relationalDb) { this.relationalDb = relationalDb; } public User getUser(string emailAddress) { string sql = " SELECT * FROM usr WHERE email = ? "; auto user = this.relationalDb.loadRow!User( sql, variantArray(emailAddress) ); } } It compiles, but it wont link. Is it the case that you can't use templates with interfaces?
Re: Using templates with interfaces
On Sunday, 25 June 2017 at 11:39:27 UTC, Andrew Chapman wrote: Hi guys, I'm a little confused as to whether D supports interfaces with templates. I can compile OK, but linking reports an error like this: Error 42: Symbol Undefined _D12relationaldb10interfaces21RÇëÜDBIÇêÿ35__T7loadRowTS6prefix÷6P¶ZÇêáMFAyaAS3std7variant18Çâ└8VÇåìNVki20ZÇëÅZÇûð Essentially my desired interface looks like this: interface RelationalDBInterface { public T loadRow(T)(string sql, Variant[] params); } An example implementation: public T loadRow(T)(string sql, Variant[] params) { Prepared prepared = prepare(this.conn, sql); prepared.setArgs(params); auto row = prepared.queryRow(); if (row.isNull()) { throw new Exception(this.classID ~ "::loadRow - Query returned an empty row"); } T item; return row.toStruct!T(item); } And I would try to call it like this: auto user = this.loadRow!User(sql, params); Is it possible, or do I need to rethink the solution? The idea is to pass around a RelationalDBInterface so I can later switch from MySQL to Postgres or SQLite or whatever. You cannot have unimplemented templates in interfaces (where would they go in the virtual function table?), just return a variant. Implementations of interfaces must be classes not free functions so class MyDB : IRelationalDB { // implementation ... } which you then need to create a instance of auto mydb = MyDB(...); // connection auto user = mydb.loadRow!User(sql, params); 'this' is only valid inside an aggregate (struct or class).
Using templates with interfaces
Hi guys, I'm a little confused as to whether D supports interfaces with templates. I can compile OK, but linking reports an error like this: Error 42: Symbol Undefined _D12relationaldb10interfaces21RÇëÜDBIÇêÿ35__T7loadRowTS6prefix÷6P¶ZÇêáMFAyaAS3std7variant18Çâ└8VÇåìNVki20ZÇëÅZÇûð Essentially my desired interface looks like this: interface RelationalDBInterface { public T loadRow(T)(string sql, Variant[] params); } An example implementation: public T loadRow(T)(string sql, Variant[] params) { Prepared prepared = prepare(this.conn, sql); prepared.setArgs(params); auto row = prepared.queryRow(); if (row.isNull()) { throw new Exception(this.classID ~ "::loadRow - Query returned an empty row"); } T item; return row.toStruct!T(item); } And I would try to call it like this: auto user = this.loadRow!User(sql, params); Is it possible, or do I need to rethink the solution? The idea is to pass around a RelationalDBInterface so I can later switch from MySQL to Postgres or SQLite or whatever.