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?

Reply via email to