Notice: On March 31, it was **announced** that Statalist is moving from an email list to a **forum**. The old list will shut down on April 23, and its replacement, **statalist.org** is already up and running.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

From |
Richard Foltyn <richard.foltyn@gmail.com> |

To |
statalist@hsphsun2.harvard.edu |

Subject |
Re: st: Pointer to class member function? |

Date |
Sat, 4 May 2013 19:22:56 +0200 |

Thank you very much for taking the time to write such an elaborate answer! It was highly appreciated. Best, Richard On Fri, May 3, 2013 at 6:36 PM, William Gould, StataCorp LP <wgould@stata.com> wrote: > Richard Foltyn (richard.foltyn@gmail.com) is doing advanced class > programming in Mata. He is trying to take the address of a > class-member function and then use that address later to invoke the > function. > > The short answer is that what Richard is trying to do will not work and > is not supported. > > I'm about to explain why it's not supported, but I don't really expect > that will alleviate Richard's disappointment. > > Richard offered the following example: > > mata: > > class testClass { > void run1() > void run2() > real scalar func() > } > > real scalar testClass::func(real scalar arg) > { > return(arg) > } > > void testClass::run2(pointer(real scalar function) scalar f, /// > real scalar arg) > { > (*f)(arg) > } > > void testClass::run1() > { > this.run2(&func(), 1) > } > > foo = testClass() > foo.run1() > > end > > The result of running example, Richard correctly reports, is, > > : foo.run1() > testClass::run1(): 3499 func() not found [3] > <istmt>: - function returned error [1] > > Richard took a good stab and writing what wanted to achieve. Even so, > there is conceptually too little information in what Richard wrote > above for any compiler to interpret or to compile the code correctly. > > To understand the issue, I first need to tell you how class functions > are implemented by compilers. When a programmer codes > > a.myfunc() > > The compiler in fact compiles > > <name_of_class>memberfunction_myfunc(a) > > That is, the a. at the front becomes an argument to more-or-less regular > function! Details vary, but all compilers do this. When programmer > codes > > a.func2(d, e) > > compiles in fact compile > > <name_of_class>memberfunction_func2(a, d, e) > > The class object again moves to being just an argument of the function. > > The class object had to to be passed to the function in some way, and > because the mechanism already exists to pass arguments, it's easiest > for compilers to simply pass the class object using them. > > So now consider one of the functions Richard wrote, > > > void testClass::run1() > { > this.run2(&func(), 1) > } > > That is where the error occurred, but at this point, the conceptual > problem has not yet arisen. It is merely that Mata will not allow the > & operator to be applied to member functions. That is not implemented > because, as I am about to show you, even if you had the address, we are > about to run into a substantive problem. > > So pretend that Mata did return the address of member function > func(). It would have been easy enough for Mata do to that. In fact, > extra code had to be added to prevent Mata from doing that. > > That substantive problem would arise when Richard when to use the > pointer. In Richard's case, that would be in the following code: > > void testClass::run2(pointer(real scalar function) scalar f, /// > real scalar arg) > { > (*f)(arg) > } > > All Mata knows about object f is that it is a pointer to a real scalar > function. It does not know that the pointer is in fact a pointer to a > member function of some class, nor does it know the name of the class. > Thus, if Mata were to compile > > (*f)(arg) > > It would not know that it needed to add a first argument to the > function containing a pointer to the class object! So even if we had > the address of the member function, the code would be miscompiled and > that would lead to another error. > > And that is why Mata won't let you take the address of a member > function. There is nothing useful you could do with it. > > As I said, we actually went to extra work to prevent Mata from > returning the address of the function. So how might we extend the > programming language so that in the definition of member function > run2() Mata would know that *f is a member of function and of this same > class. > > One way would be, > > void testClass::run2( > pointer(real scalar member(testClass) function) scalar f, > real scalar arg) > { > (*f)(arg) > } > > We could teach Mata to understand that, although we will not do that. > > One feature of Mata is that Mata never crashes. Errors are > caught and Stata stays running. If we opened this door, You > could specify the name of the class incorrectly. How would we > know that you did not? If you did, a crash would be possible > unless we also add run-time code to catch the problem, and that will > slow down the execution of all functions in all contexts. > > In C++, you can do something like Richard wants, but if you do it > incorrectly, the resulting code crashes. Moreover, even in C++, the > standard implementations do not provide exactly what Richard wants. > C++ requires that a pointer to the class object be passed along with a > pointer to the function. And you must be sure that you get the > pointers right or the resulting code crashes. > > One solution Richard might consider is to move the functions outside > of the class and pass the class object explicitly to the functions. > Mata does allow pointers to ordinary functions and they may be used > even inside classes. > > The other possible solution, which can be implemented inside the class, > is to use codes rather than address and a switcher function inside > the class to call the appropriate function. The code would look like this: > > > mata: > > class testClass { > void run1() > void run2() > real scalar func(), func2(), func3() > real scalar switch() > } > > real scalar testClass::func(real scalar arg) > { > return(arg) > } > > /* define func2(), func3() how you wish */ > > real scalar testClass::switch(real scalar id, real scalar arg) > { > if (id==1) return(this.func(arg)) > if (id==2) return(this.func2(arg)) > if (id==3) return(this.func3(arg)) > _error("invalid id" > } > > ... > > -- Bill -- Hua Peng > wgould@stata.com hpeng@stata.com > > * > * For searches and help try: > * http://www.stata.com/help.cgi?search > * http://www.stata.com/support/faqs/resources/statalist-faq/ > * http://www.ats.ucla.edu/stat/stata/ * * For searches and help try: * http://www.stata.com/help.cgi?search * http://www.stata.com/support/faqs/resources/statalist-faq/ * http://www.ats.ucla.edu/stat/stata/

**References**:**Re: st: Pointer to class member function?***From:*"William Gould, StataCorp LP" <wgould@stata.com>

- Prev by Date:
**Re: st: comparing regression coefficients across two models with the same dependent variables** - Next by Date:
**st: How to calculate intercept and slope of ROC curve - STATA 12.1** - Previous by thread:
**Re: st: Pointer to class member function?** - Next by thread:
**st: Solving simultaneous equations** - Index(es):