Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine the type of a function parameter given the type of argument passed to it?

I need a type trait which will report the type of a functor's operator() parameter given the type of the functor and the type of an argument passed to it. Basically, I need to determine precisely what type the argument will be converted to when passing it to the functor. For simplicity, let's assume that I'm only interested in a (potentially templated, potentially overloaded) operator() with a single argument. Unfortunately, I'm limited to c++03. Can it be done? If not, how about c++11?

Here's one example:

#include <cassert>
#include <type_traits>

template<typename Functor, typename Argument>
  struct parameter_type
{
  // what goes here?
  typedef ... type;
};

struct takes_float_cref
{
  void operator()(const float &);
};

int main()
{
  // when calling takes_float_cref::operator() with an int,
  // i'd expect a conversion to const float &
  assert(std::is_same(parameter_type<takes_float_cref, int>::type, const float &>::value);

  return 0;
}

A related question (whose answer doesn't give me quite what I need) gives the context for needing such a trait. I've put further unit tests on ideone.

like image 285
Jared Hoberock Avatar asked Jan 19 '12 23:01

Jared Hoberock


People also ask

How can you get the type of argument passed to a function?

There are two ways to pass arguments to a function: by reference or by value. Modifying an argument that's passed by reference is reflected globally, but modifying an argument that's passed by value is reflected only inside the function.

What are the 2 types of function parameters?

Mandatory and Optional Parameters That is, when we initialise a parameter with a default value, it becomes optional. Otherwise, the parameter will be mandatory. In the above example, man1 and man2 are mandatory because they are not initialised in the function definition.

How do you specify data types in a Python function argument?

Unlike other languages Java, C++, etc. Python is a strongly-typed dynamic language in which we don't have to specify the data type of the function return value and function argument. It relates type with values instead of names.


2 Answers

I am afraid that this is not exactly possible without help from your client.

TL;DR: unit test fail (grrr gcc).

The general case of your question is this functor:

struct Functor {
  template <typename T>
  typename std::enable_if<std::is_integral<T>::value>::type
  operator()(T t) const;

  void operator(double d) const;
};

It combines the two main issues here:

  1. If there is an overload, then taking &F::operator() requires a static_cast to a given type to disambiguate which overload should be used
  2. Templates (and arbitrary conditions to express them) cannot be succintly expressed as typedefs

Therefore, the client (Functor here) need to provide additional hooks for you if you truly wish to get this type. And without decltype I don't see how to get it (note, gcc provides typeof as an extension in C++03).

Getting the client to give us hints:

// 1. Make use of the return value:
struct Functor {
  template <typename T>
  typename std::enable_if<std::is_integral<T>::value, T>::type
  operator()(T t) const;

  double operator(double d) const;
};

// 2. Double up the work (but leave the return value as is)
struct Functor {
  template <typename T>
  static typename std::enable_if<std::is_integral<T>::value, T>::type Select(T);

  static double Select(T);

  template <typename T>
  typename std::enable_if<std::is_integral<T>::value>::type
  operator()(T t) const;

  void operator(double d) const;
};

Let's say we go for the second case (leaving the return value free for another use).

template <typename F, typename T>
struct parameter {
  static T t;
  typedef decltype(F::Select(t)) type;
};

In C++03, replace decltype by typeof with gcc.

I don't see a way to forego decltype. sizeof does provides an unevaluated context but it does not seem to help much here.

Unit Tests Here.

Unfortunately, there is a gcc bug it seems with the references, and float& gets reduced to float (and any other reference really), the bug remains with decltype so it's just a buggy implementation :/ Clang 3.0 has no problem with the C++11 version (decltype) but does not implement typeof I think.

This can be worked around by requiring the client to use a ref<float> class instead, and then unwrapping it. Just a bit more burden...

like image 193
Matthieu M. Avatar answered Oct 13 '22 12:10

Matthieu M.


To get started I would go with this:

template<typename F>
struct parameter_type_impl;

// may be with variadic arguments
template<typename R, typename A, typename F>
struct parameter_type_impl<R (F::*)(A)> {
  typedef A type;
};

template<typename F>
struct parameter_type {
  typedef typename parameter_type_impl<decltype(&F::operator())>::type type;
};

I don't see why you would pass in the actual argument type. If the conversion is not able to take place you have to use special measures (e.g. SFINAE) later on. I think the two things are orthogonal: deducing the argument type, then deciding if the argument you would like to pass in is convertible.

The non-C++03 decltype is hard to get rid of. Specifying a function type always requires knowledge of the arguments. As soon as you would spell out the arguments, the whole thing would be moot.

The same problem would occur with Boost.Function Types.

like image 21
pmr Avatar answered Oct 13 '22 10:10

pmr