Пытаясь решить эту проблему, я нашел следующее частичное решение:
#include <cstdlib>
#include <functional>
#include <iostream>
#include <typeinfo>
#include <boost/bind.hpp>
#include <boost/function.hpp>
template< typename T >
struct identity
{
typedef T type;
};
// ----------
// Function signature metafunction implementation
// Also handler for function object case
// ----------
template< typename T >
struct function_signature_impl
: function_signature_impl< decltype( &T::operator() ) >
{
};
// ----------
// Function signature specializations
// ----------
template< typename R >
struct function_signature_impl< R () >
: identity< R () >
{
};
template< typename R, typename A1 >
struct function_signature_impl< R ( A1 ) >
: identity< R ( A1 ) >
{
};
template< typename R, typename A1, typename A2 >
struct function_signature_impl< R ( A1, A2 ) >
: identity< R ( A1, A2 ) >
{
};
// ----------
// Function pointer specializations
// ----------
template< typename R >
struct function_signature_impl< R ( * )() >
: function_signature_impl< R () >
{
};
template< typename R, typename A1 >
struct function_signature_impl< R ( * )( A1 ) >
: function_signature_impl< R ( A1 ) >
{
};
// ----------
// Member function pointer specializations
// ----------
template< typename C, typename R >
struct function_signature_impl< R ( C::* )() >
: function_signature_impl< R () >
{
};
template< typename C, typename R, typename A1 >
struct function_signature_impl< R ( C::* )( A1 ) >
: function_signature_impl< R ( A1 ) >
{
};
template< typename C, typename R >
struct function_signature_impl< R ( C::* )() const >
: function_signature_impl< R () >
{
};
template< typename C, typename R, typename A1 >
struct function_signature_impl< R ( C::* )( A1 ) const >
: function_signature_impl< R ( A1 ) >
{
};
// ----------
// Function signature metafunction
// ----------
template< typename T >
struct function_signature
: function_signature_impl< T >
{
};
// ----------
// Tests
// ----------
template< typename F >
void test( F f )
{
typedef function_signature< F >::type signature_type;
std::cout << typeid( F ).name() << std::endl;
std::cout << '\t' << typeid( signature_type ).name() << std::endl;
std::cout << std::endl;
}
int foo( int )
{
return 0;
}
struct bar
{
int operator ()( int )
{
return 0;
}
};
struct cbar
{
int operator ()( int ) const
{
return 0;
}
};
struct abar1
{
int operator ()( int ) const
{
return 0;
}
int operator ()( int )
{
return 0;
}
};
struct abar2
{
int operator ()( int )
{
return 0;
}
int operator ()( double )
{
return 0;
}
};
struct mem
{
int f( int ) const
{
return 0;
}
};
int main()
{
test(
[]( int ) -> int { return 0; }
);
test(
foo
);
test(
&foo
);
test(
bar()
);
test(
cbar()
);
test(
std::function< int ( int ) >( &foo )
);
test(
boost::function< void ( int ) >( &foo )
);
/*
test(
std::bind( &mem::f, mem(), std::placeholders::_1 )
);
*/
/*
test(
boost::bind( &mem::f, mem(), _1 )
);
*/
/*
test(
abar1()
);
*/
/*
test(
abar2()
);
*/
return EXIT_SUCCESS;
}
(код для проверки аргументов inproper не добавлен.)
Идея состоит в том, что function_signature< decltype( f ) >::type
должна быть подпись вызова f( ... )
, где это "..." является подписью.В частности, это означает, что указатель на функцию-член здесь является недопустимым аргументом (хотя код не проверяет это), поскольку такой указатель не может быть «вызван» напрямую.
В конце тесты, которые не пройдены (в VS2010).Все из-за перегрузки operator ()
.И это делает этот код в основном бесполезным, так как он не будет работать с результатом bind
.Но, возможно, это может быть доработано.
Ответ на вопрос Андре Бергнера:
function_signature_impl
никогда не происходит от самого себя.Это шаблон типа, который означает только слабосвязанное семейство реальных типов.Но фактические типы (даже если они принадлежат к одному и тому же семейству) являются разными типами.
&T::operator()
- это указатель на оператор вызова (operator()
) типа T
- очевидно.По сути, просто указатель на функцию-член (где функция-член оказывается оператором вызова).В то время как decltype
это тип этого указателя.Это может показаться незначительным (особенно если type_info::name
обоих показывает одно и то же), но для шаблонов это имеет значение, поскольку один является указателем, а другой - типом (очевидно).
Этот «случай» необходим дляобложка для функторов (типы объектов, которые можно вызвать).Обратите внимание, что этот неспециализированный function_signature_impl
используется только в том случае, если аргумент шаблона T
не соответствует ничему другому среди перечисленных «случаев».
Надеюсь, я понял это сразу после этого долгого времени.Хотя я не уверен, что когда-либо действительно и полностью понял это.Код был немного результатом экспериментов.