www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Having trouble with objects.

reply Jarrod <qwerty ytre.wq> writes:
Hey all.
As I might have mentioned a while ago, I have a config library that I 
wrapped in D classes a while ago.
While it works perfectly fine, it's currently running somewhat 
inefficiently and I wish to fix this. 

The problem: The library currently contains two classes. A root class and 
a node class. To load/create a config, one would create a root class and 
tie it to a file. Each key=value pair in the config can be accessed by 
passing the value's "path" to the root object, which will return a node. 
Herein lies the issue.
Nodes are very short lived objects, and are often created and discarded 
very quickly. It would make far more sense if a node was a struct, as 
classes being uses like this tend to needlessly waste memory. The problem 
is I need to create the node in the root object (or in a parent node) and 
pass it back to the caller which would be more efficient to do with 
classes. In C++, what I would do is pass the back argument to the 
struct's constructor, which would create the struct and pass it back 
rather efficiently, and the node would be destroyed when scope is left. I 
doubt this is possible in D since structs don't have constructors.
So what should I do?

Another issue: Nodes can contain different data types. As such, I need to 
access their contents using an overloaded function 'getValue' which will 
use the passed parameter to determine what type of data the node holds 
and then pass it back. Yes, this sucks. Since DMD doesn't have implicit, 
multiple opCast overloads it appears that the only other solution is to 
have a different node type for each type of data. I guess this can be 
done with templates but nodes are created at runtime, not compile time. 
Different node types also mean I need to know what type of node the root 
config will return and I can't always do that since configs are user 
editable.
Is there anything I can do about this?


After writing this post out and looking at my situation I kind of get the 
feeling I'm just going to have to grit my teeth and bear it for now 
because it looks like the only workarounds are probably going to be 
elaborate hacks. But hey, if you can think of something I'm all ears.

Thanks,
Jarrod.

PS pleaseeee add multiple implicit opCasts and struct constructors to D 
soon, Walter. It would be really helpful, especially for making and using 
abstract data types.

PSS It's kind of late so I'm sorry if this post has any grammatical 
errors.
Mar 18 2008
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Jarrod" <qwerty ytre.wq> wrote in message 
news:frobsv$960$1 digitalmars.com...
 Hey all.
 As I might have mentioned a while ago, I have a config library that I
 wrapped in D classes a while ago.
 While it works perfectly fine, it's currently running somewhat
 inefficiently and I wish to fix this.

 The problem: The library currently contains two classes. A root class and
 a node class. To load/create a config, one would create a root class and
 tie it to a file. Each key=value pair in the config can be accessed by
 passing the value's "path" to the root object, which will return a node.
 Herein lies the issue.
 Nodes are very short lived objects, and are often created and discarded
 very quickly. It would make far more sense if a node was a struct, as
 classes being uses like this tend to needlessly waste memory. The problem
 is I need to create the node in the root object (or in a parent node) and
 pass it back to the caller which would be more efficient to do with
 classes. In C++, what I would do is pass the back argument to the
 struct's constructor, which would create the struct and pass it back
 rather efficiently, and the node would be destroyed when scope is left. I
 doubt this is possible in D since structs don't have constructors.
 So what should I do?

NRVO (Named Return Value Optimization) to the rescue. There are no struct constructors, but it doesn't mean the compiler can't optimize this out. When you return a struct from a function, chances are very good that it will not be pushed onto the stack, but will be constructed in place. So even though it looks like you're allocating (on the stack) and filling out a struct in the function, the compiler will rewrite the function so that a pointer to the struct is passed as a parameter and the function fills out the struct in place.
 Another issue: Nodes can contain different data types. As such, I need to
 access their contents using an overloaded function 'getValue' which will
 use the passed parameter to determine what type of data the node holds
 and then pass it back. Yes, this sucks. Since DMD doesn't have implicit,
 multiple opCast overloads it appears that the only other solution is to
 have a different node type for each type of data. I guess this can be
 done with templates but nodes are created at runtime, not compile time.
 Different node types also mean I need to know what type of node the root
 config will return and I can't always do that since configs are user
 editable.
 Is there anything I can do about this?

Well, what would you have to do in C++? So you have multiple implicit cast overloads, but how is the node represented? As a tagged union? As a string which is converted to the correct type upon use? Is there a member in the node which represents its type? Don't you still need to check that the node is of the correct type before getting its value anyway, by which time you already know the type and the implicit cast is next to useless? It sounds like your node is wanting to be a variant. In D1, there's a variant type in std.boxer called Box, and in D2, there's std.variant. (In Tango, there's tango.core.Variant.) Or, if you want to implement it yourself, it's not that hard, it's basically a tagged union with some methods to check the type and convert it to a desired value.
Mar 18 2008
parent reply Jarrod <qwerty ytre.wq> writes:
On Tue, 18 Mar 2008 09:05:19 -0400, Jarrett Billingsley wrote:

 NRVO (Named Return Value Optimization) to the rescue.  There are no
 struct constructors, but it doesn't mean the compiler can't optimize
 this out. When you return a struct from a function, chances are very
 good that it will not be pushed onto the stack, but will be constructed
 in place.  So even though it looks like you're allocating (on the stack)
 and filling out a struct in the function, the compiler will rewrite the
 function so that a pointer to the struct is passed as a parameter and
 the function fills out the struct in place.

This is good to hear. I guess I won't feel (as) guilty about having to pass back a struct then.
 Well, what would you have to do in C++?  So you have multiple implicit
 cast overloads, but how is the node represented?  As a tagged union?  As
 a string which is converted to the correct type upon use?  Is there a
 member in the node which represents its type?  Don't you still need to
 check that the node is of the correct type before getting its value
 anyway, by which time you already know the type and the implicit cast is
 next to useless?
 
 It sounds like your node is wanting to be a variant.  In D1, there's a
 variant type in std.boxer called Box, and in D2, there's std.variant. 
 (In Tango, there's tango.core.Variant.)  Or, if you want to implement it
 yourself, it's not that hard, it's basically a tagged union with some
 methods to check the type and convert it to a desired value.

Well yeah the node acts much like a variant. And yes, it internally uses a union with an enum to keep track of the stored value type. It tries its best to convert the stored value to the desired type upon being called. My problem isn't with storing or handling the abstract data, it's that I need to use an overloaded function call to get the value. It's quite ugly and results in a lot of temporary variables being created when they don't need to be created, like for instance if I wanted to pass a number to a function:
int temp;
myNode.getValue(temp);
someFunc(temp);

In C++ with overloaded return types I can just use:
someFunc(myNode); //Sometimes a cast is needed, but it's still better

Okay, it's not exactly a show stopper, it's just a bit ugly and somewhat inconvenient. With that said, it looks like boxer/variant have workarounds for this using templates. I'll look at their source and see how it's done. Thanks for your reply, it was quite helpful.
Mar 18 2008
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Jarrod" <qwerty ytre.wq> wrote in message 
news:frpi9g$1avh$1 digitalmars.com...

 Well yeah the node acts much like a variant. And yes, it internally uses
 a union with an enum to keep track of the stored value type.
 It tries its best to convert the stored value to the desired type upon
 being called. My problem isn't with storing or handling the abstract
 data, it's that I need to use an overloaded function call to get the
 value. It's quite ugly and results in a lot of temporary variables being
 created when they don't need to be created, like for instance if I wanted
 to pass a number to a function:
int temp;
myNode.getValue(temp);
someFunc(temp);


This is precisely where a templated method would come in, as you've noticed: someFunc(myNode.to!(int)); Sure, you don't get the cast syntax, but it's actually less typing and doesn't require fancy languages support. Implicit casting in this case would be nice though. D2 is apparently getting overloadable opImplicitCast at some nebulous time in the future.
Mar 18 2008
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Jarrod" wrote
 Hey all.
 As I might have mentioned a while ago, I have a config library that I
 wrapped in D classes a while ago.
 While it works perfectly fine, it's currently running somewhat
 inefficiently and I wish to fix this.

 The problem: The library currently contains two classes. A root class and
 a node class. To load/create a config, one would create a root class and
 tie it to a file. Each key=value pair in the config can be accessed by
 passing the value's "path" to the root object, which will return a node.
 Herein lies the issue.
 Nodes are very short lived objects, and are often created and discarded
 very quickly. It would make far more sense if a node was a struct, as
 classes being uses like this tend to needlessly waste memory. The problem
 is I need to create the node in the root object (or in a parent node) and
 pass it back to the caller which would be more efficient to do with
 classes. In C++, what I would do is pass the back argument to the
 struct's constructor, which would create the struct and pass it back
 rather efficiently, and the node would be destroyed when scope is left. I
 doubt this is possible in D since structs don't have constructors.
 So what should I do?

Have you tried static opCall? It's sort of like a struct constructor: struct S { int val; static S opCall(int arg) { S retval; retval.val = arg; return retval; } } S func() { return S(1); } -Steve
Mar 18 2008
parent Jarrod <qwerty ytre.wq> writes:
On Tue, 18 Mar 2008 12:00:29 -0400, Steven Schveighoffer wrote:
 
 Have you tried static opCall?  It's sort of like a struct constructor:
 
 struct S
 {
   int val;
   static S opCall(int arg)
   {
       S retval;
       retval.val = arg;
       return retval;
   }
 }
 
 S func()
 {
     return S(1);
 }
 
 -Steve

Hey, that is actually really handy. I'll have to remember it. Thanks Steve.
Mar 18 2008