One of my favorite things from C#/Java are interfaces and I've been trying to
work on writing some macros can generate an interface and allow you to
implement it. So far I've played around with getting some sort of "toy
interface," working as well as some of the macros too.
Here is my initial prototype code:
type
# The interface
IWorker* = object
obj*: ref RootObj
setup_proc*: proc(obj: ref RootObj, a, b: float)
run_proc*: proc(obj: ref RootObj)
getResult_proc*: proc(obj: ref RootObj): float
# A base type
MathOperation = object of RootObj
result: float
Adder = ref object of MathOperation
first, second: float
Subtracter = ref object of MathOperation
initial, subtraction: float
# Multiplier = ref object of MathOperation
# value, multiplier: float
#
# Divider = ref object of MathOperation
# numerator, divisor: float
#=== Procs for the IWorker ===#
proc setup(worker: IWorker; a, b: float) =
worker.setup_proc(worker.obj, a, b)
proc run(worker: IWorker) =
worker.run_proc(worker.obj)
proc getResult(worker: IWorker): float =
return worker.getResult_proc(worker.obj)
#=== Procs for the Adder ===#
proc Adder_setup(obj: ref RootObj; a, b: float) =
var this = cast[Adder](obj)
this.first = a
this.second = b
proc Adder_run(obj: ref RootObj) =
var this = cast[Adder](obj)
this.result = this.first + this.second
proc Adder_getResult(obj: ref RootObj): float =
var this = cast[Adder](obj)
return this.result
#=== Procs for the Subtractter ===#
proc Subtracter_setup(obj: ref RootObj; a, b: float) =
var this = cast[Subtracter](obj)
this.initial = a
this.subtraction = b
proc Subtracter_run(obj: ref RootObj) =
var this = cast[Subtracter](obj)
this.result = this.initial - this.subtraction
proc Subtracter_getResult(obj: ref RootObj): float =
var this = cast[Subtracter](obj)
return this.result
var
worker: IWorker
# Slap together the interface
add = IWorker(
setup_proc: Adder_setup,
run_proc: Adder_run,
getResult_proc: Adder_getResult
)
sub = IWorker(
setup_proc: Subtracter_setup,
run_proc: Subtracter_run,
getResult_proc: Subtracter_getResult
)
# Assign an object (the objects in this case really don't have any special
data)
add.obj = new(Adder)
sub.obj = new(Subtracter)
# Try out the workers
worker = add
worker.setup(10.0, 2.0)
worker.run
echo worker.getResult # Should be 12in
worker = sub
worker.setup(10.0, 2.0)
worker.run
echo worker.getResult # Should be 8
Right now I've got a macro called INTERFACE that acts like the one below and
can produce the type object.
INTERFACE IWorker ACTS_ON MathOperation:
proc setup(a, b: float)
proc run()
proc getResult(): float
# Produces this specifically:
#type
# # The interface
# IWorker = object
# obj: ref MathOperation
# setup_proc: proc(obj: ref MathOperation; a, b: float)
# run_proc: proc(obj: ref MathOperation)
# getResult_proc: proc(obj: ref MathOperation): float
But I've been doing some thinking and wondering if I should have the interface
actually act on an object. This would get rid of the obj field and remove the
insertion of the ref RootObj`s from the generated `type object interface. This
way an interface isn't tied to working on one type of object, but instead an
interface could be a "collection of functions (stored in an object) that can
act on any types," instead of a "contract for operations on a specific type."
I'm wondering which road I should go down. The "contract," way would closely
represent how interfaces work in C#/Java, but I feel that the "collection,"
idea would give more flexibility to Nim. What's your input?