How can I acquire the basic function type from an std::function?
-
27-06-2021 - |
Question
I have a lot of classes with a typedef of std::function<void(PARAMS)>
where PARAMS is specific to each class. I need to specialize based on the number of parameters and the first parameter's type. I'd like to use boost::function_traits
for this, but in order to use it, I need to have the raw function type of the std::function
in question.
For example, given std::function<void(int,int)>
, I want to retrieve void(int,int)
.
Is there any way to extract the native type in a portable manner? As a side note, I do not have access to C++11 features.
Solution
To get the function type, you can use partial specialization:
template <typename T>
struct Detect;
template <typename F>
struct Detect<std::function<F>> {
typedef F Result;
};
Now when you get an unknown std::function<?>
type T
, you just use
typename Detect<T>::Result
(you might want do define Result
as F *
, as some contexts (e.g., field types) only allow pointer-to-function, and not bare function types.
Edit:
To specialize on the number of arguments and the type of the first one, you would need either C++11 variadic templates
template <typename T>
struct Detect;
template <typename R, typename A, typename... As>
struct Detect<std::function<R(A,As...)>> {
static constexpr auto numargs = 1 + sizeof...(As);
typedef R Result;
typedef A FirstArg;
};
or to code the equivalent of the above, using a separate specialization for each possible number of arguments:
template <typename R, typename A1>
struct Detect<std::function<R(A1)>> {
enum { numargs = 1 };
typedef R Result;
typedef A1 FirstArg;
};
template <typename R, typename A1, typename A2>
struct Detect<std::function<R(A1,A2)>> {
enum { numargs = 2 };
...
};
...
OTHER TIPS
std::function
contains result_type
, argument_type
for unary functions, and first_argument_type
and second_argument_type
for binary functions. You can extract these. For n-ary
functions defined with variadic templates, I don't think there is a std::tuple
containing all the arguments.
If you want your own traits class:
template<typename Fun>
struct function_traits;
template<typename R, typename... Args>
struct function_traits<std::function<R(Args...)>
{
typedef R return_type;
typedef std::tuple<Args...> arguments_type;
};
It should be simple enough to create a metafunction to extract the T
in the boost::function<T>
template<typename T>
struct func_extractor
template<typename T>
struct func_extractor<boost::function<T> >
{
typedef T type;
};
int main()
{
typedef boost::function<void(int, int)> func_type1;
typedef func_extractor<func_type1>::type extracted_type;
typedef boost::function<extracted_type> func_type2;
std::cout << boost::is_same<func_type1, func_type2>::value << std::endl;
}