Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the signature of a C++ bind(...) expression

I am trying to write a metafunction named signature_of which, given the type of a function (pointer), functor, or lambda, returns its signature.

Here's what I have so far:

#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/function_types/is_member_function_pointer.hpp>
#include <boost/function_types/function_type.hpp>
#include <boost/function_types/result_type.hpp>
#include <boost/function_types/parameter_types.hpp>

#include <type_traits>

template <typename F>
struct signature_of_member
{
    typedef typename boost::function_types::result_type<F>::type result_type;
    typedef typename boost::function_types::parameter_types<F>::type parameter_types;
    typedef typename boost::mpl::pop_front<parameter_types>::type base;
    typedef typename boost::mpl::push_front<base, result_type>::type L;
    typedef typename boost::function_types::function_type<L>::type type;
};

template <typename F, bool is_class>
struct signature_of_impl
{
    typedef typename boost::function_types::function_type<F>::type type;
};

template <typename F>
struct signature_of_impl<F, true>
{
    typedef typename signature_of_member<decltype(&F::operator())>::type type;
};

template <typename F>
struct signature_of
{
    typedef typename signature_of_impl<F, std::is_class<F>::value>::type type;
};

It's pretty straightforward, with most of the real work being done by the boost::function_types library. The general idea is:

  • use std::is_class to discriminate between built-in functions (including lambdas) and functors
  • for built-in function types, use boost::function_types::function_type to get its signature
  • for functors, get the type of their operator(), get its signature, and doctor it to remove the "this" parameter

This works for built-in functions:

int f(int);
typedef signature_of<decltype(f)>::type Sig;  // Sig is int(int)

for lambdas:

auto f = [](int) { return 0; }
typedef signature_of<decltype(f)>::type Sig;  // Sig is int(int)

and for functors:

struct A
{
    int operator()(int);
};
typedef signature_of<A>::type Sig;  // Sig is int(int)

However, it doesn't work for bind() expressions (which are a special case of functors). If I try this:

#include <functional>
int g(int);
typedef signature_of<decltype(std::bind(g, 0))>::type Sig;

I get a compiler error:

In file included from test.cpp:3:0:
signature_of.hpp: In instantiation of 'signature_of_impl<
        _Bind<int (*(int))(int)>, true
    >':
signature_of.hpp:45:74:   instantiated from 'signature_of<
        _Bind<int (*(int))(int)>
    >'
test.cpp:21:52:   instantiated from here
signature_of.hpp:39:74: error: type of '& _Bind<
        int (*)(int)({int} ...)
    >::operator()' is unknown

The problem is that the operator() of the functor returned by bind() is a template, and so its type cannot be determined.

Is it possible to get the signature of a bind() expression another way?

like image 998
HighCommander4 Avatar asked Jan 22 '11 23:01

HighCommander4


People also ask

What does std :: bind do in C++?

std::bind is for partial function application. That is, suppose you have a function object f which takes 3 arguments: f(a,b,c); You want a new function object which only takes two arguments, defined as: g(a,b) := f(a, 4, b);

How does std:: bind works?

Internally, std::bind() detects that a pointer to a member function is passed and most likely turns it into a callable objects, e.g., by use std::mem_fn() with its first argument.

What is boost :: bind?

boost::bind is a generalization of the standard functions std::bind1st and std::bind2nd. It supports arbitrary function objects, functions, function pointers, and member function pointers, and is able to bind any argument to a specific value or route input arguments into arbitrary positions.

What is STD invoke?

The std::invoke() method can be used to call functions, function pointers, and member pointers with the same syntax: #include <iostream>#include <functional>using namespace std;void globalFunction( ) { cout << "globalFunction ..." <<


2 Answers

You've got more problems than the fact that a binder's operator() is templated, it also has an arbitrary count of parameters. Remember that you're supposed to be able to invoke the result of bind with any number of extra arguments. For example:

int f(int);

auto bound = boost::bind(f, _2);

bound may now be called with any number of arguments of 2 or more, only the second is actually forwarded on to the function.

So basically, as another answer said, this object has no signature. It's signature is defined only by how it is used.

like image 114
Edward Strange Avatar answered Nov 07 '22 10:11

Edward Strange


If the operator is templated, then it doesn't have a signature.

like image 35
Puppy Avatar answered Nov 07 '22 12:11

Puppy