Reputation: 419
This is part of interpreter for a simple language. The language also includes strings, lists, functions, etc and the corresponding functions evalString, evalList, etc. functions in the Evaluator. Only the Number subclass and evalNumber function are shown.
The Evaluator.evaluate
function calls Expr.eval
, and each Expr
subclass calls the correct Evaluator
function (evalNumber
, evalString
, evalList
, etc.) to evaluate the expression.
import std.stdio;
import std.string;
abstract class Expr {
Out eval(Out)(Evaluator!Out evaluator);
}
class Number: Expr {
double num;
this(double _num) { num = _num; }
override Out eval(Out)(Evaluator!Out evaluator) {
return evaluator.evalNumber(this);
}
}
class Evaluator(Out) {
Out evaluate(Expr expr) {
return expr.eval(this); // ### Error is here ###
}
abstract Out evalNumber(Number number);
}
class ExprPrinter: Evaluator!string {
override string evalNumber(Number num) {
return format("%s", num.num);
}
}
void main() {
Number n = new Number(5);
auto printer = new ExprPrinter();
writefln("Number: %s", printer.evaluate(n));
}
When I try to build it, I get
example.o: In function `_D7example__T9EvaluatorTAyaZQp8evaluateMFCQBo4ExprZQBb':
/home/neniu/projects/duover/source/example.d:19: undefined reference to `_D7example4Expr__T4evalTAyaZQkMFCQBf__T9EvaluatorTQBaZQpZQBh'
collect2: error: ld returned 1 exit status
Error: linker exited with status 1
If I read the mangle correctly, the compiler is saying that the expr.eval
on line 19 (marked in the code) is referring to a function which is not defined, but (in this case) the Number.eval
function is supposed to be that function.
I have tried changing line 19 to include the Out specialisation
return expr.eval!Out(this);
but that made no difference at all.
What am I missing here? How can I get the expr.eval(this)
call to resolve to Number.eval
?
This is with DMD 2.081.1.
Thank you.
*&When I try to build it, I get
example.o: In function `_D7example__T9EvaluatorTAyaZQp8evaluateMFCQBo4ExprZQBb':
/home/neniu/projects/duover/source/example.d:19: undefined reference to `_D7example4Expr__T4evalTAyaZQkMFCQBf__T9EvaluatorTQBaZQpZQBh'
collect2: error: ld returned 1 exit status
Error: linker exited with status 1
If I read the mangle correctly, the compiler is saying that the expr.eval on line 19 (marked in the code) is referring to a function which is not defined, but (in this case) the Number.eval function is supposed to be that function.
I have tried changing line 19 to include the Out specialisation
return expr.eval!Out(this);
but that made no difference at all.
What am I missing here? How can I get the expr.expr(this) call to resolve to Number.eval?
This is with DMD 2.081.1.
Thank you.
* Edit *
I tried making Expr.eval
abstract (with and without the abstract
qualifier to the class itself, since that is the function that each subclass has to override:
abstract class Expr {
abstract Out eval(Out)(Evaluator!Out evaluator);
}
but now the compiler complains
example.d(5): Error: function `example.Expr.eval!string.eval` final functions cannot be abstract
example.d(28): Error: template instance `example.Expr.eval!string` error instantiating
example.d(35): instantiated from here: Evaluator!string
I don't want Expr.eval
to be final. I've done nothing to make Expr.eval
final. I don't see why this makes Expr.eval
final. How do I make Expr.eval
not final so I can make it abstract so I can -- no, have to -- override it?
Wouldn't declaring something as abstract
("You have to override this function") be exactly the opposite of declaring it final
("You can not override this function")?
Upvotes: 0
Views: 81
Reputation: 419
The only reason that Expr.eval
needs to know about the Out
parameter type is because I can't figure out a way to get Evaluator.evaluate
to recognise the run-time type of its input. If I could do something like
// In class Evaualor. Maybe (Out) is a parameter the class, not the method.
Out evaluate(Out)(Expr e) {
if (isType!Num(e)) {
return evalNum(cast(Num) e);
}
else if (isType!Str(e)) {
return evalStr(cast(Str) e);
}
else if (isType!List(e)) {
return evalList(cast(List) e);
}
and so on
}
then the Expr
would need to know nothing about the Evaluator
or the type that its evaluate
method emits.
Is there such a way to determine the actual run-time type of such variables? That would save me some time.
Upvotes: 0
Reputation: 2289
Wouldn't declaring something as abstract ("You have to override this function") be exactly the opposite of declaring it final ("You can not override this function")?
Yup. But templated functions are always final. IMO, there should be an error message on override Out eval(Out)(Evaluator!Out evaluator) {
, but apparently the compiler chooses to stay silent. You'll need to either make eval
not be a template, or move it such that it's not a member function.
What you're doing is essentially multiple dispatch, which is implemented as a library in OpenMethods. Knowing only the code you've shown, it's hard to tell if this is exactly what you need.
Upvotes: 1