About Monkey 2 › Forums › Monkey 2 Development › Initializer list and default constructor
This topic contains 3 replies, has 3 voices, and was last updated by
Felipe 2 years, 8 months ago.
-
AuthorPosts
-
July 31, 2016 at 6:59 am #2567
Hi!
I don’t know if this has been talked about but is there a plan to add initializer list like C++ to mx2 constructors?
Also I noticed there isn’t a concept of default constructor on mx2. Any plans on that, this would be nice specially for Structs.
I comment this issues because I started porting a game library I’ve written in C++ which includes some custom collections, memory allocators and base structures. I wanted to write everything in MX2
The first thing I decided to port was my doubly linked intrusive list which ported to MX2 looks like this:
Monkey123456789101112131415161718192021222324252627282930313233Struct IntrusiveList<T>Method New(_pOwner:T Ptr = Null)pPrev = Varptr SelfpNext = Varptr SelfpHead = Varptr SelfpOwner = _pOwnerEndMethod Remove:Void()pPrev->pNext = pNextpNext->pPrev = pPrevpPrev = Varptr SelfpNext = Varptr SelfpHead = Varptr SelfEndMethod InsertTail:Void(pTarget:IntrusiveList<T> Ptr)Local pTargetHead:IntrusiveList<T> Ptr = pTarget->pHeadRemove()pNext = pTargetHeadpPrev = pTargetHead->pPrevpTargetHead->pPrev = Varptr SelfpPrev->pNext = Varptr SelfpHead = pTargetHead->pHeadEnd' .... Some other methods go hereField pPrev:IntrusiveList<T> PtrField pNext:IntrusiveList<T> PtrField pHead:IntrusiveList<T> PtrField pOwner:T PtrEndFor testing I did something like this. A simple Int list.
Monkey12345678910111213141516Struct IntValueMethod New(_value:Int)value = _valuenode = New IntrusiveList<IntValue>(Varptr Self)EndField value:IntField node:IntrusiveList<IntValue>EndFunction Main:Void()Local root:IntrusiveList<IntValue> = New IntrusiveList<IntValue>(Null)Local a:IntValue = New IntValue(0)' Crash herea.node.InsertTail(Varptr root)EndThis was crashing. I immediately started looking at the generated code and I found why it was crashing. The reason was this line in IntValue
Monkey1node = New IntrusiveList<IntValue>(Varptr Self)The problem was that it was constructing a temporary object to pass to the node member and then immediately destroying it. This is a problem for the IntrusiveList since it uses a pointer to Self.
There are ways to go through this problem but they seem very hacky and prone to error. Here are two ways I solved the crashing.
The first one was passing a IntrusiveList<T> pointer to the constructor and use it as a Self pointer.
Monkey123456Method New(pSelf:IntrusiveList<T> Ptr, _pOwner:T Ptr = Null)pPrev = pSelfpNext = pSelfpHead = pSelfpOwner = _pOwnerEndand I had to change my test to this:
Monkey123456789101112131415161718Struct IntValueMethod New(_value:Int)value = _value' Forced to pass a pointer to node for it to worknode = New IntrusiveList<IntValue>(Varptr node, Varptr Self)EndField value:IntField node:IntrusiveList<IntValue>EndFunction Main:Void()Local root:IntrusiveList<IntValue>Local a:IntValue = New IntValue(0)' I am forced to init this after declaring root' because I get undefined 'root'root = New IntrusiveList<IntValue>(Varptr root, Null)a.node.InsertTail(Varptr root)EndThe other one was adding an Init method that would take care of this:
Monkey12345678910111213Struct IntrusiveList<T>Method New(_pOwner:T Ptr = Null)Init(_pOwner)EndMethod Init:Void(_pOwner:T Ptr = Null)pPrev = Varptr SelfpNext = Varptr SelfpHead = Varptr SelfpOwner = _pOwnerEnd' ... Other methods...EndAnd calling it like this in the IntValue constructor:
Monkey1234Method New(_value:Int)value = _valuenode.Init(Varptr Self)EndBoth of this solutions are error-prone in several ways.
What I suggest Is having a solution similar to intializer list. Maybe like this:
Monkey123456Struct FooMethod New(x:Float = 0.0)value{x}EndField value:FloatEndThe other issue I commented about was default constructors. This is something specific to Structs
For example if you don’t call explicitly the constructor on a struct it will call an empty generated constructor. Even If you define a constructor without any arguments it won’t be called since it’ll generate a code that forces it to go with the empty generated constructor. It’d be nice to have something like this:
Monkey1Local x:Foo(100.0)This way we avoid creating unnecessary temporary objects and calling the copy constructor.
I know this is probably low priority and some people probably don’t even care for stuff like this but I feel I would make the language more flexible and efficient.
Cheers,
Felipe
July 31, 2016 at 8:06 am #2569Use classes, not structs, for reference types!
Structs are not designed to be used ‘by reference’. Yes, you can Varptr them, but as you’ve found, as soon as they are assigned to something, passed to a function or returned from a function (which is what they’re designed for) the old Ptr becomes invalid. The intended use for Varptr with structs is really for interfacing with c/c++.
Varptr Self is esp. dangerous, as the code has no way of knowing if ‘Self’ is temporary or not. Self could refer to a local, a temporary copy, etc that is just about to go out of scope, or it could refer to ‘safe’ heap memory, such as a class field, global or array element.
This is one of the primary difference between c++ and mx2. In c++, structs/classes are the same thing and can be used as either references types or value types depending on how variables are declared. But in mx2 (and c# and d), a struct is always a value type while a class is always a reference type. Not as flexible as c++, but IMO well worth the huge amount of simplification it provides.
July 31, 2016 at 9:02 am #2571@Mark: This is one of the things that needs to be explained in the language docs.
July 31, 2016 at 2:01 pm #2584Hi Mark,
Thanks for the reply. I understand but one of the reasons I wanted to use Structs instead of Class for IntrusiveList was to avoid heap allocation. At the end I would get fragmented resources which would defeat the whole purpose of creating an intrusive list.
Monkey12345678910111213141516' IntValue layed out in memory if IntrusiveList is a StructClass IntValue[value : Int,node_pPrev : IntrusiveListnode_pNext : IntrusiveListnode_pHead : IntrusiveListnode_pOwner : IntValue]' IntValue layed out in memory if IntrusiveList is a ClassClass IntValue[value : Intnode : IntrusiveList -> Some random place in memory]IntrusiveList as a Class would be a very slow structure.
Cheers,
Felipe.
-
AuthorPosts
You must be logged in to reply to this topic.