Address of bound member function

389 views
Skip to first unread message

Matthew

unread,
Sep 20, 2015, 7:04:44 AM9/20/15
to ISO C++ Standard - Future Proposals
Hi,

I'm new to this list. I was wondering if there have been any proposals or interest in taking address of bound member functions to return a lambda.

For example:
&object->memfun
would be shorthand
for the following lambda:
[object](params_of_memfun...)->return_type_of_memfun {
   
return object->memfun(std::fwd<>(params_of_memfun...));
}



Nicol Bolas

unread,
Sep 20, 2015, 9:39:05 AM9/20/15
to ISO C++ Standard - Future Proposals
I'll ignore the syntax and instead focus on the general idea.

It's way too narrowly focused. There is already a general desire to take an arbitrary callable name (not a function/member pointer, but a name) and convert it into a functor that forwards its arguments and return values, keeping intact any and all overloading that might exist, with the exact access controls (public/private/protected) that are available at the cite where the functor was created.

The only advantage your suggestion has is the automatic binding of the object pointer. And to be frank, automatic binding like that is dangerous, since it can lead to resource issues. Binding ought to be more explicit.

David Krauss

unread,
Sep 20, 2015, 10:37:28 AM9/20/15
to std-pr...@isocpp.org
Namely, we have std::bind( &class::member, obj );

Any overload set reification proposal should account for member functions, though. They’re easy to forget about.

Nicol Bolas

unread,
Sep 20, 2015, 1:00:11 PM9/20/15
to ISO C++ Standard - Future Proposals

At first, I was going to say that it's unnecessary once we have the ability to call member functions without member syntax (automatically converting member(object) into object.member() if the first doesn't match). But then I realized that this would unfairly bias reification towards non-member functions, allowing you to override overload sets externally to a class.

That may be what you want, and it should be possible. But you may want the other way around, and the automatic conversion is supposed to go both ways (converting object.member() into member(object) if the first doesn't match). And it's reasonable to let the user decide which to bias reification in favor of: member or non-member.

And of course we'll need both ways if we don't get this automatic conversion syntax ;)

Viacheslav Usov

unread,
Oct 27, 2015, 7:43:29 AM10/27/15
to ISO C++ Standard - Future Proposals



That discussion was in itself a spin-off of an earlier (non)discussion in yet another forum: https://groups.google.com/d/topic/comp.std.c++/NyViDQRDHR8/discussion 

Cheers,
V.

Brent Friedman

unread,
Oct 27, 2015, 10:39:08 AM10/27/15
to std-pr...@isocpp.org
sg14 is interested in being able to take a virtual function + object and resolve the specific function that would be called. We could use such a feature to do manual devirtualization and other efficiency optimizations.

The bound member function syntax is one of our ideas for how to express "what function will this resolve to?".

auto obj_func = &(obj->fun);
if (obj_func == &derived::fun)
{
   obj->derived::fun();
}
else
{
   obj->fun();
}

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Nicol Bolas

unread,
Oct 27, 2015, 11:02:38 AM10/27/15
to ISO C++ Standard - Future Proposals
On Tuesday, October 27, 2015 at 10:39:08 AM UTC-4, Brent Friedman wrote:
sg14 is interested in being able to take a virtual function + object and resolve the specific function that would be called. We could use such a feature to do manual devirtualization and other efficiency optimizations.

I'm curious as to how, exactly. A virtual call involves getting the vtable, followed by fetching the function pointer through an offset.

In order to do devirtualization within a hierarchy, you would have to have each instance of different derived classes store function objects to the actual function to be called. But since the storage for that functor is in the base class, the object being stored has to be able to store any such member pointer. I'm not sure if it's possible to do that without type erasure or some other kind of virtual call.

Maybe I'm misunderstanding what you mean by "devirtualization".

Nicol Bolas

unread,
Oct 27, 2015, 11:18:15 AM10/27/15
to ISO C++ Standard - Future Proposals
On Tuesday, October 27, 2015 at 7:08:35 AM UTC-4, Edward Catmur wrote:
Most recent discussion on std-proposals: https://groups.google.com/a/isocpp.org/d/topic/std-proposals/eltqBVyh5H0/discussion - should we move to there?

On Monday, 26 October 2015 20:24:35 UTC, Nicol Bolas wrote:
On Monday, October 26, 2015 at 2:54:54 PM UTC-4, Miro Knejp wrote:
Am 26.10.2015 um 19:03 schrieb Nicol Bolas:
On Monday, October 26, 2015 at 1:01:46 PM UTC-4, Viacheslav Usov wrote:
I'm pretty sure this is missing a number of `std::forward` calls and `&&` syntax.
Which just strengthens his position that the "proper" way to do it is difficult to get right and suffers from boilerplate.

I wasn't contesting the need for the general idea. My problem was with his chosen form.

C++ does need some way to say "give me a functor that will call this function as if I typed that name." But this ought to be separate from binding to variables, since that's way too easy to get wrong.

If you don't provide object binding, people are going to use std::bind or some partial applicator, which is just as easy to get wrong.

And what about the people who don't want to have binding mixed into it? Who just want the overload member function set, callable on any appropriate object? Whatever syntax gets used has to account for all of the viable options.

We already want overload syntax, for member functions and non-member functions. The obvious implementation is a lambda, so simply allow lambdas to do it cleanly, without the boilerplate:

[]Class::Funcname; //Functor that calls the named member function, which takes as a first parameter a Class reference/pointer/object.
[d]d.Funcname;     //Functor that calls the named member function, using a variable bound by lambda rules.
[]d.Funcname;      //A compile error. You must explicitly bind the variable.
[]Funcname;        //Functor calling a non-member function.
[this]Funcname;    //Functor possibly calling a member function or non-member function, depending on the type of `this`.

Brent Friedman

unread,
Oct 27, 2015, 11:45:46 AM10/27/15
to std-pr...@isocpp.org
I'm curious as to how, exactly. A virtual call involves getting the vtable, followed by fetching the function pointer through an offset.

Right - the thought behind this is that &(obj->fun) would get the vtable for obj and fetch the function pointer. It effectively separates the virtual lookup step from the virtual call step. The result of the expression is the concrete member function (or overload resolution set) that would be called. We don't entirely know if it's feasible, sort of a brainstorm at the moment.

--

Viacheslav Usov

unread,
Oct 27, 2015, 2:02:31 PM10/27/15
to std-pr...@isocpp.org
We already want overload syntax, for member functions and non-member functions. The obvious implementation is a lambda, so simply allow lambdas to do it cleanly, without the boilerplate:

[]Class::Funcname; //Functor that calls the named member function, which takes as a first parameter a Class reference/pointer/object.
[d]d.Funcname;     //Functor that calls the named member function, using a variable bound by lambda rules.
[]d.Funcname;      //A compile error. You must explicitly bind the variable.
[]Funcname;        //Functor calling a non-member function.
[this]Funcname;    //Functor possibly calling a member function or non-member function, depending on the type of `this`.

How would we wrap static member functions with this?

I think this could be generalized further by introducing a placeholder syntax. Something like the following: * forwards one argument, and ... forwards any number, and it would not need to be at the end of the argument list.

[a]a.f(...) // functor that calls member f, forwarding any number of arguments
[a, b]a.f(b, ...) // functor that calls member f, with captured b and any number of other arguments
[a, b, c]a.f(*, b, ..., c) // functor that calls member f, with one placeholder, captured b and any number of other arguments in between
[b, c]*.f(b, ..., c) // functor that calls member f of first placeholder, with captured b and any number of other arguments in between

In principle, the above does not need to be limited to "callable" names, any expression should work just fine, but then dealing with placeholders becomes messier. In principle again, nothing speaks against allowing lambda-declarator_opt of [expr.prim.lambda] in such lambdas, but that will re-introduce boilerplate. This needs more thinking.

Cheers,
V.

Nicol Bolas

unread,
Oct 27, 2015, 2:36:16 PM10/27/15
to ISO C++ Standard - Future Proposals
On Tuesday, October 27, 2015 at 2:02:31 PM UTC-4, Viacheslav Usov wrote:
We already want overload syntax, for member functions and non-member functions. The obvious implementation is a lambda, so simply allow lambdas to do it cleanly, without the boilerplate:

[]Class::Funcname; //Functor that calls the named member function, which takes as a first parameter a Class reference/pointer/object.
[d]d.Funcname;     //Functor that calls the named member function, using a variable bound by lambda rules.
[]d.Funcname;      //A compile error. You must explicitly bind the variable.
[]Funcname;        //Functor calling a non-member function.
[this]Funcname;    //Functor possibly calling a member function or non-member function, depending on the type of `this`.

How would we wrap static member functions with this?

If `Class::Function` is a static member, then the resulting functor calls that.
 
I think this could be generalized further by introducing a placeholder syntax. Something like the following: * forwards one argument, and ... forwards any number, and it would not need to be at the end of the argument list.

I'd much rather start small and expand it later than try to cram arbitrary and universal binding syntax into this. By focusing on the biggest problem (wrapping a function overload set), you can then extend it as needed later.

Just having the basic overload set stuff I outlined above solves about 90% of uses of std::bind.

Sean Middleditch

unread,
Oct 28, 2015, 12:10:48 AM10/28/15
to ISO C++ Standard - Future Proposals
On Tuesday, October 27, 2015 at 8:18:15 AM UTC-7, Nicol Bolas wrote:
We already want overload syntax, for member functions and non-member functions. The obvious implementation is a lambda, so simply allow lambdas to do it cleanly, without the boilerplate:

[]Class::Funcname; //Functor that calls the named member function, which takes as a first parameter a Class reference/pointer/object.
[d]d.Funcname;     //Functor that calls the named member function, using a variable bound by lambda rules.
[]d.Funcname;      //A compile error. You must explicitly bind the variable.
[]Funcname;        //Functor calling a non-member function.
[this]Funcname;    //Functor possibly calling a member function or non-member function, depending on the type of `this`.


That looks a lot like an extended variation on https://isocpp.org/files/papers/n3617.txt for what it's worth.

It's an active EWG issue (http://cplusplus.github.io/EWG/ewg-active.html#65) and so far as I can tell has never been thoroughly reviewed or voted on.

Someone with an interest should write a followup paper and take it to Jacksonville. :)

Matthew

unread,
Oct 28, 2015, 1:17:43 AM10/28/15
to ISO C++ Standard - Future Proposals

Proposal for address of bound function:

 

I want to give a more concrete proposal for how I think the address of bound function operation would work. These ideas are based on two very specific use cases, so I will state those up-front because it appears from reading people's comments that there are other use cases which I haven't given much consideration to. Note also, I will use the syntax (&object.fun) as example syntax. Other syntax may be more appropriate. I've just thrown this together for now, as I would like feedback before getting more specific/detailed.

 

Use case 1: "Native C++" callback functions:

C currently has a fairly standard callback function system, which consists of the following type of registration function:

 

typedef RetType(Callback*)(void* UserData, params…)

void RegisterCallback(Callback* CallbackFunc, void* UserData);

 

This works well for C where data/functions are not linked. However, in many cases C++ programs want UserData to point to an object, and callbackfunc to be a member function of that object. That is, we want to be able to have:

 

typedef RetType(*::MemCallback*)(params)

void RegisterCallback(MemCallback* CallbackMemberFunction);

 

which could be called using the following or similar syntax:

RegisterCallback(&object.memfunc)

 

I think that this could be described as the "native C++" equivalent to C-style callback functions. Note that it is possible for this to work without dynamic memory allocation and by utilizing only the memory space of 2 pointers for storing the function call. However, it is not possible with the current C++ spec.

 

Use case 2: Wrapping C-style callbacks

 

The second related use case is for wrapping C-style callback function syntax in the native C++ syntax. So if we are using a C library in C++, we should be able to have a wrapper to which we can pass RegisterCallback(&object.memfunc), and have it internally prepare a non-member function pointer and void* pointer which contain all of the necessary information to have a member function called by the callback function. In my proposal, this is possible and fairly efficient via the indirect use of stub functions.

 

Just a special note of consideration for this use case is that we cannot rely on the C library always passing the void* userdata argument as the first argument in a function or in a way that is compatible with the thiscall calling convention.

 

Problems with Current C++:

The current C++ spec has a number of problems, the most severe of which is the implementation of member function pointers. (Note I'm basing this on the MSVC implementation, but I believe these problems are common to GCC, etc.)

 

Problem 1:

There are 3 different and completely incompatible types of member function pointers based on the class of which the function is a member. If the class is purely singly inherited, the member pointer is implemented as a single pointer to the function code, which is something that is relatively easy to work with. However, if the class contains multiple inheritance or virtual inheritance in the hierarchy, then the member pointer additionally has information relating to adjusting the offset of the object pointer, and these three different structures are incompatible. In the case of callback functions, however, the object and function pointer are always supplied together. There should be no need to have different size structures when the object is known in advance.

 

Problem 2:

The second problem is that the current system is error prone. Take the following example for the case of trying to implement a member function callback system:

 

template<class Obj_t, class MemObj_t>

void RegisterCallback(Obj_t* obj, void (MemObj_t::mem_fun*)(params…))

 

class Base1 {
};

class Derived1 : Base1 {

    virtual void DoStuff();

};

 

class Base2 {
    virtual void DoStuff();

};

class Derived2 : Base2 {

    virtual void DoStuff();

};

 

Base1 b1;

RegisterCallback(&b1, &Derived1::DoStuff);

 

Base2 b2;

RegisterCallback(&b2, &Derived2::DoStuff);

 

The first example is broken, but we do not get any compile-time errors.

The second example is fine, and is the reason we need to allow different classes in the object and member function definitions.

 

Problem 3:

The third problem is related to passing virtual functions to C-style callback systems. It is basically impossible to do without implementing stub objects that store additional information.

 

Notation:

I first want to introduce special notation I want to use to describe the proposal.

THISCALL(func*, object*, params…)

This is pseudo-code to represent the compilation code that actually makes a function call given a pointer to the actual function code, and the pointer to the this object. Note that the object* pointer here is required to be already offset-adjusted to point to the correct location within the inheritance hierarchy. It is expected to be a void* at runtime.

 

func* VTABLOOKUP(object*, index)

This is pseudo-code for taking an object (as before, already offset-adjusted to point to the correct vtable for the function being called) and the index within that table, and returning a pointer to the function that will actually run. The function returned from the vtable will take an object* as the this pointer without adjustment because of the way vtables are constructed.

 

Returned structure:

Because of the differences between virtual member functions and non-virtual member functions, the structures returned by (&obj.fun) will need to be slightly different but only in terms of type information. The actual data stored in the structures will be identically usable.

 

For virtual functions, &obj.fun returns:

 

template<int virtindex, class object_t, class RetType(Params…)>

struct VirtualMemFunc {

              object_t* callobject;

              func* functionentrypoint;

             

              // For "native C++" callback

              RetType operator(Params…) {

                            return THISCALL(functionentrypoint, callobject, Params…);

              }

 

              // For "C-style" callback

              typedef RetType(Param0Callback*)(void* object, Params…);

              typedef RetType(Param1Callback*)(Param0_t Param0, void* object, Params…);

 

              Param0Callback* GetCCallbackStub0() {

                            return &VirtualMemFuncCStub<virtindex, RetType(Params…)>::Stub0;

              }

              Param1Callback* GetCCallbackStub1() {

                            return &VirtualMemFuncCStub<virtindex, RetType(Params…)>::Stub1;

              }

              void* GetCCallbackUserData() {

                            return (void*) callobject;

              }

};

 

template<int virtindex, class RetType(Params…)>

struct VirtualMemFuncCStub {

              static RetType Stub0(void* UserData, Params…) {

                            return THISCALL(VTABLOOKUP(UserData, virtindex), UserData, Params…);

              }

              static RetType Stub1(Param0_t Param0, void* UserData, Params…) {

                            return THISCALL(VTABLOOKUP(UserData, virtindex), UserData, Param0, Params…);

              }

}

 

For non-virtual member functions, &obj.fun returns:

template<uint64_t funcaddr, class object_t, class RetType(Params…)>

struct NonVirtualMemFunc {

              object_t* callobject;

              func* functionentrypoint;

             

              // For "native C++" callback

              RetType operator(Params…) {

                            return THISCALL(functionentrypoint, callobject, Params…);

              }

 

              // For "C-style" callback

              typedef RetType(Param0Callback*)(void* object, Params…);

              typedef RetType(Param1Callback*)(Param0_t Param0, void* object, Params…);

 

              Param0Callback* GetCCallbackStub0() {

                            return &NonVirtualMemFuncCStub<funcaddr, RetType(Params…)>::Stub0;

              }

              Param1Callback* GetCCallbackStub1() {

                            return &NonVirtualMemFuncCStub<funcaddr, RetType(Params…)>::Stub1;

              }

              void* GetCCallbackUserData() {

                            return (void*) callobject;

              }

};

 

template<uint64_t funcaddr, class RetType(Params…)>

struct NonVirtualMemFuncCStub {

              static RetType Stub0(void* UserData, Params…) {

                            return THISCALL(funcaddr, UserData, Params…);

              }

              static RetType Stub1(Param0_t Param0, void* UserData, Params…) {

                            return THISCALL(funcaddr, UserData, Param0, Params…);

              }

}

 

Notes on Abstraction/Reification:

The basic idea is that any offset-adjustment is performed at the time that (&obj.fun) is called. That is, for the following:

 

class Base1 {

   int dat;

}

class Base2 {

   virtual DoStuff() {};

}

class Derived : Base1, Base2 {

};

Derived d;

auto PtrToMemFun = (&d.DoStuff);

 

In this case, at compile time the compiler sees that DoStuff is defined in Base2. Thus, the structure returned is:

VirtualMemFunc<0, Base2, RetType(Params…)>

That is, the object stored in the structure points to Base2, not to Derived. There is no further need for pointer adjustment, and so the type information is no longer needed in terms of actually calling the function. The same is true of non-virtual member functions. That is, we can use a common base class:

 

template<class RetType(Params…)>

struct CommonMemFunc {

              void* callobject;

              void* functionentrypoint;

             

              RetType operator(Params…) {

                            return THISCALL(functionentrypoint, callobject, Params…);

              }

}

 

For C-style callbacks, we have a similar situation. We can extract the function call and void* UserData pointers to get a common set of data.

 

Usage:

For "native C++" callbacks, it would be sufficient to define:

 

void RegisterCallback(CommonMemFunc<RetType(Params…)> callbackfunc) {

}

 

The structure is purely a set of 2 pointers that do not need type information.

 

For C-style callback wrappers things are slightly more complicated since we need to have access the reified structures to get the correct stub function:

 

template<int virtindex, class object_t>

void RegisterCallback(VirtualMemFunc<virtindex, object_t, RetType(Params…)> callbackfunc) {

              CCodeRegisterCallback(callbackfunc.GetCCallbackStub0(), callbackfunc.GetCCallbackUserData());

}

 

template<uint64_t funcaddr, class object_t>

void RegisterCallback(NonVirtualMemFunc<funcaddr, object_t, RetType(Params…)> callbackfunc) {

              CCodeRegisterCallback(callbackfunc.GetCCallbackStub0(), callbackfunc.GetCCallbackUserData());

}

 

 

OK, this has gotten quite long. I hope this gives an idea of what I was looking for. Suggestions/questions welcome.

Viacheslav Usov

unread,
Oct 28, 2015, 5:35:54 AM10/28/15
to ISO C++ Standard - Future Proposals
On Tuesday, October 27, 2015 at 7:02:31 PM UTC+1, Viacheslav Usov wrote:

> I think this could be generalized further by introducing a placeholder syntax. Something like the following: * forwards one argument, and ... forwards any number, and it would not need to be at the end of the argument list.

[...]

> nothing speaks against allowing lambda-declarator_opt of [expr.prim.lambda] in such lambdas, but that will re-introduce boilerplate.

The problem with lambda-declarator_opt is that every variable mentioned there needs a type specifier, at least an auto. A variable that needs perfect forwarding has to be further decorated with &&, and further used with std::forward(). All of that bloats lambda definitions; I have also observed that this has a psychological effect on less experienced programmers who would perceive that as something esoteric and so to be avoided.

So the question is, can we make the type specifier fully optional in lambda-declarator_opt? For example, auto x = [a](b)(a + b) would mean we create a lambda that captures a, and has one argument denoted by b; the body of the lambda is the expression (a + b). Another example: auto x = [a](b)f(a, b); in this case, b is forwarded perfectly to f (so this is an example of partial application).

The semantics of an untyped variable in lambda-declarator_opt needs to be defined, and I suspect that could be tricky. Fundamentally, such a declaration means that the compiler should just do the Right Thing with that variable, which includes forwarding it perfectly where needed and possible. We obviously need well-defined behaviour, even if it cannot satisfy every conceivable need; it is even possible that the definition of the Right Thing needs to impose certain restrictions on the expression, the types that the lambda can be called with, or both. Which I think is fine, because in the more complicated cases the user can use the current fully-typed syntax; all we really need is a way to write simple lambda expressions in a simple way.

Another nice thing to have would be the ability to omit the type and name of variadic variables, e.g.: auto x = [a](...)a.f(...); this is the example of how the goal in the original post in this thread, and also in the referenced threads, can be achieved. This is not restricted to member functions, though, because one could equally say auto x = [a](...)f(a, ...).

With the above, I do not think we need the placeholders mentioned in my previous message.

Comments?

Cheers,
V.

Nicol Bolas

unread,
Oct 28, 2015, 9:18:41 AM10/28/15
to ISO C++ Standard - Future Proposals

I thought I'd seen something like that before.

maurice barnum

unread,
Oct 31, 2015, 2:00:11 PM10/31/15
to std-pr...@isocpp.org
On 2015-10-27 8:45 , Brent Friedman wrote:
> I'm curious as to how, exactly. A virtual call involves getting the
> vtable, followed by fetching the function pointer through an offset.
>
>
> Right - the thought behind this is that &(obj->fun) would get the vtable
> for obj and fetch the function pointer. It effectively separates the
> virtual lookup step from the virtual call step. The result of the
> expression is the concrete member function (or overload resolution set)
> that *would* be called. We don't entirely know if it's feasible, sort of
> a brainstorm at the moment.

The idea is feasible has been implemented by at least two compilers,
Borland (now Embarcadero) and GCC, but with differing syntax, see
http://preview.tinyurl.com/qd9pmjn and
http://preview.tinyurl.com/mlbtxs3, respectively.

Matthew

unread,
Nov 2, 2015, 12:41:37 AM11/2/15
to ISO C++ Standard - Future Proposals, pi...@burble.org

Is there another forum/thread/newsgroup where this discussion is ongoing, because it's hard to follow just getting snippets of the discussion.
Reply all
Reply to author
Forward
0 new messages