Using templates with interfaces
Andrew Chapman via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Sun Jun 25 06:29:19 PDT 2017
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?
More information about the Digitalmars-d-learn
mailing list