13

Let's say I have some arbitrary complicated overloaded function:

template <class T> void foo(T&& );
template <class T> void foo(T* );
void foo(int );

I want to know, for a given expression, which foo() gets called. For example, given some macro WHICH_OVERLOAD:

using T = WHICH_OVERLOAD(foo, 0);       // T is void(*)(int);
using U = WHICH_OVERLOAD(foo, "hello"); // U is void(*)(const char*);
// etc.

I don't know where I would use such a thing - I'm just curious if it's possible.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 3
    Note that the type is not enough for distinguish overload btw. – Jarod42 Feb 22 '16 at 18:51
  • 5
    I don't think this is possible. This is very similar to the library fundamentals TS's invocation type traits, and the latter is known to require compiler magic to implement. – T.C. Feb 22 '16 at 18:53
  • @T.C. Ah, yeah, that's what I thought. [This one](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3866.html)? – Barry Feb 22 '16 at 19:28
  • It is possible (at least in gcc), but I could not find solution again. I remember idea, it was to provide some fake construct that will fail during compilation time and print error with whole template. – VladimirS Feb 22 '16 at 20:17
  • _"I don't know where I would use such a thing - I'm just curious if it's possible."_ You should ask questions about actual, practical problems that you face. – Lightness Races in Orbit Feb 22 '16 at 20:26
  • 4
    @PreferenceBean My insatiable curiosity is an actual, practical problem that I face. – Barry Feb 22 '16 at 20:48
  • Could you elaborate, you want to know which overload was used during compile time, or you would like to use return function type in the code? – VladimirS Feb 23 '16 at 00:20
  • You could replace with typedef decltype(bind(foo, 0)) T; //T is std::bind Technically it is very close... – VladimirS Feb 23 '16 at 14:15
  • @user3545806 Did you try it? That cannot compile, since you cannot pass an overloaded function into a template. – Barry Feb 23 '16 at 14:44
  • You are right it compiles only for int... – VladimirS Feb 23 '16 at 15:56
  • It might be quite interesting for testing, if you want to make sure that you got your overloads right... – Rumburak Feb 27 '16 at 09:01
  • Instead of overloading the function what about inheriting from a fake object and then checking at run-time which is the actual object ? – Claudio Mar 18 '16 at 14:39

2 Answers2

1

Barry, sorry for the misunderstanding in my first answer. In the beginning I understood your question in a wrong way. 'T.C.' is right, that it is not possible except in some rare cases when your functions have different result types depending on the given arguments. In such cases you can even get the pointers of the functions.

#include <string>
#include <vector>
#include <iostream>

//template <class T> T foo(T ) { std::cout << "template" << std::endl; return {}; };
std::string foo(std::string) { std::cout << "string" << std::endl; return {}; };
std::vector<int> foo(std::vector<int>) { std::cout << "vector<int>" << std::endl; return {}; };
char foo(char) { std::cout << "char" << std::endl; return {}; };

template<typename T>
struct Temp
{
    using type = T (*) (T);
};

#define GET_OVERLOAD(func,param) static_cast<Temp<decltype(foo(param))>::type>(func);

int main(void)
{
    auto fPtr1 = GET_OVERLOAD(foo, 0);
    fPtr1({});

    auto fPtr2 = GET_OVERLOAD(foo, std::string{"hello"});
    fPtr2({});

    auto fPtr3 = GET_OVERLOAD(foo, std::initializer_list<char>{});
    fPtr3({});

    auto fPtr4 = GET_OVERLOAD(foo, std::vector<int>{});
    fPtr4({});

    auto fPtr5 = GET_OVERLOAD(foo, std::initializer_list<int>{});
    fPtr5({});

    return 0;
}

The output is:

char
string
string
vector<int>
vector<int>
Stamen Rakov
  • 456
  • 4
  • 10
0

I'm probably far from what you have in mind, but I've spent my time on that and it's worth to add an answer (maybe a completely wrong one, indeed):

#include<type_traits>
#include<utility>

template <class T> void foo(T&&);
template <class T> void foo(T*);
void foo(int);

template<int N>
struct choice: choice<N+1> { };

template<>
struct choice<3> { };

struct find {
    template<typename A>
    static constexpr
    auto which(A &&a) {
        return which(choice<0>{}, std::forward<A>(a));
    }

private:
    template<typename A>
    static constexpr
    auto which(choice<2>, A &&) {
        // do whatever you want
        // here you know what's the invoked function
        // it's template<typename T> void foo(T &&)
        // I'm returning its type to static_assert it
        return &static_cast<void(&)(A&&)>(foo);
    }

    template<typename A>
    static constexpr
    auto which(choice<1>, A *) {
        // do whatever you want
        // here you know what's the invoked function
        // it's template<typename T> void foo(T *)
        // I'm returning its type to static_assert it
        return &static_cast<void(&)(A*)>(foo);
    }

    template<typename A>
    static constexpr
    auto
    which(choice<0>, A a)
    -> std::enable_if_t<not std::is_same<decltype(&static_cast<void(&)(A)>(foo)), decltype(which(choice<1>{}, std::forward<A>(a)))>::value, decltype(&static_cast<void(&)(A)>(foo))>
    {
        // do whatever you want
        // here you know what's the invoked function
        // it's void foo(int)
        // I'm returning its type to static_assert it
        return &foo;
    }
};

int main() {
    float f = .42;
    static_assert(find::which(0) == &static_cast<void(&)(int)>(foo), "!");
    static_assert(find::which("hello") == &static_cast<void(&)(const char *)>(foo), "!");
    static_assert(find::which(f) == &static_cast<void(&)(float&)>(foo), "!");
    static_assert(find::which(.42) == &static_cast<void(&)(double&&)>(foo), "!");
}

I'll delete this answer after a short period during the which I expect experts to curse me. :-)

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • If you're just going to enumerate all the overloads, you don't need `choice<>`. You can just have all the overloads return the signature [demo](http://coliru.stacked-crooked.com/a/49779a83d15d0e36) – Barry Apr 24 '16 at 17:10
  • Actually, the aim is to give priorities to all the not templated functions (as `void foo(int);`), then to the templated ones. This way you are enumerating two specializations plus all the available not templated functions (not only the `int` one of your example). Surely it can be done with less code, not the one you linked anyway. – skypjack Apr 24 '16 at 17:36
  • I read a comment of yours to another answer (*it won't work if I change it in `void foo(char)`*). Because of that, I thought the goal was to get the not templated function if any exists, the right specialization otherwise. The `choice` simply helps to iterate over them. It could have been done with a sort of `int`/`char` dispatching maybe or whatever. Anyway, I hope I succeeded in explaining what the target was. – skypjack Apr 24 '16 at 17:50
  • The goal is to pick the correct overload - based on the normal overload resolution mechanism. That's the target. If you're going to just enumerate all the possible overloads - you don't need any additional dispatching, what I linked is sufficient. But enumerating the overloads can never work (e.g. what if somebody adds a `namespace N { struct X {}; void foo(X ) }` somewhere else...) – Barry Apr 24 '16 at 18:10
  • You say *I want to know, for a given expression, which foo() gets called*. In which form do you expect this information to be? Just to know, for I guess I've completely misunderstood the problem. I'm sorry. – skypjack Apr 24 '16 at 18:12