Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

overload resolution and function pointer

125 views
Skip to first unread message

Marc

unread,
May 11, 2012, 3:21:54 PM5/11/12
to
Hello,

I have several overloads of a function, I have a set of arguments, and
I would like to get a function pointer to the overload the compiler
would pick if I called this function on these arguments. I am not
interested in applying the function now, I just want to get that
pointer.

Is this possible? Or is anything vaguely similar possible?


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Johannes Schaub

unread,
May 11, 2012, 5:25:12 PM5/11/12
to
Am 11.05.2012 21:21, schrieb Marc:
> Hello,
>
> I have several overloads of a function, I have a set of arguments,
> and I would like to get a function pointer to the overload the
> compiler would pick if I called this function on these arguments. I
> am not interested in applying the function now, I just want to get
> that pointer.
>
> Is this possible? Or is anything vaguely similar possible?
>
>

"Something similar" is possible:
http://stackoverflow.com/a/10398323/34509

However it does not take a set of arguments, but a set of parameters
of the function you are interested in. Summary:

-----------------------
template<typename T>
struct identity { typedef T type; };

template<typename T>
using NoDeduce = typename identity<T>::type;

template<typename T>
using Identity = T;

template<typename ...P, typename R>
Identity<R(P...)> *get_f(R f(NoDeduce<P>...)) {
return f;
}

// R f(int, int, int);
auto *f1 = get_f<int, int, int>(f);

// R f(int, int);
auto *f2 = get_f<int, int>(f);

--------------------

If you have a set of (returntype, parameters) pairs and a single
argument tuple and wanna see what parameter tuple the compiler would
take (perhaps for teaching purposes), you could produce a set of
surrogate call candidates like this

template<typename T>
struct identity { typedef T type; };

template<typename T>
using Identity = typename identity<T>::type;

template<typename ...P>
struct F { };

template<typename R, typename ...P1, typename ...P>
struct F<R(P1...), P...> : F<P...> {
operator Identity<identity<R(P1...)>(P1...)>*() const {
throw "You shall not call me";
}
};

F<void(int&), void(int&&)> f;
static_assert(
is_same<decltype(f(10))::type, void(int&&)>::value,
"something gone wrong");

With a bit more effort, you could use real member functions and make
them return a function pointer of your choice.

Hope it helps.

Pete Becker

unread,
May 11, 2012, 9:30:57 PM5/11/12
to
On 2012-05-11 19:21:54 +0000, Marc said:

> Hello,
>
> I have several overloads of a function, I have a set of arguments, and
> I would like to get a function pointer to the overload the compiler
> would pick if I called this function on these arguments. I am not
> interested in applying the function now, I just want to get that
> pointer.
>
> Is this possible? Or is anything vaguely similar possible?

Use a cast to select from a set of overloaded functions:

void f(int);
void f(double);

typedef void (*f_of_double)(double);

f_of_double fptr = (f_of_double)&f;

The typedef isn't actually needed; I used it here to clarify what's
going on. Yes, the cast looks wierd, but that's the rule.

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Seungbeom Kim

unread,
May 12, 2012, 6:10:03 AM5/12/12
to
On 2012-05-11 18:30, Pete Becker wrote:
> On 2012-05-11 19:21:54 +0000, Marc said:
>
>> I have several overloads of a function, I have a set of arguments, and
>> I would like to get a function pointer to the overload the compiler
>> would pick if I called this function on these arguments. I am not
>> interested in applying the function now, I just want to get that
>> pointer.
>>
>> Is this possible? Or is anything vaguely similar possible?
>
> Use a cast to select from a set of overloaded functions:
>
> void f(int);
> void f(double);
>
> typedef void (*f_of_double)(double);
>
> f_of_double fptr = (f_of_double)&f;
>
> The typedef isn't actually needed; I used it here to clarify what's
> going on. Yes, the cast looks wierd, but that's the rule.

You don't actually need the cast either, do you?

void (*f_int)(int) = &f; // points to f(int)
void (*f_double)(double) = &f; // points to f(double)

--
Seungbeom Kim

Marc

unread,
May 12, 2012, 2:05:23 PM5/12/12
to
Johannes Schaub wrote:

> Am 11.05.2012 21:21, schrieb Marc:
>> Hello,
>>
>> I have several overloads of a function, I have a set of arguments,
>> and I would like to get a function pointer to the overload the
>> compiler would pick if I called this function on these arguments. I
>> am not interested in applying the function now, I just want to get
>> that pointer.
>>
>> Is this possible? Or is anything vaguely similar possible?
>
> "Something similar" is possible:
> http://stackoverflow.com/a/10398323/34509

Hello,

thanks for answering,

> However it does not take a set of arguments, but a set of parameters
> of the function you are interested in. Summary:
>
> -----------------------
> template<typename T>
> struct identity { typedef T type; };
>
> template<typename T>
> using NoDeduce = typename identity<T>::type;
>
> template<typename T>
> using Identity = T;
>
> template<typename ...P, typename R>
> Identity<R(P...)> *get_f(R f(NoDeduce<P>...)) {
> return f;
> }
>
> // R f(int, int, int);
> auto *f1 = get_f<int, int, int>(f);
>
> // R f(int, int);
> auto *f2 = get_f<int, int>(f);

(I couldn't get either g++ or clang++, latest snapshots for both, to
accept this code, as soon as f is overloaded)

This doesn't do overload resolution, it requires me to already know
the prototype of the right function (minus the return type, but that's
easy to deduce). If f takes a long and a double, I'd want
get_f<int,int> to still find it, if there is no better overload.

> --------------------
>
> If you have a set of (returntype, parameters) pairs and a single
> argument tuple and wanna see what parameter tuple the compiler would
> take (perhaps for teaching purposes), you could produce a set of
> surrogate call candidates like this
>
> template<typename T>
> struct identity { typedef T type; };
>
> template<typename T>
> using Identity = typename identity<T>::type;
>
> template<typename ...P>
> struct F { };
>
> template<typename R, typename ...P1, typename ...P>
> struct F<R(P1...), P...> : F<P...> {
> operator Identity<identity<R(P1...)>(P1...)>*() const {
> throw "You shall not call me";
> }
> };
>
> F<void(int&), void(int&&)> f;
> static_assert(
> is_same<decltype(f(10))::type, void(int&&)>::value,
> "something gone wrong");
>
> With a bit more effort, you could use real member functions and make
> them return a function pointer of your choice.

And for this one I need to already know all the prototypes of the
overloads.

Pete Becker

unread,
May 12, 2012, 2:05:28 PM5/12/12
to
On 2012-05-12 10:10:03 +0000, Seungbeom Kim said:

> On 2012-05-11 18:30, Pete Becker wrote:
>> On 2012-05-11 19:21:54 +0000, Marc said:
>>
>>> I have several overloads of a function, I have a set of arguments, and
>>> I would like to get a function pointer to the overload the compiler
>>> would pick if I called this function on these arguments. I am not
>>> interested in applying the function now, I just want to get that
>>> pointer.
>>>
>>> Is this possible? Or is anything vaguely similar possible?
>>
>> Use a cast to select from a set of overloaded functions:
>>
>> void f(int);
>> void f(double);
>>
>> typedef void (*f_of_double)(double);
>>
>> f_of_double fptr = (f_of_double)&f;
>>
>> The typedef isn't actually needed; I used it here to clarify what's
>> going on. Yes, the cast looks wierd, but that's the rule.
>
> You don't actually need the cast either, do you?
>
> void (*f_int)(int) = &f; // points to f(int)
> void (*f_double)(double) = &f; // points to f(double)

Good point. No, you don't need it if the left-hand side determines the
type. You do need it when it doesn't:

template <class T> void call_it(T);

call_it(&f); // ambiguous
call_it((f_of_double)&f); // OK

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)


Wil Evers

unread,
May 13, 2012, 12:42:02 AM5/13/12
to
Pete Becker wrote:

> On 2012-05-11 19:21:54 +0000, Marc said:
>
>> Hello,
>>
>> I have several overloads of a function, I have a set of arguments,
>> and I would like to get a function pointer to the overload the
>> compiler would pick if I called this function on these arguments. I
>> am not interested in applying the function now, I just want to get
>> that pointer.
>>
>> Is this possible? Or is anything vaguely similar possible?
>
> Use a cast to select from a set of overloaded functions:
>
> void f(int);
> void f(double);
>
> typedef void (*f_of_double)(double);
>
> f_of_double fptr = (f_of_double)&f;
>
> The typedef isn't actually needed; I used it here to clarify what's
> going on. Yes, the cast looks wierd, but that's the rule.

I don't think that cast is required. This should also work:

void f(int);
void f(double);

typedef void (*f_of_double)(double);

f_of_double fptr = &f;

Unnecessary casts, especially C-style casts, should be avoided:
they tell the compiler to be silent about potentially dangerous
conversions.

- Wil


--

Johannes Schaub

unread,
May 13, 2012, 2:57:21 PM5/13/12
to
This, to me innocently looking code, appears to be in fact ill-formed
when "f" is overloaded.

The reason that is now apparent to me is that "R f(NoDeduce<P>...)"
will, during argument deduction, look like "R f(int, int,
NoDeduce<P>...)". I initially thought that the non-deducibility of the
trailing parameters of the 3-param function pointer eliminates that
pointer from consideration and leaves only the 2-param pointer that
would finally selected.

However, what I think happens is that the trailing parameters just
count as "matched" (that's always the case when you have a non-deduced
context! As the name says, they are non-deduced and hence there is no
deduction that can yield a mismatch). So we will have both candidates
match the "R(int, int, non-deduced-seq)", and consequently have the
whole function parameter of the template (including the "R"!) be a
non-deduced context, and hence "R" cannot be deduced anymore.

Unfortunately I don't know a way out of this. If you make it a deduced
context (making it "R f(T...)") both will match too because the T will
just continue matching the trailing parameters.

A workaround is to introduce an earlier level of expansion, so that
"T" is fully expanded during the time R is deduced:

template<typename ...T> struct E {
template<typename R>
static void f(R g(T...)) { }
};

void a(int, int) { }
void a(int) { }

int main() { E<int>::f(a); }

Hope it helps to see why it failed.

Wil Evers

unread,
May 13, 2012, 8:17:52 PM5/13/12
to
Pete Becker wrote:

> On 2012-05-12 10:10:03 +0000, Seungbeom Kim said:
>
>> On 2012-05-11 18:30, Pete Becker wrote:
>>
>>> Use a cast to select from a set of overloaded functions:
>>>
>>> void f(int);
>>> void f(double);
>>>
>>> typedef void (*f_of_double)(double);
>>>
>>> f_of_double fptr = (f_of_double)&f;
>>>
>>> The typedef isn't actually needed; I used it here to clarify
>>> what's going on. Yes, the cast looks wierd, but that's the rule.
>>
>> You don't actually need the cast either, do you?
>>
>> void (*f_int)(int) = &f; // points to f(int)
>> void (*f_double)(double) = &f; // points to f(double)
>
> Good point. No, you don't need it if the left-hand side determines
> the type. You do need it when it doesn't:
>
> template <class T> void call_it(T);
>
> call_it(&f); // ambiguous
> call_it((f_of_double)&f); // OK

You don't need a cast here either:

#include <iostream>

void f(int) { std::cout << "f(int)" << std::endl; }
void f(double) { std::cout << "f(double)" << std::endl; }

typedef void (*f_of_double)(double);

template <typename T>
void call_it(T t)
{
t(42);
}

int main()
{
// call_it(&f); // ambiguous
call_it<f_of_double>(&f); // no cast here
return 0;
}

The reason I'm going on about this is that I regularly see experts
recommending the use of a cast to disambiguate between overloaded
function names, while, for good reasons, casts are otherwise frowned
upon. IMO, the use of a cast should never be recommended, unless
there really is no alternative.

- Wil


--

Daniel Krügler

unread,
May 14, 2012, 3:12:30 PM5/14/12
to
Am 11.05.2012 21:21, schrieb Marc:
> Hello,
>
> I have several overloads of a function, I have a set of arguments, and
> I would like to get a function pointer to the overload the compiler
> would pick if I called this function on these arguments. I am not
> interested in applying the function now, I just want to get that
> pointer.
>
> Is this possible? Or is anything vaguely similar possible?

I don't know how to realize your exact need, but consider the following
as an alternative:

// Examples:
void f(int){}
void f(double){}
void f(int, bool){}

#include <utility>

template<class... Args>
struct f_caller
{
auto operator()(Args&&... args) const ->
decltype(f(std::forward<Args>(args)...))
{
return f(std::forward<Args>(args)...);
}
};

If we consider this template as a kind of "holder" for some particular
function overload f, what kind of property is missing for your use-case?

Here an example of usage:

template<class... Args>
auto test(Args&&... args) -> f_caller<Args...>
{
return f_caller<Args...>();
}

int main(){
test(0, true);
test(1.2);
test(2);
auto f2 = test(0, 0);
f2(0, 0);
}

There is a way of considering f2 as a function-pointer like storage of
some unknown f. We could proceed and reduce f_caller to a functor that
can be called without any arguments when we redefine it such that it
also binds the original arguments. Since I don't know what your actual
intention is, I leave that demonstration here - it might not what you
want to realize.

HTH & Greetings from Bremen,

Daniel Krügler

Marc

unread,
May 14, 2012, 6:30:16 PM5/14/12
to
That's a clever technique indeed. Not what I wanted for this question,
but I might find other uses for it :-)

> Since I don't know what your actual intention is, I leave that demonstration
> here - it might not what you want to realize.

The origin of this question is a discussion on the libstdc++ mailing
list about implementing the standard algorithms that use operator< in
terms of those that take a functor parameter (instead of duplicating
the code as is the case now). Perfect forwarding is not perfect, among
other things because it prevents copy elision, so passing the
algorithm a wrapper around operator< can make a difference. On the
other hand, if I could pass the algorithm a pointer to the appropriate
overload of operator<, the functor version could be equivalent to the
special-case version with operator<.

Then, because I am into theory, I wondered whether it was actually
possible to get the right pointer, and that question became more
interesting to me than the original issue.
0 new messages