11

I would like to know if there is any way to obtain the address of the instantiation of a function template produced by a specific set of arguments.

#include <iostream>

template <typename T>
class A {}; 

template <typename T, typename T2>
int hello(A<T> a, A<T2> b, int c)
{
    return 69; 
}

int main()
{
    A<int> a;
    A<float> b;
    std::cout << (&hello)(a, b, 3) << "\n";                                                                                                                                                           

    return 0;
}

This code prints the value returned by the function call. How can I print the address of the version of "hello" instantiated for a and b parameters? I would like the types to be inferred by the compiler.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • @Borgleader: That's not quite right, the template parameter to `hello` would be the `int`, but there's no way to get `int` from `a` with the code as-is. – Mooing Duck Apr 08 '15 at 18:46
  • This is equivalent to "deducing which overload a set of parameters would bind to", which is, quite sadly, impossible in C++14. (I hear rumors they're adding something to C++17 for this) – Mooing Duck Apr 08 '15 at 18:49
  • @MooingDuck What if a typedef was added to A so that a::type was int? –  Apr 08 '15 at 18:49
  • @Lalaland: I assumed we couldn't make use of the fact that we knew `a` to be an `A`. If we _can_ assume that, then yes, that typedef would make it possible. – Mooing Duck Apr 08 '15 at 18:50
  • @MooingDuck why would it be *"impossible"*? I can think of a few ways to get it working (some nicer than others). – Filip Roséen - refp Apr 08 '15 at 18:52
  • 2
    @Lalaland: parameters could be anything, not necessarily a class or class template. – Javier Cabezas Rodríguez Apr 08 '15 at 18:53
  • 1
    @FilipRoséen-refp: There are ways to get the _return type_, but to get the address, you need to know the parameters _after_ implicit conversions, and there is no way to do this in C++. (If you find a counterexample, that would be groundbreaking, and I would be thrilled to see it) (Related, I'm thinking about [the general case](http://coliru.stacked-crooked.com/a/2f58e4fd99be5f55). In _this_ case it _might_ be possible because there's only one template function, with no overloads, or conversions) – Mooing Duck Apr 08 '15 at 18:59
  • 1
    @MooingDuck Not even C++17. We do get invocation type traits in the library fundamentals TS, but they only work on function objects, not generic overload sets. – T.C. Apr 08 '15 at 19:01
  • @JavierCabezasRodríguez: Is [this](http://coliru.stacked-crooked.com/a/a5012043f29f790c) a clearer example of what you're trying to accomplish? – Mooing Duck Apr 08 '15 at 19:06
  • @MooingDuck I don't see what you find "impossible", getting the address of a template function instantiation is definitely possible... – Synxis Apr 08 '15 at 19:08
  • @Synxis see the added snippet that he just added/linked. – Filip Roséen - refp Apr 08 '15 at 19:10
  • @Synxis: Can you fill out the `get_foo_ptr` function for me then? http://coliru.stacked-crooked.com/a/a5012043f29f790c You can get the address of a template function instantiation just fine. Easy. As you show in your example. What you cannot do is get the address of a function that would be called after overload resolution. If he's asking for the prior, then it's doable, and your answer is correct. If he's asking for the latter, it's not doable, and hvd's answer is correct. As it is, the question is too vague to be certain. – Mooing Duck Apr 08 '15 at 19:11
  • @MooingDuck ah yeah, I was thinking of the simple example provided by *OP* (which is solvable with relative ease) - thanks for the elaboration. – Filip Roséen - refp Apr 08 '15 at 19:11
  • Since the OP said he "would like the types to be inferred by the compiler" (in a deleted comment), I inferred the latter. @JavierCabezasRodríguez: Can you clarify the question as to what we can and can't assume about the parameters when trying to get the pointer? – Mooing Duck Apr 08 '15 at 19:13
  • Ah I see. I really didn't read the question like you did, gonna see it deeper now. – Synxis Apr 08 '15 at 19:14
  • Finally deleted my answer, as it was a bit too narrow. In the general case, getting the address of function with inferred argument types is not possible, even in the case of template function (because first not all parameters are templated, and second because you can use 'advanced' pattern matching with templates). Can you add an example of what you're trying to achieve ? – Synxis Apr 08 '15 at 21:04
  • @MooingDuck See [this blog post](http://b.atch.se/posts/non-constant-constant-expressions/), using the technique a solution should be possible to implement. – Filip Roséen - refp Apr 26 '15 at 17:45

3 Answers3

9

The process of determining the function to call based on the arguments is called overload resolution, and the standard lists in which cases it gets used:

13.3 Overload resolution [over.match]

2 Overload resolution selects the function to call in seven distinct contexts within the language:

(2.1) -- invocation of a function named in the function call syntax (13.3.1.1.1);

(2.2) -- invocation of a function call operator, a pointer-to-function conversion function, a reference-to-pointer-to-function conversion function, or a reference-to-function conversion function on a class object named in the function call syntax (13.3.1.1.2);

(2.3) -- invocation of the operator referenced in an expression (13.3.1.2);

(2.4) -- invocation of a constructor for direct-initialization (8.5) of a class object (13.3.1.3);

(2.5) -- invocation of a user-defined conversion for copy-initialization (8.5) of a class object (13.3.1.4);

(2.6) -- invocation of a conversion function for initialization of an object of a nonclass type from an expression of class type (13.3.1.5); and

(2.7) -- invocation of a conversion function for conversion to a glvalue or class prvalue to which a reference (8.5.3) will be directly bound (13.3.1.6).

Of these, the only one that applies to regular functions is 2.1, and that requires a f(args) context which only tells the caller the result.

So, what you ask for cannot be done. Not exactly, anyway.

Now, depending on what you want to accomplish, there are some things that are possible:

It is possible to get a pointer to the function if you know the exact signature: given template <typename T> int hello(A<T> a, A<T> b), you can obtain the address using that: static_cast<int(*)(A<int>,A<int>)>(hello). However, for this to work, you need to know the return type (which you might be able to obtain using decltype), and you need to know the parameter types (which may be different from the argument types, and which you aren't able to obtain reliably).

It is also possible to get a pointer to a function that, when called, will have the same effect as hello:

auto callable = +[](A<int> a, A<int> b) { return hello(a, b); };

The [](A<int> a, A<int> b) { return hello(a, b); } creates a lambda without any captures, and lambdas without any captures can implicitly be converted to a function pointer of a matching type. The + forces the use of that conversion, without requiring the type to be spelled out.

However, this will not have the same address as hello, so might not be suitable for subsequent comparisons.

That's the best you can get.

  • 1
    Might want to explain that `+`. It's a bit obscure. – T.C. Apr 08 '15 at 18:58
  • The pointer to lambda does not have the same effect as hello if copying `A` has any side effects. If you move, then if the move ctor has side effects (or if `hello` treats rvalues differently) it won't have the same effect. If `hello` takes by-reference, any changes done to the objects won't be propagated out in the lambda solution. All of these basically require inspecting the implementation of the function template `hello` and its signature to the level that you might as well do it all manually, no? – Yakk - Adam Nevraumont Apr 08 '15 at 20:22
  • @Yakk You know how you want to call `hello`, so you can update the lambda to match, without having to know the implementation details of `hello`. If you know you'll want to pass lvalues, write `auto callable = +[](A &a, A &b) { return hello(a, b); };`. It'll work even if `hello` takes by value. Similarly rvalues, except for an added `std::move`: that too will work even if `hello` takes by value. But you do raise a valid point: there are some situations in which copies or moves of the parameters have a visible effect and in which that cannot be avoided. –  Apr 08 '15 at 20:32
2

I tried this in Ideone:

// A and hello defined as OP
int main()
{
    A<int> a;
    A<float> b;

    decltype(hello(a,b,3)) (*pf)(decltype(a), decltype(b), decltype(3))=hello;
    std::cout << (void*)pf << "\n";                                                                                                                                                           

    return 0;
}

It seemed to output a memory address.

quamrana
  • 37,849
  • 12
  • 53
  • 71
1

@hvd: AFAIK, you cannot declare a lambda without knowing the signature of the function you wanna wrap (lambdas cannot be templated). But you can use an intermediate class with a static method instead:

#include <functional>
#include <iostream>

template<class A>
void func(A a, int b)
{
    std::cout << "a=" << a << " b=" << b << "\n";
}

template<class... A>
class proxy_func
{
public:
    static auto call(A... args) -> decltype(func(args...))
        {
            return func(args...);
        }
};

template<template<class...> class P, class... User>
void* addr(User... user)
{
    return (void*)&P<User...>::call;
}

template<template<class...> class P, class... User>
auto call(User... user) -> decltype(P<User...>::call(user...))
{
    return P<User...>::call(user...);
}

template<class T>
void test()
{
    T value = 1;
    printf("func > %p\n", &func<T>);
    printf("func > ");
    func(value, 1);
    printf("proxy> %p\n", &proxy_func<T, int>::call);
    printf("proxy> ");
    proxy_func<T, int>::call(value, 1);
    printf("auto > %p\n", addr<proxy_func>(value, 1));
    printf("auto > ");
    call<proxy_func>(value, 1);
}

int main(int argc, char **argv)
{
    printf("==int==\n");
    test<int>();
    printf("==long==\n");
    test<long>();
}

The result is this:

g++ -std=c++11 -o /tmp/test /tmp/test.cpp && /tmp/test
==int==
func > 0x400a8d
func > a=1 b=1
proxy> 0x400ae6
proxy> a=1 b=1
auto > 0x400ae6
auto > a=1 b=1
==long==
func > 0x400b35
func > a=1 b=1
proxy> 0x400b91
proxy> a=1 b=1
auto > 0x400b91
auto > a=1 b=1

Of course, this requires declaring a generic proxy that knows about the name of the target function (nothing else), and that might not be acceptable as a solution for @JavierCabezasRodríguez.

PS: Sorry I did not post it as a comment, but I do not have enough reputation for it.

Edit

Using a proxy class instead of a lambda forgoes the need to know the number of parameters, so that you can use a hackishly macro approach to wrap any target function:

#define proxy(f)                                \
    template<class... A>                        \
    class proxy_ ## f                           \
    {                                           \
    public:                                                     \
        static auto call(A... args) -> decltype(f(args...))     \
        {                                                       \
            return f(args...);                                  \
        }                                                       \
    }

proxy(func);
Lluís Vilanova
  • 849
  • 8
  • 9
  • Welcome to SO. It can take just a single good answer to gain enough reputation to comment, and I encourage you to do so if this is the level of comments you'd like to leave, this is useful. :) Lambdas can be templated in C++14 (look up `auto` parameters), but that wouldn't help, a templated lambda can be converted to multiple function pointer types, so the trick of using `+` to force the single possible conversion doesn't work. But... –  Apr 09 '15 at 17:20
  • ...you don't need templated lambdas, and you don't need the lambdas to match exactly to the function being called, you merely need to match the arguments you want to pass. The lambda body can take care of any implicit conversions from the argument types to the wrapped function's parameter types. For instance, `void f(long l) { } int main() { auto x = +[](int a) { f(a); }; }` is fine, and gives `x` type `void(*)(int)`. It's okay that it doesn't exactly match the type of `&f`. –  Apr 09 '15 at 17:22
  • Right, I was thinking about avoiding the declaration of a lambda with a specific number of arguments. See edit on my response. – Lluís Vilanova Apr 09 '15 at 18:51