Sorry for that basic topic but I want an object field to be able to handle two differents types with a clean syntax on the user side. No problem if the code behinds the scene is a hacky template or something, but the user should never need to see that.
I written a simplified example.
type
Rect = ref object
width:float
height:float
ConvexP = ref object
vertices:seq[float]
type Drawing = ref object
shape:Shape # I want this to hold a Rect or a ConvexP
# two stupid functions who just show if the program works.
proc presents(rect:Rect) =
echo "This is a rectangle "
proc presents(convP:ConvexP) =
echo "This is a convex polygon "
var rect = Rect(width: 2.0, height:2.0)
var drawing = Drawing()
drawing.shape = rect
drawing.presents shape # must runs the first version of "display"
This code is wrong because Shape is still not defined.
I want to avoid generics parameters because the code will be cluttered by many "[]", "Drawing" may handle several generic fields in the future, not only the shape. What is your suggestion ?
type RectOrConvex: Rect | Convex
What is your suggestion ?
When you already use ref object, you may consider inheritance. Or you may use sum types (union types) -- maybe bad if memory footprint of your objects differs a lot.
I want an object field to be able to handle two differents types with a clean syntax on the user side
Take a look at Object variants section in the manual, maybe that is something you can use in your case.
But strict typing is killing us
I'll repeat what I have already tried to tell you several times, based on the examples/questions you have posted previously.
I don't think 'strict typing' is what is killing you. It is your idea to do things "the old way", like you did them in some other language, and when that plan fails, you ask as how to patch it to make it work. Instead of doing things the other, easier, way where you might not even need those patches.
I've already gave you link to Wikipedia'a entry for 'XY problem' so this time I'll directly copy–paste it here:
The XY problem is a communication problem encountered in help desk and similar situations in which the real issue ("X") of the person asking for help is obscured, because instead of asking directly about issue X, they ask how to solve a secondary issue ("Y") which they believe will allow them to resolve issue X. However, resolving issue Y often does not resolve issue X, or is a poor way to resolve it, and the obscuring of the real issue and the introduction of the potentially strange secondary issue can lead to the person trying to help having unnecessary difficulties in communication and offering poor solutions.
I'm sorry if this sounds rude — that is not my intention. My intention is to help you so in the future you'll ask more questions about "X issue".
Here you go:
type
Shape = ref object of RootObj
Rect = ref object of Shape
width:float
height:float
ConvexP = ref object of Shape
vertices:seq[float]
type Drawing = object # Don't use ref object if there is no need, this involves the GC and is inefficient
shape: Shape
method display_type_of(self: Shape) {.base.} =
raise newException(ValueError, "Please implement display_type_of for your custom shape")
method display_type_of(self:Rect) =
echo "This is a rectangle "
method display_type_of(self:ConvexP) =
echo "This is a convex polygon "
let rect = Rect(width: 2.0, height:2.0)
var drawing = Drawing()
drawing.shape = rect
display_type_of drawing.shape # This is a rectangle
I just read your answers carefully and I will tests these solutions, my project is much more complex than the example.
Thank you all for taking the time to write such insightful answers.
If you want your program to spawn objects during runtime you'll need ref objects.
Not really. For example, when you have a single value object and add() it to a seq, it is copied -- similar as C++ vector does copy when you use std::vector::push_back(). You can modify your value object then, and add next copy. Similar for other data structures like hash or tree. Of course seq uses a internal buffer allocated dynamically on heap, but we may not really notice that.
kcvinu, of course you can use ref objects and inheritance for GUI stuff and much more. Ref objects are less efficient due to indirection of course, you have the ref on the stack, which points to the data on the heap, which is most often not in cache. That may make a difference of a few 100 clock cycles, which is less than 100 ns. May be much for high performance computing and games, not that much for us ordinary people.
If when assigning an object you don't want it to be copied, ref are a natural fit. For example a resource like database connection or a network socket, when you pass those around you don't want to suddenly create a new one, so use ref for those. Note that you usually don't create those in a tight loop.
In some cases you have to use ref as well like for tree data structures or linked lists or for inheritance.
The important thing is understanding the tradeoff and making it a conscious choice. Well my view is probably skewed anyway as I mostly deal with plain old data, very low-level programming with high performance need.