www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - __traits and determining all overridable functions

reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
Hi,

I was thinking of making a template that derives from an object, but 
intercepts all calls to virtual functions automatically.  This could be used 
for something like RMI or .net Remoting to allow a call to either execute a 
method locally or in an object on a server if connected.

I thought maybe I could use __traits to determine all virtual functions, 
then override them using a mixin.  I thought __traits was a compile-time 
function, and so I thought I could create a compile-time-function that 
returns a string defining all virtual functions.

However, in doing so I find that the function does not work correctly, and 
in addition, the argument to __traits for isVirtualFunction does not take a 
string, but the result of __traits allMembers is an array of strings.  I 
think it would be more useful if isVirtualFunction and the other isXX would 
take either a string or the direct statement.  For example:

__traits(isVirtualFunction, "A.foo")
would be the same as:
__traits(isVirtualFunction, A.foo)

Then I could do:

foreach(x; __traits(allMembers, A))
    if(__traits(isVirtualfunction, "A." ~ x))
        // append to string that redefines virtual function.

Second, the mixin doesn't work.  I hard-coded the function to output the 
string I was trying to build.  Therefore, the compiler should be able to 
execute the method in compile time, and pre-determine the string to return. 
But the mixin just defines the function name that I am calling as a member. 
i.e., I define my function above as:

string redefFunctions(T)();

and my mixin in the class is:

mixin(redefFunctions!(A)());

But when I build my class, there is a member redefFunctions!(A) instead of 
the list of virtual functions I expected to override.

Any ideas?  Is there another way this will work?  If not, is it possible to 
make it so it would work :) ?

-Steve 
Nov 12 2007
parent reply Christopher Wright <dhasenan gmail.com> writes:
Steven Schveighoffer wrote:
 Hi,
 
 I was thinking of making a template that derives from an object, but 
 intercepts all calls to virtual functions automatically.  This could be used 
 for something like RMI or .net Remoting to allow a call to either execute a 
 method locally or in an object on a server if connected.

http://dsource.org/projects/dmocks/browser/trunk/dmocks Take a look at MockObject.d and MethodMock.d -- they have pretty much everything you need. Maybe I should extract them out.... One caveat is constructors, though. Your proxied class isn't guaranteed a default constructor. You have to use, as I recall, ParameterTypeTuple!(T._ctor) to get the arguments, and supply a constructor of the appropriate type. Hm. I thought I had solved that problem in my library, but now I see I commented that test out. Bad me |:(
 I thought maybe I could use __traits to determine all virtual functions, 
 then override them using a mixin.  I thought __traits was a compile-time 
 function, and so I thought I could create a compile-time-function that 
 returns a string defining all virtual functions.

It does, and it works. See above :)
 However, in doing so I find that the function does not work correctly, and 
 in addition, the argument to __traits for isVirtualFunction does not take a 
 string, but the result of __traits allMembers is an array of strings.  I 
 think it would be more useful if isVirtualFunction and the other isXX would 
 take either a string or the direct statement.  For example:
 
 __traits(isVirtualFunction, "A.foo")
 would be the same as:
 __traits(isVirtualFunction, A.foo)

class A { final void foo (); int foo (int i); } What to return? __traits(isVirtualFunction, A.foo) is ambiguous as well. I don't recommend using it, and I don't use it.
 Then I could do:
 
 foreach(x; __traits(allMembers, A))
     if(__traits(isVirtualfunction, "A." ~ x))
         // append to string that redefines virtual function.

You can't foreach an array at compile time, and __traits(allMembers) returns an array. You can index an array at compile time, but again, you can't use a while loop at compile time. You have to use recursion. Eventually, static foreach should be able to do this. Or you can bug Walter about changing allMembers to return a tuple of strings.
 Second, the mixin doesn't work.  I hard-coded the function to output the 
 string I was trying to build.  Therefore, the compiler should be able to 
 execute the method in compile time, and pre-determine the string to return. 
 But the mixin just defines the function name that I am calling as a member. 
 i.e., I define my function above as:
 
 string redefFunctions(T)();
 
 and my mixin in the class is:
 
 mixin(redefFunctions!(A)());
 
 But when I build my class, there is a member redefFunctions!(A) instead of 
 the list of virtual functions I expected to override.

Not sure what the problem there is. If you omit the parentheses around the template method call, you'll definitely get the result you describe (that had me hung up for a bit), but your example has the parentheses.
 Any ideas?  Is there another way this will work?  If not, is it possible to 
 make it so it would work :) ?
 
 -Steve 
 
 

Good luck!
Nov 12 2007
next sibling parent Christopher Wright <dhasenan gmail.com> writes:
Christopher Wright wrote:
 Hm. I thought I had solved that problem in my library, but now I see I 
 commented that test out. Bad me |:(

The constructor problem is solved, and I converted all my small integration tests into large integration tests (that is, instead of testing half the code with each test, they test all the code -- better than nothing). But that isn't a great feat, once you have the rest.
Nov 12 2007
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Christopher Wright" wrote
 http://dsource.org/projects/dmocks/browser/trunk/dmocks
 Take a look at MockObject.d and MethodMock.d -- they have pretty much 
 everything you need. Maybe I should extract them out....

Excellent work! I think this is exactly what I was looking for.
 However, in doing so I find that the function does not work correctly, 
 and in addition, the argument to __traits for isVirtualFunction does not 
 take a string, but the result of __traits allMembers is an array of 
 strings.  I think it would be more useful if isVirtualFunction and the 
 other isXX would take either a string or the direct statement.  For 
 example:

 __traits(isVirtualFunction, "A.foo")
 would be the same as:
 __traits(isVirtualFunction, A.foo)

class A { final void foo (); int foo (int i); } What to return? __traits(isVirtualFunction, A.foo) is ambiguous as well. I don't recommend using it, and I don't use it.

Hm... good point. So the getVirtualFunctions would be a better idea.
 Then I could do:

 foreach(x; __traits(allMembers, A))
     if(__traits(isVirtualfunction, "A." ~ x))
         // append to string that redefines virtual function.

You can't foreach an array at compile time, and __traits(allMembers) returns an array. You can index an array at compile time, but again, you can't use a while loop at compile time. You have to use recursion.

Aha! this is what I was missing :)
 Eventually, static foreach should be able to do this. Or you can bug 
 Walter about changing allMembers to return a tuple of strings.

 Second, the mixin doesn't work.  I hard-coded the function to output the 
 string I was trying to build.  Therefore, the compiler should be able to 
 execute the method in compile time, and pre-determine the string to 
 return. But the mixin just defines the function name that I am calling as 
 a member. i.e., I define my function above as:

 string redefFunctions(T)();

 and my mixin in the class is:

 mixin(redefFunctions!(A)());

 But when I build my class, there is a member redefFunctions!(A) instead 
 of the list of virtual functions I expected to override.

Not sure what the problem there is. If you omit the parentheses around the template method call, you'll definitely get the result you describe (that had me hung up for a bit), but your example has the parentheses.

Not sure either :) But enough about my horribly disfigured abortion of code, I think I'll just base my stuff off of your Mock Objects lib :) -Steve
Nov 12 2007
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Christopher Wright" wrote
 You can't foreach an array at compile time, and __traits(allMembers) 
 returns an array. You can index an array at compile time, but again, you 
 can't use a while loop at compile time.

 You have to use recursion.

 Eventually, static foreach should be able to do this. Or you can bug 
 Walter about changing allMembers to return a tuple of strings.

Is static foreach a planned feature? This sounds like it would be much preferrable to the recursion. -Steve
Nov 12 2007
parent Christopher Wright <dhasenan gmail.com> writes:
Steven Schveighoffer wrote:
 "Christopher Wright" wrote
 You can't foreach an array at compile time, and __traits(allMembers) 
 returns an array. You can index an array at compile time, but again, you 
 can't use a while loop at compile time.

 You have to use recursion.

 Eventually, static foreach should be able to do this. Or you can bug 
 Walter about changing allMembers to return a tuple of strings.

Is static foreach a planned feature? This sounds like it would be much preferrable to the recursion.

Yes. But not quite yet. There's a ticket for it.
Nov 12 2007
prev sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Christopher Wright Wrote:

 You can't foreach an array at compile time [...]

Yes you can, in the context of a function. string ct_func(string[] arr) { foreach(s; arr) { ... } } mixin(ct_func(__traits(allMembers, Foo)));
Nov 12 2007
parent Christopher Wright <dhasenan gmail.com> writes:
Robert Fraser wrote:
 Christopher Wright Wrote:
 
 You can't foreach an array at compile time [...]

Yes you can, in the context of a function. string ct_func(string[] arr) { foreach(s; arr) { ... } } mixin(ct_func(__traits(allMembers, Foo)));

You're right. You can use that to, say, do string manipulations based on a string array. However, you cannot use these strings with pragmas or __traits. I had tried using foreach on __traits(allMembers) in my code, but since I went on to do __traits(getVirtualFunctions) using those strings, dmd wasn't happy with me. :`( That's why I use recursive templates in my code.
Nov 12 2007