Re: Using templates with interfaces

2017-06-27 Thread Andrew Chapman via Digitalmars-d-learn
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

2017-06-25 Thread via Digitalmars-d-learn

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

2017-06-25 Thread via Digitalmars-d-learn

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

2017-06-25 Thread Andrew Chapman via Digitalmars-d-learn
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

2017-06-25 Thread Andrew Chapman via Digitalmars-d-learn

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

2017-06-25 Thread Nicholas Wilson via Digitalmars-d-learn

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

2017-06-25 Thread Andrew Chapman via Digitalmars-d-learn
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.