Bookmark and Share

Notice: On March 31, it was announced that Statalist is moving from an email list to a forum. The old list will shut down at the end of May, and its replacement, statalist.org is already up and running.


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

Re: st: Pointer to class member function?


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/


© Copyright 1996–2014 StataCorp LP   |   Terms of use   |   Privacy   |   Contact us   |   Site index