neniu
neniu

Reputation: 419

Linker error compiling D template

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

Answers (2)

neniu
neniu

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

BioTronic
BioTronic

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

Related Questions