www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is it possible to store properties via opDispatch using tuples?

reply "Gary Willoughby" <dev nomad.so> writes:
What i would like to achieve is to dynamically assign and 
retrieve properties without declaring them first. For example:

class T
{
     public this()
     {
         this.foo = "bar";
     }
}

Ordinarily the above won't compile because 'foo' hasn't been 
declared but with opDispatch i can handle this. The problem is 
how do i handle different types of each property.

I was thinking about something like this:

class A
{
}

class B : A
{
}

class C : B
{
}

class T
{
     private Tuple[string] _properties;

     public this()
     {
         this.a = new A();
         this.b = new B();
         this.c = new C();
     }

     public void opDispatch(string name, T)(T element)
     {
         this._properties[name] = Tuple(T, element);
     }

     public auto opDispatch(string name)()
     {
         if (name in this._properties)
         {
             return 
cast(this._properties[name][0])this._properties[name][1];
         }
     }
}

Of course this doesn't compile but is this actually possible? 
i.e. storing the type and data. Then on retrieval returning the 
correct data cast to the correct type? All done dynamically 
without any property being pre-declared.
Jan 16 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Gary Willoughby:

 What i would like to achieve is to dynamically assign and 
 retrieve properties without declaring them first.
Do you mean something like this? http://rosettacode.org/wiki/Add_a_variable_to_a_class_instance_at_runtime#D Bye, bearophile
Jan 16 2014
parent reply "Gary Willoughby" <dev nomad.so> writes:
On Thursday, 16 January 2014 at 21:48:14 UTC, bearophile wrote:
 Gary Willoughby:

 What i would like to achieve is to dynamically assign and 
 retrieve properties without declaring them first.
Do you mean something like this? http://rosettacode.org/wiki/Add_a_variable_to_a_class_instance_at_runtime#D Bye, bearophile
Yes exactly but i would like to preserve the types of differently typed properties so i can cast them back on retrieval.
Jan 16 2014
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Gary Willoughby:

 Yes exactly but i would like to preserve the types of 
 differently typed properties so i can cast them back on 
 retrieval.
A Variant needs to keep the type of the value you have stored in, look at the Variant documentation. (But the usual limitations of a ahead-of-time compiled statically typed language apply.) Bye, bearophile
Jan 16 2014
next sibling parent "Gary Willoughby" <dev nomad.so> writes:
On Thursday, 16 January 2014 at 22:31:01 UTC, bearophile wrote:
 Gary Willoughby:

 Yes exactly but i would like to preserve the types of 
 differently typed properties so i can cast them back on 
 retrieval.
A Variant needs to keep the type of the value you have stored in, look at the Variant documentation. (But the usual limitations of a ahead-of-time compiled statically typed language apply.) Bye, bearophile
I didn't think about variants but would that also need coercing into the correct type?
Jan 16 2014
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2014-01-16 23:31, bearophile wrote:

 A Variant needs to keep the type of the value you have stored in, look
 at the Variant documentation. (But the usual limitations of a
 ahead-of-time compiled statically typed language apply.)
A Varian won't work, since it only stores a TypeInfo, not the static type. But perhaps that's what you're saying. -- /Jacob Carlborg
Jan 16 2014
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-16 21:26, Gary Willoughby wrote:
 What i would like to achieve is to dynamically assign and retrieve
 properties without declaring them first. For example:

 class T
 {
      public this()
      {
          this.foo = "bar";
      }
 }

 Ordinarily the above won't compile because 'foo' hasn't been declared
 but with opDispatch i can handle this. The problem is how do i handle
 different types of each property.

 I was thinking about something like this:

 class A
 {
 }

 class B : A
 {
 }

 class C : B
 {
 }

 class T
 {
      private Tuple[string] _properties;

      public this()
      {
          this.a = new A();
          this.b = new B();
          this.c = new C();
      }

      public void opDispatch(string name, T)(T element)
      {
          this._properties[name] = Tuple(T, element);
      }

      public auto opDispatch(string name)()
      {
          if (name in this._properties)
          {
              return
 cast(this._properties[name][0])this._properties[name][1];
          }
      }
 }

 Of course this doesn't compile but is this actually possible? i.e.
 storing the type and data. Then on retrieval returning the correct data
 cast to the correct type? All done dynamically without any property
 being pre-declared.
I have no way of seeing this work. The problem is you need to somehow store the static type revived in opDispatch. But to store an unknown type as an instance variable you need to use a template class. -- /Jacob Carlborg
Jan 16 2014
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jan 17, 2014 at 08:56:18AM +0100, Jacob Carlborg wrote:
 On 2014-01-16 21:26, Gary Willoughby wrote:
What i would like to achieve is to dynamically assign and retrieve
properties without declaring them first. For example:

class T
{
     public this()
     {
         this.foo = "bar";
     }
}

Ordinarily the above won't compile because 'foo' hasn't been declared
but with opDispatch i can handle this. The problem is how do i handle
different types of each property.

I was thinking about something like this:

class A
{
}

class B : A
{
}

class C : B
{
}

class T
{
     private Tuple[string] _properties;

     public this()
     {
         this.a = new A();
         this.b = new B();
         this.c = new C();
     }

     public void opDispatch(string name, T)(T element)
     {
         this._properties[name] = Tuple(T, element);
     }

     public auto opDispatch(string name)()
     {
         if (name in this._properties)
         {
             return
cast(this._properties[name][0])this._properties[name][1];
         }
     }
}

Of course this doesn't compile but is this actually possible? i.e.
storing the type and data. Then on retrieval returning the correct data
cast to the correct type? All done dynamically without any property
being pre-declared.
I have no way of seeing this work. The problem is you need to somehow store the static type revived in opDispatch. But to store an unknown type as an instance variable you need to use a template class.
[...] Couldn't you just return a Variant? I thought this is what Variants are made for. T -- "The whole problem with the world is that fools and fanatics are always so certain of themselves, but wiser people so full of doubts." -- Bertrand Russell. "How come he didn't put 'I think' at the end of it?" -- Anonymous
Jan 17 2014
parent reply "Gary Willoughby" <dev nomad.so> writes:
On Friday, 17 January 2014 at 15:56:46 UTC, H. S. Teoh wrote:
 Couldn't you just return a Variant? I thought this is what 
 Variants are
 made for.


 T
Yes but then i would need to coerce it to get it's underlying type.
Jan 17 2014
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jan 17, 2014 at 05:29:14PM +0000, Gary Willoughby wrote:
 On Friday, 17 January 2014 at 15:56:46 UTC, H. S. Teoh wrote:
Couldn't you just return a Variant? I thought this is what Variants
are made for.


T
Yes but then i would need to coerce it to get it's underlying type.
But isn't that what you'd have to do anyway? I mean, how else would the following code work? class DynClass { ... auto opDispatch(string field)() { return dotDotDotMagic(); } } void main(string[] args) { auto d = new DynClass(); if (args[1] == "int") d.abc = 123; // d.abc = int else d.abc = "xyz"; // d.abc = string // Suppose this somehow works: auto x = d.abc; // what's the type of x? } Since the type of x must be known at compile-time, but the type of d.abc can't be known until runtime, the above code can't possibly work unless d.abc returns a Variant. It's simply not possible for a runtime-determined type to be put into a variable of compile-time determined type without some kind of runtime check. Now I'm not sure if Variant allows assignment to a static type, but in theory this should be possible: // assume d.abc returns a Variant int x = d.abc; // will assert if d.abc doesn't hold an int at runtime T -- There are 10 kinds of people in the world: those who can count in binary, and those who can't.
Jan 17 2014
next sibling parent reply "Gary Willoughby" <dev nomad.so> writes:
On Friday, 17 January 2014 at 17:50:54 UTC, H. S. Teoh wrote:
 On Fri, Jan 17, 2014 at 05:29:14PM +0000, Gary Willoughby wrote:
 On Friday, 17 January 2014 at 15:56:46 UTC, H. S. Teoh wrote:
Couldn't you just return a Variant? I thought this is what 
Variants
are made for.


T
Yes but then i would need to coerce it to get it's underlying type.
But isn't that what you'd have to do anyway? I mean, how else would the following code work? class DynClass { ... auto opDispatch(string field)() { return dotDotDotMagic(); } } void main(string[] args) { auto d = new DynClass(); if (args[1] == "int") d.abc = 123; // d.abc = int else d.abc = "xyz"; // d.abc = string // Suppose this somehow works: auto x = d.abc; // what's the type of x? } Since the type of x must be known at compile-time, but the type of d.abc can't be known until runtime, the above code can't possibly work unless d.abc returns a Variant. It's simply not possible for a runtime-determined type to be put into a variable of compile-time determined type without some kind of runtime check. Now I'm not sure if Variant allows assignment to a static type, but in theory this should be possible: // assume d.abc returns a Variant int x = d.abc; // will assert if d.abc doesn't hold an int at runtime T
In the example in my original post the types are known at compile time but they are different between properties. I wondered if there was a solution to storing and retrieving while preserving the original type via opDispatch. I have a working solution but it only stores the base type. So i f i have an inheritance chain like this: A -> B -> C I can store properties of type A, B and C but when i retrieve them they all come back as A because of array covariance. I wonder if there is a way of casting them automagically to the correct type.
Jan 17 2014
next sibling parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
 I can store properties of type A, B and C but when i retrieve 
 them they all come back as A because of array covariance. I 
 wonder if there is a way of casting them automagically to the 
 correct type.
So you basically want to declare a classmember for every name opDispatch is called with? That is not possible; I thought about declaring it static in opDispatch itself, but than you can only have one instance. Next thought: Declare a static hashmap and store the values there, but then you could just use std.variant.
Jan 17 2014
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jan 17, 2014 at 07:08:54PM +0000, Tobias Pankrath wrote:
I can store properties of type A, B and C but when i retrieve them
they all come back as A because of array covariance. I wonder if
there is a way of casting them automagically to the correct type.
So you basically want to declare a classmember for every name opDispatch is called with? That is not possible; I thought about declaring it static in opDispatch itself, but than you can only have one instance. Next thought: Declare a static hashmap and store the values there, but then you could just use std.variant.
One of the reasons this breaks down is because of situations like this: class MyClass { ... auto opDispatch(string op)() { return ... /* assume we magically return the right type here */; } } // Which of these assignments to 'abc' should determine its // type? What if the function that gets called is determined at // runtime only? What if they are in two separately-compiled // modules? void fun(MyClass o) { o.abc = 123; } void gun(MyClass o) { o.abc = "xyz"; } auto x = o.abc; // what's the type of x? Another problematic case: auto o = new MyClass; auto p = new MyClass; o.abc = 123; p.abc = "xyz"; // Problem: now the return type of opDispatch cannot be // determined at compile-time. Now, if you decide beforehand that "abc" is always an int, and "def" is always a string, then this problem is avoided: o.abc = 123; // OK p.abc = 123; // OK o.abc = "xyz"; // compile error p.abc = "xyz"; // compile error o.def = "xyz"; // OK p.def = "xyz"; // OK o.def = 123; // compile error p.def = 123; // compile error T -- Freedom of speech: the whole world has no right *not* to hear my spouting off!
Jan 17 2014
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jan 17, 2014 at 06:49:25PM +0000, Gary Willoughby wrote:
[...]
 In the example in my original post the types are known at compile
 time but they are different between properties. I wondered if there
 was a solution to storing and retrieving while preserving the
 original type via opDispatch.
 
 I have a working solution but it only stores the base type. So i f i
 have an inheritance chain like this:
 
 A -> B -> C
 
 I can store properties of type A, B and C but when i retrieve them
 they all come back as A because of array covariance. I wonder if
 there is a way of casting them automagically to the correct type.
Is it possible, at compile-time, to determine the type just from the property name alone? If not (i.e., different class instances may have a property named "abc" map to int or string, respectively), then this is not possible, because the return type of opDispatch can only depend on its compile-time parameter, that is, the property name. If there's a compile-time procedure for determining the type of a property solely from its name, then yes, this is possible. What you do is to first determine the return type from the property name (at compile-time!), hash the .mangleof of this type, and store an AA of AA's of property values, with the outer AA keyed by the hash value. Something like this: class MyClass { Variant[string][size_t] props; auto opDispatch(string ident)() { alias returnType = ... /* determine type from ident */; enum id = hashOf(T.mangleof); // unique ID for T return cast(returnType)props[id][ident]; } } The success of this depends on whether it's possible to determine the return type from the property name (at compile-time), though. If that's not possible, then this doesn't work. T -- Acid falls with the rain; with love comes the pain.
Jan 17 2014
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-17 18:49, H. S. Teoh wrote:

 Now I'm not sure if Variant allows assignment to a static type, but in
 theory this should be possible:

 	// assume d.abc returns a Variant
 	int x = d.abc; // will assert if d.abc doesn't hold an int at runtime
It doesn't work. Variant doesn't retain the static type, which is needed in this case. -- /Jacob Carlborg
Jan 17 2014
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jan 17, 2014 at 09:08:50PM +0100, Jacob Carlborg wrote:
 On 2014-01-17 18:49, H. S. Teoh wrote:
 
Now I'm not sure if Variant allows assignment to a static type, but
in theory this should be possible:

	// assume d.abc returns a Variant
	int x = d.abc; // will assert if d.abc doesn't hold an int at runtime
It doesn't work. Variant doesn't retain the static type, which is needed in this case.
[...] Is that because D doesn't have implicit casting via opCast? -- because, conceivably, if x = d.abc; gets lowered to x = d.opCast!(typeof(x))(d.abc); then the above can be made to work. T -- If you look at a thing nine hundred and ninety-nine times, you are perfectly safe; if you look at it the thousandth time, you are in frightful danger of seeing it for the first time. -- G. K. Chesterton
Jan 17 2014
parent Jacob Carlborg <doob me.com> writes:
On 2014-01-17 22:14, H. S. Teoh wrote:

 Is that because D doesn't have implicit casting via opCast? -- because,
 conceivably, if

 	x = d.abc;
 	
 gets lowered to

 	x = d.opCast!(typeof(x))(d.abc);
 	
 then the above can be made to work.
I don't know, maybe. -- /Jacob Carlborg
Jan 17 2014