Thread overview | |||||||||
---|---|---|---|---|---|---|---|---|---|
|
June 25, 2017 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. |
June 25, 2017 Re: Using templates with interfaces | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrew Chapman | 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).
|
June 25, 2017 Re: Using templates with interfaces | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nicholas Wilson | 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? |
June 25, 2017 Re: Using templates with interfaces | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrew Chapman | 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. |
June 25, 2017 Re: Using templates with interfaces | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrew Chapman | 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)); |
June 25, 2017 Re: Using templates with interfaces | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrew Chapman | 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;
}
}
|
June 27, 2017 Re: Using templates with interfaces | ||||
---|---|---|---|---|
| ||||
Posted in reply to Petar Kirov [ZombineDev] | 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!
|
Copyright © 1999-2021 by the D Language Foundation