Topic: Suggestion: making bound member functions usable


Author: Dennis Yelle <dennis51@jps.net>
Date: Mon, 26 Mar 2001 06:36:13 GMT
Raw View
Marco Manfredini wrote:
>
> Currently an expression that represents a bound member function can only
> be used to call this function in the expression where it is formed.
>
> Example:
>
> class A
> {
> public:
>         int foo() { return 0; }
> };
>
> template<class T>
> void use(T x)
> {
>         x();
> }
> int main()
> {
>         A a;
>         a.foo(); // OK
>         use(a.foo); // not OK, there is no Type that can hold a.foo
> }
>
> On the other hand, C++ is powerful enough to describe objects that act
> like functions, we know them as functors, and a C++ programmer would
> come up with a solution that would essentially look like that:
>
> template<class INST, class RET>
> struct bound_member_fun0_t
> {
>  INST &inst;
>  typedef RET (INST::*mf_t)(void);
>  mf_t mf;
>  RET operator () (void) { return (inst.*mf)(); }
>  bound_member_fun0_t(INST &_inst, mf_t &_mf) : inst(_inst), mf(_mf) {}
> };
>
> template<class INST, class RET>
> bound_member_fun0_t<INST,RET>
> bound_member_function(INST &inst, RET (INST::*mf)(void))
> {
>         return bound_member_fun0_t<INST,RET>(inst, mf);
> }
> int main()
> {
>         A a;
>         use(bound_member_function(a,&A::foo));
> }
>
> My suggestion now is a very simple one: Whenever the compiler sees an
> bound member function expression, which is *not* used to call the
> function, it simply replaces it with an appropriate call to (the
> typically overloaded) operator bound_member_function using the current
> lookup rules. The parameters to operator bound_member_function are the
> two components that form the BMF expression, i.e. the instance pointer
> and the member function pointer. The return value of operator
> bound_member_function shall be an object which overloads the operator ()
> with a signature that matches the signature of the member function minus
> the instance dependent part.
>
> Examples:
>
> 1.
> template<class INST, class RET>
> operator bound_member_fun0_t<INST,RET>
> bound_member_function(INST &inst, RET (INST::*mf)(void))
> {
>         return bound_member_fun0_t<INST,RET>(inst, mf);
> }
>
> class B
> {
>  B *prev;
>  int foo() {}
>  void bar()
>  {
>   use(foo);
>   use(prev->foo);
>  }
> };
>
> becomes:
>
> class B
> {
>  int foo() {}
>  void bar()
>  {
>   use(operator bound_member_function(*this, &B::foo));
>   use(operator bound_member_function(prev, &B::foo));
>  }
> };
[...]

> My proposal is to add syntactical sugar. But it would lead to more
> readable (and shorter) code and extent the language in an interesting
> way: Essentially operator bound_member_function implements an
> "invisible" operator: The compiler decides that he sees this operation
> from the lack of function invocation and delegates to the user to handle
> it.
>
> Comments? Suggestions?

There is still one piece missing.

I want to be able to call this function:

void twice( void (*f)() )
{
  f();
  f();
}

and pass it a pointer to a functor like
your bound_member_fun0_t above.

This is not as crazy as it seems.

All it requires is that the compiler build something that
looks like this:

bound_member_fun0_t* __hidden_data_123 = something;
void __hidden_123()
{
  (*__hidden_data_123)();
}

and then pass __hidden_123 to twice.

And it can all be on the stack.
(Assuming that twice does not try to store
 the function pointer in static storage.)

Dennis Yelle
--
I am a computer programmer and I am looking for a job.
There is a link to my resume here:
http://table.jps.net/~vert/

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: gt5163b@prism.gatech.edu (Brian McNamara!)
Date: Mon, 26 Mar 2001 14:03:02 GMT
Raw View
marco@technoboredom.net (Marco Manfredini) once said:
>   a.foo(); // OK
>   use(a.foo); // not OK, there is no Type that can hold a.foo
...
> use(bound_member_function(a,&A::foo));
...
>My suggestion now is a very simple one: Whenever the compiler sees an
>bound member function expression, which is *not* used to call the
>function, it simply replaces it with an appropriate call to (the
>typically overloaded) operator bound_member_function using the current
>lookup rules.
...
>Comments? Suggestions?

Not worth it.  As you have pointed out, the functionality is easily
achieved via a library.  The only difference is the syntax:

   use( a.foo )
versus
   use( F( &A::foo, a ) )

where "F" is whatever you happen to name the library function which
enables the binding.  I don't think it's worth further complicating the
language for such a minor convenience.

Furthermore, this extension only addresses binding the receiver object
in a method call, whereas a library approach easily generalizes to
binding any argument of any function.  For example, given

   int f( int, int );
   struct Foo {
      int g( int, int );
   };

   template <class H>
   int use( H h ) {
      return h(3);
   }

we can easily adapt "f" and "g" to be used with "use" via library:

   use( F(&f)(4)   );     // call f(4,3)
   use( F(&f)(4,_) );     // call f(4,3)
   use( F(&f)(_,4) );     // call f(3,4)
   Foo foo;
   use( F(&Foo::g)(&foo,4)   );    // call foo.g(4,3)
   use( F(&Foo::g)(&foo,4,_) );    // call foo.g(4,3)
   use( F(&Foo::g)(&foo,_,4) );    // call foo.g(3,4)

This is exactly how it works in the FC++ library: "F" is actually
called "ptr_to_fun", and it converts any function-like entity into a
uniform calling mechanism (a "functoid") which enables easy syntax for
binding its arguments.  Given a functoid, any trailing arguments left
unspecified, as well as any arguments specified as "_", are preserved as
arguments in the resulting function; the rest are bound to the values
specified.  This mechanism is far more general, at the expense of having
no convenient syntax for the commonest case of "a.foo" you suggested.

In fact, if you are really keen on having "a.foo" represent a bound
member function, you can actually already do this within the language as
well.  The general idea is:

   class A {
     void real_foo(int,int);
   public:
     const BMF foo; //BMF is bound_mem_fun_type<int,int,void> or whatever
     A() : foo(this,&A::real_foo) {}
   };

Now you can say

   a.foo(4,5);

just like normal, but also

   use( a.foo );

too.

--
 Brian M. McNamara   lorgon@acm.org  :  I am a parsing fool!
   ** Reduce - Reuse - Recycle **    :  (Where's my medication? ;) )

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Edward Diener <eddielee@tropicsoft.com>
Date: Mon, 26 Mar 2001 20:02:33 GMT
Raw View
Marco Manfredini wrote:

> Currently an expression that represents a bound member function can only
> be used to call this function in the expression where it is formed.
>
> Example:
>
> class A
> {
> public:
>         int foo() { return 0; }
> };
>
> template<class T>
> void use(T x)
> {
>         x();
> }
> int main()
> {
>         A a;
>         a.foo(); // OK
>         use(a.foo); // not OK, there is no Type that can hold a.foo
> }
>
> On the other hand, C++ is powerful enough to describe objects that act
> like functions, we know them as functors, and a C++ programmer would
> come up with a solution that would essentially look like that:

The rest is snipped for brevity...

Better yet, have a new type in C++, call it "bound-member function pointer",
which can hold the address of any bound member function in any class of a
particular prototype. Borland's C++ Builder under Microsoft Windows has such
a type with the unfortunate name of "__closure". This would greatly simplify
the use of member functions of any class as functors as well as event
holders. In C++ Builder, in non-ANSI mode, your above code compiles without
any errors.


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Marco Manfredini <marco@technoboredom.net>
Date: Tue, 27 Mar 2001 00:15:06 GMT
Raw View
Brian McNamara! wrote:

> marco@technoboredom.net (Marco Manfredini) once said:

> >Comments? Suggestions?
>
> Not worth it.  As you have pointed out, the functionality is easily
> achieved via a library.  The only difference is the syntax:
>
>    use( a.foo )
> versus
>    use( F( &A::foo, a ) )
>
> where "F" is whatever you happen to name the library function which
> enables the binding.  I don't think it's worth further complicating the
> language for such a minor convenience.

First, I don't think that the change will not complicate the language.
The replacement I suggest requires the compiler only to replace the
offending expression by a call to F, which is then compiled. This doesn't
look like it would break into million lines of compiler code, and the
change isn't even visible to the user (except for the new feature, that x.y
constructs a functor, if neccesary). Adding a working F to the library
shouldn't be expensive as well (Or it is already in there, see FC++)

Second, I think it is more than a minor convenience, if there is a way to
remove the duty to explicitly name types, when this information is already
present in the context (That's why we want typeof() or not?)  I'd rather
prefer to write
use(a(x).foo)
than
use(F(&what_ever_the_return_type_of_a_is_I_really_dont_care::foo, a(x)));

Which can be really hard, if a is overloaded and I have no clue what the
overload of a returns (unless I am traiting it). typeof() will be a relief,
but I suspect that it will lead to parrot code:

use(F(&typeof(polly_wants_a(cake))::foo, polly_wants_a(cake));

Third: Take for example

void bar(T &x)
{
 x->AMember();
 use(x->AMember);
}

x->AMember could mean, that T is a pointer type and y is a member function
(or a member that holds a function)
*or* x->AMember could mean, that T is of class type and has operator->
overloaded etc. If these cases shall be covered in bar, then the canonical
version of bar will soon look yucky, doesn't it?  With the little, tiny
extension you'll probably never care.

>
> Furthermore, this extension only addresses binding the receiver object
> in a method call, whereas a library approach easily generalizes to
> binding any argument of any function.

Well yes, but I see it as: transforming something that looks like a
callable thing into an callable thing, if the compiler can't do it.

> For example, given
>    int f( int, int );
>    struct Foo {
>       int g( int, int );
>    };
>
>    template <class H>
>    int use( H h ) {
>       return h(3);
>    }
>
> we can easily adapt "f" and "g" to be used with "use" via library:
>
>    use( F(&f)(4)   );     // call f(4,3)
>    use( F(&f)(4,_) );     // call f(4,3)
>    use( F(&f)(_,4) );     // call f(3,4)
>    Foo foo;
>    use( F(&Foo::g)(&foo,4)   );    // call foo.g(4,3)
>    use( F(&Foo::g)(&foo,4,_) );    // call foo.g(4,3)
>    use( F(&Foo::g)(&foo,_,4) );    // call foo.g(3,4)
>
> This is exactly how it works in the FC++ library: "F" is actually
> called "ptr_to_fun", and it converts any function-like entity into a
> uniform calling mechanism (a "functoid") which enables easy syntax for
> binding its arguments.  Given a functoid, any trailing arguments left
> unspecified, as well as any arguments specified as "_", are preserved as
> arguments in the resulting function; the rest are bound to the values
> specified.  This mechanism is far more general, at the expense of having
> no convenient syntax for the commonest case of "a.foo" you suggested.

The FC++ Library is truly impressive, and the currying syntax solution is
simply great! In STL I'd had to make puny calls to mem_fun and bind1st to
do this & after 5 minutes I would have forgotten what my code was supposed
to do :-). But let me tell you, that currying could be another thing, that
could be made transparent to the language with the help of the compiler & a
supporting library. Suppose he would transform the expression (I take the
'_' placeholder here):

f(1,2,_,3);
to:
ptr_to_fun(f)(1,2,_curry_placeholder,3);

or

a.b(3,_);
to:
ptr_to_fun(&A::b)(a,3,_curry_placeholder);

This puts a nice path to future extensions: Somebody demonstrates, how a
useful concept can be implemented within the *existing* language. If the
implementation is proven and the concept a living technique, then a
non-breaking syntax change could be made which sugars the use of the
implementation. The implementation becomes part of the standard library &
C++ becomes more expressive *without* adding too much load to the compiler
system (the ABI will not change, for example) , since the work is done in
the library, in C++.

>
> In fact, if you are really keen on having "a.foo" represent a bound
> member function, you can actually already do this within the language as
> well.  The general idea is:
>

You want me to follow Satan and spill my bytes to simulate syntax? Vade
retro :-)

--
Marco

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: gt5163b@prism.gatech.edu (Brian McNamara!)
Date: Mon, 26 Mar 2001 19:16:44 CST
Raw View
Marco Manfredini <marco@technoboredom.net> once said:
>Brian McNamara! wrote:
>> marco@technoboredom.net (Marco Manfredini) once said:
>> >Comments? Suggestions?
>> Not worth it. ...
>
>First, I don't think that the change will not complicate the language.

I think I will still argue this point, but your examples are starting to
win me over.

>present in the context (That's why we want typeof() or not?)  I'd rather
>prefer to write
>use(a(x).foo)
>than
>use(F(&what_ever_the_return_type_of_a_is_I_really_dont_care::foo, a(x)));
>
>Which can be really hard, if a is overloaded and I have no clue what the
>overload of a returns (unless I am traiting it). typeof() will be a relief,
>but I suspect that it will lead to parrot code:
>
>use(F(&typeof(polly_wants_a(cake))::foo, polly_wants_a(cake));

I had not considered this.  Hmmm...  :)

Well, if we have the other extension, you can do

   auto tmp = a(x);
   use( F(&typeof(tmp)::foo,tmp) );

but yeah, blech, it still sucks.

>Third: Take for example
>
>void bar(T &x)
>{
> x->AMember();
> use(x->AMember);
>}
>
>x->AMember could mean, that T is a pointer type and y is a member function
>(or a member that holds a function)
>*or* x->AMember could mean, that T is of class type and has operator->
>overloaded etc. If these cases shall be covered in bar, then the canonical
>version of bar will soon look yucky, doesn't it?  With the little, tiny
>extension you'll probably never care.

This is where I think it may truly complicate the language, though.  I
haven't thought about it too much yet, but

   template <class T>
   void f( T x ) {
      use( x.foo );
   }

now admits multiple meanings; foo may be a data member or a function.
With the -> operator things get even worse.  I think it is not
necessarily the case that there will ever be true ambiguities in the
language from this, but I think it does greatly increase the chance for
human error.

I guess my main argument though is not against special syntax for the
binding, but against it being implicit.  Your first example I quoted
shows the value of special syntax; I'd like to add the requirement that
you say what you're doing explicitly, so it doesn't just look like a
data member access.  Something like

   bind( obj.method )

which is more visible than the implicit

   obj.method

but less verbose than the current

   F(&typeof(obj)::method,obj)

Hmm.

>to do :-). But let me tell you, that currying could be another thing, that
>could be made transparent to the language with the help of the compiler & a
>supporting library. Suppose he would transform the expression (I take the
>'_' placeholder here):
>
>f(1,2,_,3);
>to:
>ptr_to_fun(f)(1,2,_curry_placeholder,3);
>
>or
>
>a.b(3,_);
>to:
>ptr_to_fun(&A::b)(a,3,_curry_placeholder);
>
>This puts a nice path to future extensions: Somebody demonstrates, how a
>useful concept can be implemented within the *existing* language. If the

Indeed.  I wish I had a way to make this compile even:

   (&A::b)(a,3,_)

by somehow invoking an implicit conversion so that it means

   X(&A::b).operator()(a,3,_)

where X is a type admits an implicit conversion from the
pointer-to-member-function.  But I'm pretty sure that's impossible (and
reasonably so).

>> In fact, if you are really keen on having "a.foo" represent a bound
>> member function, you can actually already do this within the language as
>> well.  The general idea is:
>
>You want me to follow Satan and spill my bytes to simulate syntax? Vade
>retro :-)

:)

My example does illustrate a useful point, perhaps, though.  That is
this: when you cheat and make a.foo a data member that is a functor, you
solve the syntax problem by having the expression

   a.foo

be an _expression_ with a _type_.  Which I guess is maybe your original
point: "a.foo" should be an expression of type

   bound_mem_fun_blah_blah<whatever,stuff>

so that

   a.foo(x,y)

still behaves exactly the same as before but now

   a.foo

is also an expression.

Hmmmm.


No doubt Andrei is reading this and putting a little tick in his book
of "1000 reasons member functions are evil".  :)

--
 Brian M. McNamara   lorgon@acm.org  :  I am a parsing fool!
   ** Reduce - Reuse - Recycle **    :  (Where's my medication? ;) )

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Dennis Yelle <dennis51@jps.net>
Date: Tue, 27 Mar 2001 16:37:42 CST
Raw View
"Brian McNamara!" wrote:
[...]
> Indeed.  I wish I had a way to make this compile even:
>
>    (&A::b)(a,3,_)
>
> by somehow invoking an implicit conversion so that it means
>
>    X(&A::b).operator()(a,3,_)
>
> where X is a type admits an implicit conversion from the
> pointer-to-member-function.  But I'm pretty sure that's impossible (and
> reasonably so).

Sorry, I have no idea what you are saying here.
What is A?  What is b?
And especially, what is X?

Dennis Yelle
--
I am a computer programmer and I am looking for a job.
There is a link to my resume here:
http://table.jps.net/~vert/

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: marco@technoboredom.net (Marco Manfredini)
Date: Tue, 27 Mar 2001 16:37:41 CST
Raw View
gt5163b@prism.gatech.edu (Brian McNamara!) wrote in
<99opbs$73f$1@news-int.gatech.edu>:

>Marco Manfredini <marco@technoboredom.net> once said:
>>Third: Take for example
>>
>>void bar(T &x)
>>{
>> x->AMember();
>> use(x->AMember);
>>}
>>
>>x->AMember could mean, that T is a pointer type and y is a member
>>function (or a member that holds a function)
>>*or* x->AMember could mean, that T is of class type and has
>>operator-> overloaded etc. If these cases shall be covered in bar,
>>then the canonical version of bar will soon look yucky, doesn't it?
>> With the little, tiny extension you'll probably never care.
>
>This is where I think it may truly complicate the language, though.
>I haven't thought about it too much yet, but
>
>   template <class T>
>   void f( T x ) {
>      use( x.foo );
>   }
>
>now admits multiple meanings; foo may be a data member or a
>function. With the -> operator things get even worse.  I think it is
>not necessarily the case that there will ever be true ambiguities in
>the language from this, but I think it does greatly increase the
>chance for human error.

My assumption is, that the programmer knows about the application of
x.foo, that is he knowns, that he can use it in a call "x.foo(1,2,3)" -
foo can be a data member that holds a function pointer or another kind
of callable object of course, but that difference doesn't play a role
(the compiler already can handle these, you showed how to use it). If
foo is a non-callable data member, then the compiler will complain when
the binding object will try to call "foo".

>
>I guess my main argument though is not against special syntax for
>the binding, but against it being implicit.  Your first example I
>quoted shows the value of special syntax; I'd like to add the
>requirement that you say what you're doing explicitly, so it doesn't
>just look like a data member access.  Something like
>
>   bind( obj.method )
>
>which is more visible than the implicit
>
>   obj.method
>
>but less verbose than the current
>
>   F(&typeof(obj)::method,obj)
>
>Hmm.

Yes, the language already includes so many implicitness that adding more
needs nerves. (like single stepping through a innocent looking statement
which suddenly explodes into conversions and constructions). But in this
case I'm quite faithful, since the syntax has it's analogous form in
plain functions and data:

int d;
void f(int);

d; // a data
f; // a function pointer
f(3); // calling

class A
{
 public:
  int d;
  void f(int);
}

A a;
a.d; // data
a.f; // a "function pointer"
a.f(3); // calling.

(I admit, that the implicit conversion from a function to a function-
pointer is a trap-feature of C/C++. But the extensions is consistent
with it :-))

>
>>to do :-). But let me tell you, that currying could be another
>>thing, that could be made transparent to the language with the help
>>of the compiler & a supporting library. Suppose he would transform
>>the expression (I take the '_' placeholder here):
>>
>>f(1,2,_,3);
>>to:
>>ptr_to_fun(f)(1,2,_curry_placeholder,3);
>>
>>or
>>
>>a.b(3,_);
>>to:
>>ptr_to_fun(&A::b)(a,3,_curry_placeholder);
>>
>>This puts a nice path to future extensions: Somebody demonstrates,
>>how a useful concept can be implemented within the *existing*
>>language. If the
>
>Indeed.  I wish I had a way to make this compile even:
>
>   (&A::b)(a,3,_)
>
>by somehow invoking an implicit conversion so that it means
>
>   X(&A::b).operator()(a,3,_)
>
>where X is a type admits an implicit conversion from the
>pointer-to-member-function.  But I'm pretty sure that's impossible
>(and reasonably so).

I remember when I learned C++, I wondered why &A::b can't be used as a
"normal" function with an A as extra parameter. I imagined, that the
compiler simply generates for every member function declared a "C"
compatible function which dispatches over its first argument. There
wasn't such a thing and till today I occasionaly have to  write C
wrappers that look like:

class IFoo
{
public:
 virtual void foo(int x)=0;
};

extern "C" { void IFoo_foo(IFoo *f, int x) { f->foo(x); } }

>>You want me to follow Satan and spill my bytes to simulate syntax?
>>Vade retro :-)
>
>:)
>
>My example does illustrate a useful point, perhaps, though.  That is
>this: when you cheat and make a.foo a data member that is a functor,
>you solve the syntax problem by having the expression
>
>   a.foo
>
>be an _expression_ with a _type_.  Which I guess is maybe your
>original point: "a.foo" should be an expression of type
>
>   bound_mem_fun_blah_blah<whatever,stuff>
>
>so that
>
>   a.foo(x,y)
>
>still behaves exactly the same as before but now
>
>   a.foo
>
>is also an expression.
>
>Hmmmm.
>
True, this is a way. But prebuilding all BMF's I will probably need and
store them in my instance, is very wasteful (unless I **really** need
the syntax for some exotic needs). The cruel thing about the BMF's is
that they are comparably large, they contain at least an instance and a
member function pointer. This can sum up to 24 bytes, if virtual
inheritance comes into the play. And I have to clutter my constructors
initialization list for them, which is also bad, because I don't want my
code to show me how he is doing something, but what he is doing.

>
>No doubt Andrei is reading this and putting a little tick in his
>book of "1000 reasons member functions are evil".  :)
>
I hope he doesn't have a list of people to beat up :-)

--
Marco

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: gt5163b@prism.gatech.edu (Brian McNamara!)
Date: Tue, 27 Mar 2001 17:49:22 CST
Raw View
Dennis Yelle <dennis51@jps.net> once said:
>"Brian McNamara!" wrote:
>> Indeed.  I wish I had a way to make this compile even:
>>
>>    (&A::b)(a,3,_)
>>
>> by somehow invoking an implicit conversion so that it means
>>
>>    X(&A::b).operator()(a,3,_)
>>
>> where X is a type admits an implicit conversion from the
>> pointer-to-member-function.  But I'm pretty sure that's impossible (and
>> reasonably so).
>
>Sorry, I have no idea what you are saying here.
>What is A?  What is b?
>And especially, what is X?

A is a class, b is a method in that class, and X is a class which has a
non-explicit constructor from &A::b.  In other words,

   struct A { void b( int, int ) {} };
   struct X {
      typedef void (A::*F)(int,int);
      F f;
      X( F g ) : f(g) {}    // implicit conversion
      void operator()( A* a, int x, int y ) const {
         a->*f(x,y);
      }
   };
   int main() {
      A a;
      // (&A::b)(&a,3,4);  // would be nice
      (X(&A::b))(&a,3,4);  // legal
      (X(&A::b)).operator()(&a,3,4);  // more explicit version
   }

Except that in the general case, we'd write 'X' as a template, so it
can turn any class's (not just A's) member functions (not just
two-argument int,int->void ones) into calls that take the receiver
object as an extra parameter.

--
 Brian M. McNamara   lorgon@acm.org  :  I am a parsing fool!
   ** Reduce - Reuse - Recycle **    :  (Where's my medication? ;) )

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: marco@technoboredom.net (Marco Manfredini)
Date: Sun, 25 Mar 2001 19:38:48 GMT
Raw View
Currently an expression that represents a bound member function can only
be used to call this function in the expression where it is formed.

Example:

class A
{
public:
 int foo() { return 0; }
};

template<class T>
void use(T x)
{
 x();
}
int main()
{
 A a;
     a.foo(); // OK
 use(a.foo); // not OK, there is no Type that can hold a.foo
}

On the other hand, C++ is powerful enough to describe objects that act
like functions, we know them as functors, and a C++ programmer would
come up with a solution that would essentially look like that:

template<class INST, class RET>
struct bound_member_fun0_t
{
 INST &inst;
 typedef RET (INST::*mf_t)(void);
 mf_t mf;
 RET operator () (void) { return (inst.*mf)(); }
 bound_member_fun0_t(INST &_inst, mf_t &_mf) : inst(_inst), mf(_mf) {}
};

template<class INST, class RET>
bound_member_fun0_t<INST,RET>
bound_member_function(INST &inst, RET (INST::*mf)(void))
{
     return bound_member_fun0_t<INST,RET>(inst, mf);
}
int main()
{
 A a;
 use(bound_member_function(a,&A::foo));
}

My suggestion now is a very simple one: Whenever the compiler sees an
bound member function expression, which is *not* used to call the
function, it simply replaces it with an appropriate call to (the
typically overloaded) operator bound_member_function using the current
lookup rules. The parameters to operator bound_member_function are the
two components that form the BMF expression, i.e. the instance pointer
and the member function pointer. The return value of operator
bound_member_function shall be an object which overloads the operator ()
with a signature that matches the signature of the member function minus
the instance dependent part.

Examples:

1.
template<class INST, class RET>
operator bound_member_fun0_t<INST,RET>
bound_member_function(INST &inst, RET (INST::*mf)(void))
{
     return bound_member_fun0_t<INST,RET>(inst, mf);
}

class B
{
 B *prev;
 int foo() {}
 void bar()
 {
  use(foo);
  use(prev->foo);
 }
};

becomes:

class B
{
 int foo() {}
 void bar()
 {
  use(operator bound_member_function(*this, &B::foo));
  use(operator bound_member_function(prev, &B::foo));
 }
};

2. Hypothetic Application Framework.

// A command button widget
class TButton : public TFrame
{
 /* event<int,int> implements an template operator += with the following
 signature:
 template<class T>
 void operator += (const bound_member_function2_t<T,void,int,int> &bmf);
 optional overloads:
 void operator += (void (*fn)(int,int));
 */
public:
 event<int,int>& onClick();
 /*etc..*/
};

// a Form that want to use a command button.
class TMyForm : public TForm
{
 TButton button;
 void doClick(int x,int y) { /*...*/ }
public:
 TMyForm()
 {
  button.onClick()+=doClick;
 }
};

Using the suggestion, TMyForm becomes:

class MyForm : public TForm
{
 TButton button;
 void doClick(int x,int y) { /*...*/ }
public:
 MyForm()
 {
  button.onClick()+=operator bound_member_function(*this,
&MyForm::doClick);
 }
};

My proposal is to add syntactical sugar. But it would lead to more
readable (and shorter) code and extent the language in an interesting
way: Essentially operator bound_member_function implements an
"invisible" operator: The compiler decides that he sees this operation
from the lack of function invocation and delegates to the user to handle
it.

Comments? Suggestions?

--
Marco

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]