www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Is there any way to create something like this?

reply "RenatoUtsch" <renatoutsch gmail.com> writes:
Hello,

I was trying to imagine how to do a thing such the one as below 
(or similar), because it would be great for the project I am 
creating...

Is there any way to create some kind of object that resembles 
different objects (like a superclass with the subclasses, but 
with transparent access to the subclasses new methods)?

For example:

[code]
void main() {
     GeneralLibrary library = createLibrary("1.0");
     GeneralLibrary library11 = createLibrary("1.1");
     GeneralLibrary library12 = createLibrary("1.2");
     GeneralLibrary library2 = createLibrary("2.0");

     library.func1(); // Should work
     library.func11(); // Should fail
     library.func12(); // Should fail
     library3.func2(); // Should fail

     library2.func1(); // Should work
     library2.func11(); // Should work
     library21.func12(); // Should fail
     library3.func2(); // Should fail

     library2.func1(); // Should work
     library2.func11(); // Should work
     library2.func12(); // Should work
     library3.func2(); // Should fail

     library3.func1(); // Should fail
     library3.func11(); // Should fail
     library3.func12(); // Should fail
     library3.func2(); // Should work
}

class Library_1_0 {
     void func1() {}
}

class Library_1_1 : Library_1_0 {
     void func11() {}
}

class Library_1_2 : Library_1_1 {
     void func12() {}
}

class Library_2_0 {
     void func2() {}
}
[/code]

Is there any way to make the GeneralLibrary class and the 
createLibrary() (or even better, make the GeneralLibrary 
constructor do that) to work with this kind of construction?
Oct 21 2012
next sibling parent reply "RenatoUtsch" <renatoutsch gmail.com> writes:
Sorry, the code is wrong, the fixed code is:

void main() {
     GeneralLibrary library = createLibrary("1.0");
     GeneralLibrary library11 = createLibrary("1.1");
     GeneralLibrary library12 = createLibrary("1.2");
     GeneralLibrary library2 = createLibrary("2.0");

     library.func1(); // Should work
     library.func11(); // Should fail
     library.func12(); // Should fail
     library.func2(); // Should fail

     library11.func1(); // Should work
     library11.func11(); // Should work
     library11.func12(); // Should fail
     library11.func2(); // Should fail

     library12.func1(); // Should work
     library12.func11(); // Should work
     library12.func12(); // Should work
     library12.func2(); // Should fail

     library2.func1(); // Should fail
     library2.func11(); // Should fail
     library2.func12(); // Should fail
     library2.func2(); // Should work
}

class Library_1_0 {
     void func1() {}
}

class Library_1_1 : Library_1_0 {
     void func11() {}
}

class Library_1_2 : Library_1_1 {
     void func12() {}
}

class Library_2_0 {
     void func2() {}
}
Oct 21 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-10-21 16:08, RenatoUtsch wrote:
 Sorry, the code is wrong, the fixed code is:

 void main() {
      GeneralLibrary library = createLibrary("1.0");
      GeneralLibrary library11 = createLibrary("1.1");
      GeneralLibrary library12 = createLibrary("1.2");
      GeneralLibrary library2 = createLibrary("2.0");

      library.func1(); // Should work
      library.func11(); // Should fail
      library.func12(); // Should fail
      library.func2(); // Should fail

      library11.func1(); // Should work
      library11.func11(); // Should work
      library11.func12(); // Should fail
      library11.func2(); // Should fail

      library12.func1(); // Should work
      library12.func11(); // Should work
      library12.func12(); // Should work
      library12.func2(); // Should fail

      library2.func1(); // Should fail
      library2.func11(); // Should fail
      library2.func12(); // Should fail
      library2.func2(); // Should work
 }

 class Library_1_0 {
      void func1() {}
 }

 class Library_1_1 : Library_1_0 {
      void func11() {}
 }

 class Library_1_2 : Library_1_1 {
      void func12() {}
 }

 class Library_2_0 {
      void func2() {}
 }
You can probably make some kind of proxy using opDispatch: http://dlang.org/operatoroverloading.html#Dispatch -- /Jacob Carlborg
Oct 21 2012
prev sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 21 October 2012 at 13:10:16 UTC, RenatoUtsch wrote:
 Is there any way to make the GeneralLibrary class and the 
 createLibrary() (or even better, make the GeneralLibrary 
 constructor do that) to work with this kind of construction?
Yes, there's ways to do that, but they might not be what you had in mind, because you'll either have to use different static types for each version or make GeneralLibrary a dynamic type, which means it will error at runtime rather than compile time. Either way, createLibrary will probably have to be a template. Here's an example of a static wrapper: .... I just accidentally saved over the static example with the dynamic example. Ugh. I guess what I'll do is link it in: http://arsdnet.net/dcode/example_dynamic.d Look at the function createLibrary and pretend the dynamicFunctionCall method wasn't there. That's what the static example looked like - just opDispatch. In main, to use the static checks, you must say auto library11 instead of GeneralLibrary. The interface is only useful for dynamic calls. auto gives you the wrapper class with static checks. This isn't very interesting because the wrapper adds nothing; you might as well just construct the original object. But you could change the function in the wrapper to do something else. But anyway, each wrapper class created inherits from a GeneralLibrary interface, so you could pass them around.. but since the interface does nothing, you really have to use auto on the return type to actually use the class. If you aren't doing anything with the wrapper, you could also just alias GeneralLibrary to be the newest version of the actual class too. Not much fun so let's look at a dynamic option. This will suck too, but in different ways. This one won't work with overloaded functions, default parameters on functions (sad, fixable in some cases, but not all), has a speed hit (you could help this a bit by optimizing dynamicFunctionCall, but it will always have some slowdown), and returns Variants instead of the original type, which is kinda annoying. And, of course, it is a dynamic call, so any failures will only happen at runtime. Here's some code: http://arsdnet.net/dcode/example_dynamic.d I haven't tested it fully, but it seems to throw exceptions at the right times for the simple functions in here. This code is messier and includes a compiler bug hack (I think)... but it worked. There's some comments in there to talk about what it does. End result: given GeneralLibrary library11 = createLibrary!("1.1"); library11.func1(); // Should work library11.func11(); // Should work library11.func12(); // Should fail // line 103 Will throw at runtime: test4.NoSuchMethodException test4.d(103): No such method: func12 And it *should* work with types too, still doing strong type checks, but doing them at runtime for the arguments. All return values for these functions are wrapped in Variants, so you'll have to pull them out dynamically too.. You could probably combine these two examples and have static types if you use auto and dynamic if you use the GeneralLibrary interface. Another option might be to have the createLibrary function be aware of all the versions - you'd have to hard code a list that it can read at compile time - and then do the kind of thing in the static example, but trying to cast to the newer versions of the class and throwing if it fails. Then you'd keep the static types, but get dynamic checks on if the method is available. This would be just a loop, cast to a newer class, if not null and the call compiles, call the function. I'm out of screwing around time this morning so I won't write this one up, but it should be doable.
Oct 21 2012
parent "RenatoUtsch" <renatoutsch gmail.com> writes:
That static version you made Adam was just perfect for what I 
need!

Thanks for the help!

Renato

On Sunday, 21 October 2012 at 15:19:24 UTC, Adam D. Ruppe wrote:
 On Sunday, 21 October 2012 at 13:10:16 UTC, RenatoUtsch wrote:
 Is there any way to make the GeneralLibrary class and the 
 createLibrary() (or even better, make the GeneralLibrary 
 constructor do that) to work with this kind of construction?
Yes, there's ways to do that, but they might not be what you had in mind, because you'll either have to use different static types for each version or make GeneralLibrary a dynamic type, which means it will error at runtime rather than compile time. Either way, createLibrary will probably have to be a template. Here's an example of a static wrapper: .... I just accidentally saved over the static example with the dynamic example. Ugh. I guess what I'll do is link it in: http://arsdnet.net/dcode/example_dynamic.d Look at the function createLibrary and pretend the dynamicFunctionCall method wasn't there. That's what the static example looked like - just opDispatch. In main, to use the static checks, you must say auto library11 instead of GeneralLibrary. The interface is only useful for dynamic calls. auto gives you the wrapper class with static checks. This isn't very interesting because the wrapper adds nothing; you might as well just construct the original object. But you could change the function in the wrapper to do something else. But anyway, each wrapper class created inherits from a GeneralLibrary interface, so you could pass them around.. but since the interface does nothing, you really have to use auto on the return type to actually use the class. If you aren't doing anything with the wrapper, you could also just alias GeneralLibrary to be the newest version of the actual class too. Not much fun so let's look at a dynamic option. This will suck too, but in different ways. This one won't work with overloaded functions, default parameters on functions (sad, fixable in some cases, but not all), has a speed hit (you could help this a bit by optimizing dynamicFunctionCall, but it will always have some slowdown), and returns Variants instead of the original type, which is kinda annoying. And, of course, it is a dynamic call, so any failures will only happen at runtime. Here's some code: http://arsdnet.net/dcode/example_dynamic.d I haven't tested it fully, but it seems to throw exceptions at the right times for the simple functions in here. This code is messier and includes a compiler bug hack (I think)... but it worked. There's some comments in there to talk about what it does. End result: given GeneralLibrary library11 = createLibrary!("1.1"); library11.func1(); // Should work library11.func11(); // Should work library11.func12(); // Should fail // line 103 Will throw at runtime: test4.NoSuchMethodException test4.d(103): No such method: func12 And it *should* work with types too, still doing strong type checks, but doing them at runtime for the arguments. All return values for these functions are wrapped in Variants, so you'll have to pull them out dynamically too.. You could probably combine these two examples and have static types if you use auto and dynamic if you use the GeneralLibrary interface. Another option might be to have the createLibrary function be aware of all the versions - you'd have to hard code a list that it can read at compile time - and then do the kind of thing in the static example, but trying to cast to the newer versions of the class and throwing if it fails. Then you'd keep the static types, but get dynamic checks on if the method is available. This would be just a loop, cast to a newer class, if not null and the call compiles, call the function. I'm out of screwing around time this morning so I won't write this one up, but it should be doable.
Oct 22 2012