views:

243

answers:

6

I'm looking for a way in C++ to extract the return type of a function (without calling it). I presume this will require some template magic.

float Foo();
int Bar();

magic_template<Foo>::type var1; // Here 'var1' should be of type 'float'
magic_template<Bar>::type var2; // and 'var2' should be of type 'int'

I am currently investigating how magic_template might be implemented, but have not found a solution so far.

Any ideas?

+5  A: 

Take a look at boost type traits library, in particular the function_traits template provides such functionality out of the box. If you cannot use boost, just download the code and read the sources for some insight into how it is done.

Note that the functionality is based on types, not concrete functions, so you might need to add some extra code there.


After doing some small tests, this might not be what you really need, and if it is the 'some extra code' will be non-trivial. The problem is that the function_traits template works on function signatures and not actual function pointers, so the problem has changed from 'get the return type from a function pointer' to 'get the signature from a function pointer' which is probably the hardest part there.

David Rodríguez - dribeas
Function return types are covariant, an iceberg that will sink that Titanic.
Hans Passant
Hmm yes this looks look. To give an example to everyone it works like this: boost::function_traits< boost::remove_pointer<float (*)(void)>::type >::result_type var1; var1 = 3.14f;My only problem now is as you said, I need the type of the whole function in order to pass into these boost utilities.
pauldoo
@nobugz: I don't really understand your comment, can you further explain?
David Rodríguez - dribeas
Ok - I solved that problem now too. template<typename T> Monkey(T) { boost::remove_pointer<T >::result_type var1 = ...; } Monkey(Foo); Monkey(Bar);Not exactly as my original question asked, but this is good enough for me. I invoke `Monkey` as needed, and my code is able to extract the return type of the functions and do something I need.
pauldoo
(Pants, didn't notice how badly the formatting gets screwed on comments. I'll copy them into another answer.)
pauldoo
@pauldoo: I am starting to believe that it is not possible to do the last step. User code must provide the types for all class template arguments (while the compiler can use type deduction for function templates). Trying to add a level of indirection through a function template and extracting the type from the return type will leave you at step 0. If you can refactor the code as James Hopkin suggests (moving the code inside a templated function) then you could use type deduction there, but that type cannot be moved outside of the function in standard c++.
David Rodríguez - dribeas
(don't forget to upvote James Hopkins answer, as that is basically what he suggested -- for the limited set of null-ary functions, and Joe Gauterin for the practical implementation of what James suggested)
David Rodríguez - dribeas
A: 

Try something like this:

template<class T>
struct magic_template
{};

template<class T>
struct magic_template<T()>
{
    typedef T type;
};
Nicht Verstehen
+4  A: 

It's tricky because function names are expression not types - you need something like gcc's typeof. Boost's TypeOf is a portable solution that gets very close.

However, if the code can be organised so that the work is done inside a function template to which Foo or Bar can be passed, there's a straight-forward answer:

template <class R>
void test(R (*)())
{
  R var1;
}

int main()
{
  test(&Foo);
}
James Hopkin
+4  A: 

Foo and Bar are functions, not function types, so you need to do a bit of extra work.

Here's a solution using a combination of boost::function_traits and BOOST_TYPEOF.

#include <boost/typeof/typeof.hpp>
#include <boost/type_traits.hpp>

float Foo();
int Bar();

int main()
{
  boost::function_traits<BOOST_TYPEOF(Foo)>::result_type f = 5.0f;
  boost::function_traits<BOOST_TYPEOF(Bar)>::result_type i = 1;
  return i;
}

Edit:

  • This will work for functions of any arity up to 10, which should be enough for most sensible uses.
  • This use of BOOST_TYPEOF works on platforms that do not supply a native typeof, so it's reasonably portable.
Joe Gauterin
A: 

As suggested by dribeas, here is the solution I eventually came to:

float Foo();
int Bar();

template<typename T> Monkey(T) {
    boost::function_traits< boost::remove_pointer<T>::type >::result_type var1 = ...;
    // Now do something
}

Monkey(Foo);
Monkey(Bar);

This isn't exactly the form I aimed for with my original question, but it's close enough for me.

pauldoo
You need additional 'typename's in there: typename boost::function_traits< typename boost::remove_pointer<T>::type >::result_type
James Hopkin
You could lose the remove_pointer just by making the signature void Monkey(T*) [oh yes, you missed out Monkey's return type too]
James Hopkin
A: 

In C++ 0x, use decltype.

For a discussion on the problems and trying to construct a solution for an earlier C++ standard, see here:

Scott Myers "challenge" (PDF) and Andrei Alexandrescu trying to solve it

peterchen