In C++ we often see constructor code like
CircularArc2D::CircularArc2D(const CircularArc2D& arc)
: CircularDisk2D( arc )
{
m_StartPoint = arc.m_StartPoint;
m_EndPoint = arc.m_EndPoint;
}
Here CircularArc2D is a subclass of CircularDisk2D, and the constructor of the parent class is used for initialization.
Well I don't know too much about C++. But I just wonder how we would do it in Nim.
type
Point = object of RootObj
x, y: float
# mimic the C++ constructors
proc initPoint(): Point =
Point(x: -1)
# mimic the C++ getters and setters
proc getX(p: Point): float = p.x
proc setX(p: var Point; v: float) =
p.x = v
# maybe another module
type
Circle = object of RootObj
p: Point
r: float
# and now the chained constructors from C++
proc initCircle(): Circle =
result.p = initPoint()
type
Arc = object of Circle
start, `end`: Point
# we want the initialization from initPoint() which is not the binary zero default!
proc initArc(): Arc =
return Arc(initCircle(Circle(result)))
At least the last line does not compile. But there may be reasons why we do it this way, maybe Circle object is defined in another module and its fields are not directly accessible.
I assume this is explained somewhere already, but I can not remember or find it. And I do not really intent to watch all the youtube videos :-)
not completely satisfying, but maybe gets somewhere towards what you're after:
type
Point = object
x, y: float
proc initPoint(): Point = Point(x: -1)
type
Circle = object of RootObj
p: Point
r: float
# and now the chained constructors from C++
proc initCircle(T:typedesc[Circle] = Circle): T =
result.p = initPoint()
type
Arc = object of Circle
start, `end`: Point
proc initArc(T:typedesc[Arc] = Arc):T =
initCircle(Arc)
echo initCircle()
echo initArc()
That is an interesting trick, I was not aware of it. I think I will use it for my current module (QuickHullOfDisks) which follows a paper and a C++ implementation.
As this seems the only and best solution currently, so can we add it to the official tutorial or manual? If not I will have forgotten it in a few months, and others may never know.
maybe after you've used it for more than a toy example you'll be able to tell how robust it is and whether its useful enough to document. I avoid oop whenever possible so i'm not the person to try it out
I super hate the triple repetition,but its necessary to
a)differentiate which constructor one is calling ( init(typedesc) is nice but doesn't work here)
b)restrict each constructor only to members of the object hierarchy and then
c)provide a default (otherwise you'd have initCircle(Circle) which is awful)
i suppose the C++ version is pretty boilerplatey too
I don't like and know well C++. But the fact is that papers today most often use C++, so Nim should support C++ notations or have good substitute. (OOP does not mean that we have to use ref objects always, for some use cases inheritance may work with value objects. For the QuickHullDisks code I think that I need references indeed.)
I currently need convex hulls of disk in 2d, and that is not such trivial as it may look. The well known algorithm for points generally fail. I have used the CGAL Apollonius graph for that ten years ago from Ruby, but would like to avoid CGAL bindings now when possible.
So there are 3 papers, the Davenport one from 1992, which contains a very abstract notation, which I have problems to follow, another paper from 1995 not that abstract, and a new paper from 2019 with a good explanation. Unfortunately the algo has corner cases, sliver triangles, which makes it a bit complicated. As the authors provide there C++ code, I decided to try it not fully from scratch but follow the C++ implementation for most parts. So I will try to get it working soon, can nimify it later. Paper is
https://www.sciencedirect.com/science/article/pii/S0096300319306186
I just tested constructors with additional parameters, and it seems to work:
type
Point = object
x, y: float
proc initPoint(): Point = Point(x: -1)
proc initPoint(x, y: float): Point =
return Point(x: x, y: y)
type
Circle = object of RootObj
p: Point
r: float
# and now the chained constructors from C++
proc initCircle(T:typedesc[Circle] = Circle): T =
result.p = initPoint()
result.r = 0
proc initCircle(p: Point; r: float; T:typedesc[Circle] = Circle): T =
result.p = p
result.r = r
type
Arc = object of Circle
start, `end`: Point
proc initArc(T:typedesc[Arc] = Arc):T =
result = initCircle(T)
result.start = initPoint()
result.`end` = initPoint()
proc initArc(p, s, e: Point; r: float; T:typedesc[Arc] = Arc): T =
result = initCircle(p, r, T)
result.start = s
result.end = e
var p: Point = initPoint()
var s: Point = initPoint(1, 1)
var e: Point = initPoint(-1, 2)
echo initArc(p, s, e, 3) # s and e may be invalid of course, this is only example data
$ ./t
(start: (x: 1.0, y: 1.0), end: (x: -1.0, y: 2.0), p: (x: -1.0, y: 0.0), r: 3.0)