digitalmars.D - Compile-time reflection
- Kirk McDonald (149/149) Jul 01 2007 The subject of compile-time reflection has been an important one to me.
- Christopher Wright (61/82) Jul 01 2007 Overloads are entirely separate functions. If you go through
- Kirk McDonald (23/101) Jul 01 2007 I am interested in compile-time reflection. The vtable doesn't exist at
- Fawzi Mohamed (25/25) May 31 2008 I understand the need of reflection t connect D to other languages.
- lihong (1/1) May 31 2008 We offer World of Warcraft Power Leveling and World of Warcraft powerlev...
- BCS (4/14) Jul 02 2007 Why have this give a type tuple? I'd have it give alias to the overloads...
- Kirk McDonald (8/28) Jul 02 2007 Aliases operate on symbols. All of those functions have the same symbol....
- BCS (18/40) Jul 03 2007 I would clam that an alias to a specific overload being nonsensical shou...
- Lutger (12/29) Jul 02 2007 Kirk McDonald wrote:
- Kirk McDonald (15/52) Jul 02 2007 Grammar wise, using 'virtual' would cause some problems. (What if you
- Lutger (12/36) Jul 02 2007 I see. Well, it's not that important, the big thing is if the
- Gregor Richards (4/4) Jul 03 2007 Time for a response without reading the entire message 8-D
- =?UTF-8?B?SnVsaW8gQ8Opc2FyIENhcnJhc2NhbCBVcnF1aWpv?= (10/17) Jul 03 2007 Very impressive Gregor. One question: Will it be possible to call a
- Bill Baxter (4/14) Jul 03 2007 Can someone post a synopsis for those of us too lazy to go sifting
- Kirk McDonald (17/33) Jul 03 2007 It is a project in two parts:
- Robert Fraser (2/9) Jul 03 2007 Wow, I had no idea that even existed... what other hidden open-source D ...
- Giles Bathgate (2/3) Jul 04 2007 surprisingly alot more than you would expect. The trouble I find is that...
- Bruno Medeiros (12/70) Jul 08 2007 Why would we need to distinguish between virtual, static and final
- Jarrett Billingsley (9/11) Jul 08 2007 At least from a scripting-language-binding-library point of view, it's
- Kirk McDonald (16/42) Jul 08 2007 For starters, you can directly call static member functions, but not the...
- Bruno Medeiros (13/27) Jul 11 2007 Casting is a kludge, and should not be used for something that is
- Kirk McDonald (8/35) Jul 11 2007 I agree with making that an error, so long as we get a way to
- Bruno Medeiros (13/72) Jul 11 2007 Ok, I don't think I made my point correctly. Yes, it's useful to
- Kirk McDonald (23/35) Jul 11 2007 I had somehow been mistaken that this was the way it worked currently.
- Walter Bright (2/5) Jul 08 2007 Thanks for taking the time to put together an especially valuable post.
The subject of compile-time reflection has been an important one to me. I have been musing on it since about the time I started writing Pyd. Here is the current state of my thoughts on the matter. ---- Functions ---- When talking about functions, a given symbol may refer to multiple functions: void foo() {} void foo(int i) {} void foo(int i, int j, int k=20) {} The first thing a compile-time reflection mechanism needs is a way to, given a symbol, derive a tuple of the signatures of the function overloads. There is no immediately obvious syntax for this. The is() expression has so far been the catch-all location for many of D's reflection capabilities. However, is() operates on types, not arbitrary symbols. A property is more promising. Re-using the .tupleof property is one idea: foo.tupleof => Tuple!(void function(), void function(int), void function(int, int, int)) However, I am not sure how plausible it is to have a property on a symbol like this. Another alternative is to have some keyword act as a function (as typeof and typeid do, for instance). I propose adding "tupleof" as an actual keyword: tupleof(foo) => Tuple!(void function(), void function(int), void function(int, int, int)) I will be using this syntax throughout the rest of this post. For the sake of consistency, tupleof(Foo) should do what Foo.tupleof does now. To umabiguously refer to a specific overload of a function, two pieces of information are required: The function's symbol, and the signature of the overload. When doing compile-time reflection, one is typically working with one specific overload at a time. While a function pointer does refer to one specific overload, it is important to note that function pointers are not compile-time entities! Therefore, the following idiom is common: template UseFunction(alias func, func_t) {} That is, any given template that does something with a function requires both the function's symbol and the signature of the particular overload to operate on to be useful. It should be clear, then, that automatically deriving the overloads of a given function is very important. Another piece of information that is useful is whether a given function has default arguments, and how many. The tupleof() syntax can be re-used for this: tupleof(foo, void function(int, int, int)) => Tuple!(void function(int, int)) Here, we pass tupleof() the symbol of a function, and the signature of a particular overload of that function. The result is a tuple of the various signatures it is valid to call the overload with, ignoring the /actual/ signature of the function. The most useful piece of information here is the /number/ of elements in the tuple, which will be equal to the number of default arguments supported by the overload. One might be tempted to place these additional function signatures in the original tuple derived by tupleof(foo). However, this is not desirable. Consider: We can say any of the following: void function() fn1 = &foo; void function(int) fn2 = &foo; void function(int, int, int) fn3 = &foo; But we /cannot/ say this: void function(int, int) fn4 = &foo; // ERROR! A given function-symbol therefore has two sets of function signatures associated with it: The actual signatures of the functions, and the additional signatures it may be called with due to default arguments. These two sets are not equal in status, and should not be treated as such. ---- Member functions ---- Here is where things get really complicated. class A { void bar() {} void bar(int i) {} void bar(int i, int j, int k=20) {} void baz(real r) {} static void foobar() {} final void foobaz() {} } class B : A { void foo() {} override void baz(real r) {} } D does not really have pointers to member functions. It is possible to fake them with some delegate trickery. In particular, there is no way to directly call an alias of a member function. This is important, as I will get to later. The first mechanism needed is a way to get all of the member functions of a class. I suggest the addition of a .methodsof class property, which will derive a tuple of aliases of the class's member functions. A.methodsof => Tuple!(A.bar, A.baz, A.foobar, A.foobaz) B.methodsof => Tuple!(A.bar, A.foobar, A.foobaz, B.foo, B.baz) The order of the members in this tuple is not important. Inherited member functions are included, as well. Note that these are tuples of symbol aliases! Since these are function symbols, all of the mechanisms suggested earlier for regular function symbols should still work! tupleof(A.bar) => Tuple!(void function(), void function(int), void function(int, int, int)) And so forth. There are three kinds of member functions: virtual, static, and final. The next important mechanism that is needed is a way to distinguish these from each other. An important rule of function overloading works in our favor, here: A given function symbol can only refer to functions which are all virtual, all static, or all final. Therefore, this should be considered a property of the symbol, as opposed to one of the function itself. The actual syntax for this mechanism needs to be determined. D has 'static' and 'final' keywords, but no 'virtual' keyword. Additionally, the 'static' keyword has been overloaded with many meanings, and I hesitate suggesting we add another. Nonetheless, I do. static(A.bar == static) == false static(A.bar == final) == false static(A.bar == virtual) == true The syntax is derived from that of the is() expression. The grammar would look something like this: StaticExpression: static ( Symbol == SymbolSpecialization ) SymbolSpecialization: static final virtual Here, 'virtual' is a context-sensitive keyword, not unlike the 'exit' in 'scope(exit)'. If the Symbol is not a member function, it is an error. A hole presents itself in this scheme. We can get all of the function symbols of a class's member functions. From these, we can get the signatures of their overloads. From /these/, can get get pointers to the member functions, do some delegate trickery, and actually call them. This is all well and good. But there is a problem when a method has default arguments. As explained earlier, we can't do this: // Error! None of the overloads match! void function(int, int) member_func = &A.bar; Even though we can say: A a = new A; a.bar(1, 2); The simplest solution is to introduce some way to call an alias of a method directly. There are a few options. My favorite is to take a cue from Python, and allow the following: alias A.bar fn; A a = new A; fn(a, 1, 2); That is, allow the user to explicitly call the method with the instance as the first parameter. This should be allowed generally, as in: A.bar(a); A.baz(a, 5.5); Given these mechanisms, combined with the existing mechanisms to derive the return type and parameter type tuple from a function type, D's compile-time reflection capabilities would be vastly more powerful. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Jul 01 2007
Kirk McDonald wrote: <snip>The first thing a compile-time reflection mechanism needs is a way to, given a symbol, derive a tuple of the signatures of the function overloads. There is no immediately obvious syntax for this.Overloads are entirely separate functions. If you go through ClassInfo.vtbl, that gives you each overload separately. And the only thing overloads have in common are their names. So is this actually important? The annoyance is when you have overloads, how do you refer to the correct one? --- void foo(int i) {} void foo(int i, char[] str) {} auto f1 = &foo; // works, but which does it give? void function(int, char[]) f2 = &foo; // does the right thing --- That works fine, as long as one person wrote it all specifically for the cases it's used in. What if you're sending a function reference to some other piece of code that can take any function? --- void foo(int i) {} void foo(int i, char[] str) {} some_other_lib.use_some_function(&foo); --- Well, you can do the obvious workaround: --- void function(int) foo_to_send = &foo; some_other_lib.use_some_function(&foo_to_send); --- It just bothers me that the only way to specify which reference is by assignment to a function reference with a specific signature. I'd prefer a syntax more like: auto f1 = &foo(int, char[]); Unambiguous, since references don't have an opCall, and shorter. But the present workaround is merely annoying, and only mildly at that. <snip>foo.tupleof => Tuple!(void function(), void function(int), void function(int, int, int))Okay, sounds simple enough, but why do you need each overload? They're separate functions; they don't have anything in common, strictly speaking, except the name.It should be clear, then, that automatically deriving the overloads of a given function is very important. Another piece of information that is useful is whether a given function has default arguments, and how many. The tupleof() syntax can be re-used for this: tupleof(foo, void function(int, int, int)) => Tuple!(void function(int, int))This is interesting. Currently, there's no way to get which arguments are omissible. ...D does not really have pointers to member functions. It is possible to fake them with some delegate trickery. In particular, there is no way to directly call an alias of a member function. This is important, as I will get to later.The reason for this is obvious; the compiler rewrites the functions as you describe below. I haven't looked, but I'd guess a delegate is something like this: struct delegate (T, R, U...) { void* func; T obj; R opCall(U u) { return *func(obj, u); } } ...A.methodsof => Tuple!(A.bar, A.baz, A.foobar, A.foobaz) B.methodsof => Tuple!(A.bar, A.foobar, A.foobaz, B.foo, B.baz)You can already get this through A.classinfo.vtbl. That contains all functions that are valid for the class. You can create a template that will determine which functions are inherited, though you cannot say which override functions from the base class with any certainty.static(A.bar == static) == false static(A.bar == final) == false static(A.bar == virtual) == trueCurrently, there's no way I know of to get this information. The is(typeof()) system works with all functions, static or not, whether you use them from a type or from an instance. Hack? Do everything from instance variables. Dunno what to do about final, though. Checking about final stuff matters if you want your program to behave differently toward final methods, but nothing in ClassInfo prevents you from replacing a final method.
Jul 01 2007
Christopher Wright wrote:Kirk McDonald wrote: <snip>I am interested in compile-time reflection. The vtable doesn't exist at compile-time. The only way to refer to a function at compile-time is through a combination of the symbol name and the signature. Anything involving function pointers is necessarily a runtime operation. [snip]The first thing a compile-time reflection mechanism needs is a way to, given a symbol, derive a tuple of the signatures of the function overloads. There is no immediately obvious syntax for this.Overloads are entirely separate functions. If you go through ClassInfo.vtbl, that gives you each overload separately. And the only thing overloads have in common are their names. So is this actually important?<snip>Same reason: It is a strictly compile-time operation.foo.tupleof => Tuple!(void function(), void function(int), void function(int, int, int))Okay, sounds simple enough, but why do you need each overload? They're separate functions; they don't have anything in common, strictly speaking, except the name.Not entirely true; see std.bind.minNumArgs (which is a variadic version, by h3r3tic, of a non-variadic template I wrote before we had tuples). I am simply proposing making this a language feature, rather than the hack it currently is.It should be clear, then, that automatically deriving the overloads of a given function is very important. Another piece of information that is useful is whether a given function has default arguments, and how many. The tupleof() syntax can be re-used for this: tupleof(foo, void function(int, int, int)) => Tuple!(void function(int, int))This is interesting. Currently, there's no way to get which arguments are omissible....The ABI is different; the instance is passed in a register, or somesuch.D does not really have pointers to member functions. It is possible to fake them with some delegate trickery. In particular, there is no way to directly call an alias of a member function. This is important, as I will get to later.The reason for this is obvious; the compiler rewrites the functions as you describe below. I haven't looked, but I'd guess a delegate is something like this: struct delegate (T, R, U...) { void* func; T obj; R opCall(U u) { return *func(obj, u); } } ...Again, the vtable is not compile-time information.A.methodsof => Tuple!(A.bar, A.baz, A.foobar, A.foobaz) B.methodsof => Tuple!(A.bar, A.foobar, A.foobaz, B.foo, B.baz)You can already get this through A.classinfo.vtbl. That contains all functions that are valid for the class. You can create a template that will determine which functions are inherited, though you cannot say which override functions from the base class with any certainty.My interest is in improving Pyd. Given a class, I want to generate a Python type wrapping the entirety of that class, as well as a D subclass of the type, providing virtual dispatching to the Python type. This means I effectively have to know /everything/ about the class and its methods, at compile-time. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.orgstatic(A.bar == static) == false static(A.bar == final) == false static(A.bar == virtual) == trueCurrently, there's no way I know of to get this information. The is(typeof()) system works with all functions, static or not, whether you use them from a type or from an instance. Hack? Do everything from instance variables. Dunno what to do about final, though. Checking about final stuff matters if you want your program to behave differently toward final methods, but nothing in ClassInfo prevents you from replacing a final method.
Jul 01 2007
I understand the need of reflection t connect D to other languages. Still I think that a solution to this problem should be (not necessarily now, but in the future) extensible to template functions. The virtual/final/static attributes seem quite straightforward and unproblematic (I like the is(x==virtual) solution because it is what I would have expected). The symbol+signature -> function pointer is also unproblematic (be it through an alias cast or assignment). The symbol -> signatures thing can be problematic if templates enter in the picture. One could think of representing all kind of signatures by allowing things like template(TemplateType T0){template(alias T1){template(int T2){template(int T3){T0 function(T0[T2],optional T0[T3])} or something like it. Unfortunately this is not enough as there is no guarantee that a given template is instantiable. Instantiability can be controlled at compile time, but still for automatic interface generators one cannot avoid giving them some extra information. One could think about giving restricting the list of signatures either to full specializations (and maybe have some hint that extra ones might exist), or to the templates that have been instantiated (but such a set is not fixed and can change from program to program). In any case some thought should be spent about this. Fawzi
May 31 2008
We offer World of Warcraft Power Leveling and World of Warcraft powerleveling,Final Fantasy XI Gil,Maple Story and Lord fo Ring Online Powerleveling.WoW Powerleveling service and cheap wow Powerleveling,World of Warcraft Power leveling sale for you. WOW Power leveling,Cheap WOW Powerleveling 1-60 and Cheap WOW Powerleveling 1 - 70.All service is faster,safer,and cheaper.WOW Powerleveling 1 - 60,WOW Powerleveling 1 - 70.Lord fo Ring Online Power Leveling 1-50 .We Please remember,we are your online game helper.pvpsale
May 31 2008
Reply to Kirk,void foo() {} void foo(int i) {} void foo(int i, int j, int k=20) {} The first thing a compile-time reflection mechanism needs is a way to, given a symbol, derive a tuple of the signatures of the function overloads.[...]foo.tupleof => Tuple!(void function(), void function(int), void function(int, int, int))Why have this give a type tuple? I'd have it give alias to the overloads them selves.
Jul 02 2007
BCS wrote:Reply to Kirk,Aliases operate on symbols. All of those functions have the same symbol. An alias to a specific overload is nonsensical. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.orgvoid foo() {} void foo(int i) {} void foo(int i, int j, int k=20) {} The first thing a compile-time reflection mechanism needs is a way to, given a symbol, derive a tuple of the signatures of the function overloads.[...]foo.tupleof => Tuple!(void function(), void function(int), void function(int, int, int))Why have this give a type tuple? I'd have it give alias to the overloads them selves.
Jul 02 2007
Reply to Kirk,BCS wrote:I would clam that an alias to a specific overload being nonsensical should in it's self be nonsensical. Why shouldn't I be able to get an alias to a specific function even if it is overloaded? I would propose that an unqualified function name (or an alias of it) would be the set of overloads (or alias to them) and a qualified name would be a specific function. I would be interested in way this would be a bad idea. void foo(int i){} void foo(Object i){} alias foo bar; // bar == {foo(int) and foo(Object)} int i; bar(i); // works bar(null); // works // proposed syntax for overload resolution alias foo(Object) baz; baz(i); // fails int i can't be converted to Object baz(null); // worksReply to Kirk,Aliases operate on symbols. All of those functions have the same symbol. An alias to a specific overload is nonsensical.void foo() {} void foo(int i) {} void foo(int i, int j, int k=20) {} The first thing a compile-time reflection mechanism needs is a way to, given a symbol, derive a tuple of the signatures of the function overloads.[...]foo.tupleof => Tuple!(void function(), void function(int), void function(int, int, int))Why have this give a type tuple? I'd have it give alias to the overloads them selves.
Jul 03 2007
Kirk McDonald wrote: <snip> It would be great to have the functionality you propose in D.There are three kinds of member functions: virtual, static, and final. The next important mechanism that is needed is a way to distinguish these from each other. An important rule of function overloading works in our favor, here: A given function symbol can only refer to functions which are all virtual, all static, or all final. Therefore, this should be considered a property of the symbol, as opposed to one of the function itself. The actual syntax for this mechanism needs to be determined. D has 'static' and 'final' keywords, but no 'virtual' keyword. Additionally, the 'static' keyword has been overloaded with many meanings, and I hesitate suggesting we add another. Nonetheless, I do. static(A.bar == static) == false static(A.bar == final) == false static(A.bar == virtual) == trueThis looks very confusing to me, however, if you'd replace the first static with 'is' it makes sense: is(A.bar == static) == false is(A.bar == final) == false is(A.bar == virtual) == true Is there a problem with this, grammar-wise? Btw, if introducing 'virtual' is undesirable, it can be left out since it can be inferred from being final nor static, and for this some library side syntactic sugar can be made.
Jul 02 2007
Lutger wrote:Kirk McDonald wrote: <snip> It would be great to have the functionality you propose in D.Grammar wise, using 'virtual' would cause some problems. (What if you have a type called 'virtual' and want to compare some other type to it?) There is another, more serious, problem: is() is defined such that if the first thing passed to it isn't a type, it returns false. (That is, one of its purposes is to test if something is a valid type.) I think that making an exception to this rule for 'static' and 'final' could potentially be confusing. Therefore, some other keyword is required. I picked 'static' more or less arbitrarily. It also allows the use of 'virtual' directly. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.orgThere are three kinds of member functions: virtual, static, and final. The next important mechanism that is needed is a way to distinguish these from each other. An important rule of function overloading works in our favor, here: A given function symbol can only refer to functions which are all virtual, all static, or all final. Therefore, this should be considered a property of the symbol, as opposed to one of the function itself. The actual syntax for this mechanism needs to be determined. D has 'static' and 'final' keywords, but no 'virtual' keyword. Additionally, the 'static' keyword has been overloaded with many meanings, and I hesitate suggesting we add another. Nonetheless, I do. static(A.bar == static) == false static(A.bar == final) == false static(A.bar == virtual) == trueThis looks very confusing to me, however, if you'd replace the first static with 'is' it makes sense: is(A.bar == static) == false is(A.bar == final) == false is(A.bar == virtual) == true Is there a problem with this, grammar-wise? Btw, if introducing 'virtual' is undesirable, it can be left out since it can be inferred from being final nor static, and for this some library side syntactic sugar can be made.
Jul 02 2007
Kirk McDonald wrote: ...I see. Well, it's not that important, the big thing is if the functionality will be included. Still I would argue that it is preferable to use 'is', because it's both clearer and more consistent. The problem with is() returning false when the symbol tested isn't a type occurs now too, I don't understand how this is different from current cases with static / final / virtual? If we need to hose virtual, one would have to write: !(is(A.bar == static) || is(A.bar == final)) This is less than ideal, but I can live with that for the benefit it gives. In practice I would wrap it anyway in some traits module.This looks very confusing to me, however, if you'd replace the first static with 'is' it makes sense: is(A.bar == static) == false is(A.bar == final) == false is(A.bar == virtual) == true Is there a problem with this, grammar-wise? Btw, if introducing 'virtual' is undesirable, it can be left out since it can be inferred from being final nor static, and for this some library side syntactic sugar can be made.Grammar wise, using 'virtual' would cause some problems. (What if you have a type called 'virtual' and want to compare some other type to it?) There is another, more serious, problem: is() is defined such that if the first thing passed to it isn't a type, it returns false. (That is, one of its purposes is to test if something is a valid type.) I think that making an exception to this rule for 'static' and 'final' could potentially be confusing. Therefore, some other keyword is required. I picked 'static' more or less arbitrarily. It also allows the use of 'virtual' directly.
Jul 02 2007
Time for a response without reading the entire message 8-D http://www.dsource.org/projects/tango.tools/browser/trunk/tools/rodin (FYI: Rodin will work with either Tango or Phobos) - Gregor Richards
Jul 03 2007
Gregor Richards wrote:Time for a response without reading the entire message 8-D http://www.dsource.org/projects/tango.tools/browser/trunk/tools/rodin (FYI: Rodin will work with either Tango or Phobos) - Gregor RichardsVery impressive Gregor. One question: Will it be possible to call a function without knowing it's signature at compile time? In all examples you used a delegate, a function or a base class. What I'd like to do is something like: object obj = refGetClass(classPath); object[] arguments = new object[] { 1, "string", new Object() }; refCallMethod(obj, "foo", arguments); Obviously the object[] doesn't work on D but maybe Box(T)? Thanks
Jul 03 2007
Julio César Carrascal Urquijo wrote:Gregor Richards wrote:Can someone post a synopsis for those of us too lazy to go sifting through an svn repository? --bbTime for a response without reading the entire message 8-D http://www.dsource.org/projects/tango.tools/browser/trunk/tools/rodin (FYI: Rodin will work with either Tango or Phobos) - Gregor RichardsVery impressive Gregor.
Jul 03 2007
Bill Baxter wrote:Julio César Carrascal Urquijo wrote:It is a project in two parts: drefgen is a utility, based on the DMD front-end and written in C++, which generates meta-data about whatever code you pass to it. rodin is a D library which is capable of reading that meta-data, allowing client code (quite possibly the very code you generated the meta-data from) access to it. While it is an impressive piece of work, the resulting data is not available at compile-time, just run-time. Therefore it is not quite what my post was talking about. It might be able to get there with some work, but I have an aversion to code generation. Actual support in the compiler would be vastly preferable. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.orgGregor Richards wrote:Can someone post a synopsis for those of us too lazy to go sifting through an svn repository? --bbTime for a response without reading the entire message 8-D http://www.dsource.org/projects/tango.tools/browser/trunk/tools/rodin (FYI: Rodin will work with either Tango or Phobos) - Gregor RichardsVery impressive Gregor.
Jul 03 2007
Gregor Richards Wrote:Time for a response without reading the entire message 8-D http://www.dsource.org/projects/tango.tools/browser/trunk/tools/rodin (FYI: Rodin will work with either Tango or Phobos) - Gregor RichardsWow, I had no idea that even existed... what other hidden open-source D projects are there?
Jul 03 2007
Wow, I had no idea that even existed... what other hidden open-source D projects are there?surprisingly alot more than you would expect. The trouble I find is that they are all a bit scattered here and there. It would be nice if there was a "distribution" of D, phobos, and a set of decent libraries that all fit together kind of like a Framework. Then again that takes the fun out of hunting them down ;)
Jul 04 2007
Kirk McDonald wrote:---- Member functions ---- Here is where things get really complicated. class A { void bar() {} void bar(int i) {} void bar(int i, int j, int k=20) {} void baz(real r) {} static void foobar() {} final void foobaz() {} } class B : A { void foo() {} override void baz(real r) {} } D does not really have pointers to member functions. It is possible to fake them with some delegate trickery. In particular, there is no way to directly call an alias of a member function. This is important, as I will get to later. The first mechanism needed is a way to get all of the member functions of a class. I suggest the addition of a .methodsof class property, which will derive a tuple of aliases of the class's member functions. A.methodsof => Tuple!(A.bar, A.baz, A.foobar, A.foobaz) B.methodsof => Tuple!(A.bar, A.foobar, A.foobaz, B.foo, B.baz) The order of the members in this tuple is not important. Inherited member functions are included, as well. Note that these are tuples of symbol aliases! Since these are function symbols, all of the mechanisms suggested earlier for regular function symbols should still work! tupleof(A.bar) => Tuple!(void function(), void function(int), void function(int, int, int)) And so forth. There are three kinds of member functions: virtual, static, and final. The next important mechanism that is needed is a way to distinguish these from each other. An important rule of function overloading works in our favor, here: A given function symbol can only refer to functions which are all virtual, all static, or all final. Therefore, this should be considered a property of the symbol, as opposed to one of the function itself.Why would we need to distinguish between virtual, static and final member functions?Given these mechanisms, combined with the existing mechanisms to derive the return type and parameter type tuple from a function type, D's compile-time reflection capabilities would be vastly more powerful.I'm not sure about some of the detail, but in a general wey, yes it would be welcome to have such changes that would allows us to better work with functions. Some of the issues here are not just about compile time reflection, for instance, that you have to create a temporary type, or do a cast, to get a function pointer of a particular overload, seems quite hackish to me. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 08 2007
"Bruno Medeiros" <brunodomedeiros+spam com.gmail> wrote in message news:f6qiqg$1bsg$1 digitalmars.com...Why would we need to distinguish between virtual, static and final member functions?At least from a scripting-language-binding-library point of view, it's useful to know that so that when you write: Def!(A.foo); Def!(A.bar); Def!(A.baz); If foo is static, bar is virtual, and baz is final, they might all have to be bound in different ways.
Jul 08 2007
Bruno Medeiros wrote:Kirk McDonald wrote:For starters, you can directly call static member functions, but not the others (they require an instance). The difference between virtual and final is more subtle, but is still worth-while to know if you start doing some stupid inheritance tricks. As Jarrett points out, this information is particularly valuable when binding the language to another language.There are three kinds of member functions: virtual, static, and final. The next important mechanism that is needed is a way to distinguish these from each other. An important rule of function overloading works in our favor, here: A given function symbol can only refer to functions which are all virtual, all static, or all final. Therefore, this should be considered a property of the symbol, as opposed to one of the function itself.Why would we need to distinguish between virtual, static and final member functions?This would not change that. In fact, I honestly see nothing wrong with that. Casting seems like simplest way of specifying the overload, without introducing new syntax. (Although I'm aware some folks want to introduce new syntax for this express purpose.) -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.orgGiven these mechanisms, combined with the existing mechanisms to derive the return type and parameter type tuple from a function type, D's compile-time reflection capabilities would be vastly more powerful.I'm not sure about some of the detail, but in a general wey, yes it would be welcome to have such changes that would allows us to better work with functions. Some of the issues here are not just about compile time reflection, for instance, that you have to create a temporary type, or do a cast, to get a function pointer of a particular overload, seems quite hackish to me.
Jul 08 2007
Kirk McDonald wrote:Casting is a kludge, and should not be used for something that is perfectly normal, valid (and possibly common). Fortunately we can hide it with some cleaner templates: auto fn2 = overload!(fn, int, char) // select fn(int, char) overload I don't think new syntax should be added. But that doesn't mean that some things could not be improved with the current design. For instance, taking the address of an overloaded function without a cast (or another overload selection mechanism) should result in a error, instead of simply returning the address of the lexically first overload. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#DI'm not sure about some of the detail, but in a general wey, yes it would be welcome to have such changes that would allows us to better work with functions. Some of the issues here are not just about compile time reflection, for instance, that you have to create a temporary type, or do a cast, to get a function pointer of a particular overload, seems quite hackish to me.This would not change that. In fact, I honestly see nothing wrong with that. Casting seems like simplest way of specifying the overload, without introducing new syntax. (Although I'm aware some folks want to introduce new syntax for this express purpose.)
Jul 11 2007
Bruno Medeiros wrote:Kirk McDonald wrote:I agree with making that an error, so long as we get a way to automatically derive the types of all of the overloads. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.orgCasting is a kludge, and should not be used for something that is perfectly normal, valid (and possibly common). Fortunately we can hide it with some cleaner templates: auto fn2 = overload!(fn, int, char) // select fn(int, char) overload I don't think new syntax should be added. But that doesn't mean that some things could not be improved with the current design. For instance, taking the address of an overloaded function without a cast (or another overload selection mechanism) should result in a error, instead of simply returning the address of the lexically first overload.I'm not sure about some of the detail, but in a general wey, yes it would be welcome to have such changes that would allows us to better work with functions. Some of the issues here are not just about compile time reflection, for instance, that you have to create a temporary type, or do a cast, to get a function pointer of a particular overload, seems quite hackish to me.This would not change that. In fact, I honestly see nothing wrong with that. Casting seems like simplest way of specifying the overload, without introducing new syntax. (Although I'm aware some folks want to introduce new syntax for this express purpose.)
Jul 11 2007
Bruno Medeiros wrote:Kirk McDonald wrote:Ok, I don't think I made my point correctly. Yes, it's useful to distinguish between virtual, static and final member functions[*], but that's something that could be done (if not already) with is-expressions. What I mean is more about this: "An important rule of function overloading works in our favor, here: A given function symbol can only refer to functions which are all virtual, all static, or all final." Why would we want this change? [*] Although, I'm not so sure about virtual vs. final distinctions. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D---- Member functions ---- Here is where things get really complicated. class A { void bar() {} void bar(int i) {} void bar(int i, int j, int k=20) {} void baz(real r) {} static void foobar() {} final void foobaz() {} } class B : A { void foo() {} override void baz(real r) {} } D does not really have pointers to member functions. It is possible to fake them with some delegate trickery. In particular, there is no way to directly call an alias of a member function. This is important, as I will get to later. The first mechanism needed is a way to get all of the member functions of a class. I suggest the addition of a .methodsof class property, which will derive a tuple of aliases of the class's member functions. A.methodsof => Tuple!(A.bar, A.baz, A.foobar, A.foobaz) B.methodsof => Tuple!(A.bar, A.foobar, A.foobaz, B.foo, B.baz) The order of the members in this tuple is not important. Inherited member functions are included, as well. Note that these are tuples of symbol aliases! Since these are function symbols, all of the mechanisms suggested earlier for regular function symbols should still work! tupleof(A.bar) => Tuple!(void function(), void function(int), void function(int, int, int)) And so forth. There are three kinds of member functions: virtual, static, and final. The next important mechanism that is needed is a way to distinguish these from each other. An important rule of function overloading works in our favor, here: A given function symbol can only refer to functions which are all virtual, all static, or all final. Therefore, this should be considered a property of the symbol, as opposed to one of the function itself.Why would we need to distinguish between virtual, static and final member functions?
Jul 11 2007
Bruno Medeiros wrote:Ok, I don't think I made my point correctly. Yes, it's useful to distinguish between virtual, static and final member functions[*], but that's something that could be done (if not already) with is-expressions. What I mean is more about this: "An important rule of function overloading works in our favor, here: A given function symbol can only refer to functions which are all virtual, all static, or all final." Why would we want this change?I had somehow been mistaken that this was the way it worked currently. Whoops. Since this isn't true, the StaticExpression syntax would have to specify the particular overload to operate on, something like: StaticExpression: static ( Symbol Type == SymbolSpecialization ) Where Type is the signature of the overload to operate on, as in: class C { void foo() {} static void foo(int i) {} } static(C.foo void function() == virtual) // true static(C.foo void function(int) == static) // true Specifying an non-existent overload should probably be an error.[*] Although, I'm not so sure about virtual vs. final distinctions.As I said, this distintion may be important if you're doing certain tricks with inheritance. And if these mechanisms are being added, I don't think there's any reason /not/ to add it. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Jul 11 2007
Kirk McDonald wrote:Given these mechanisms, combined with the existing mechanisms to derive the return type and parameter type tuple from a function type, D's compile-time reflection capabilities would be vastly more powerful.Thanks for taking the time to put together an especially valuable post.
Jul 08 2007