C ++中有没有办法确定可调用对象的函数签名?
请考虑以下事项:
template< typename F >
void fun(F f)
{
// ...
}
让我们假设只有可调用的“东西”才会调用fun
。
fun
内部我想知道函数f
的签名是什么。这应该适用于函数指针,引用,包装器,lambdas,绑定,函数对象(假设它们只有一个operator ()
)等等。我受限于Visual Studio 2010 SP 1,但即使不使用该编译器,我也对标准解决方案感兴趣。
(函数签名为Return_Type ([Arg1_Type [, Arg2_Type [, ... ] ] ])
;与std::function
/ boost::function
相同。)
至少知道f
的返回值的部分解决方案具有一定的价值。 (我已经尝试std::result_of
,但无论如何我无法让它工作。)
答案 0 :(得分:3)
在符合C ++ 0x的编译器上,您至少可以使用f()
获取decltype(f())
的结果类型。 Visual C ++ 2010应该支持decltype,虽然我还没有自己检查过。至于获取参数类型,我不确定是否有一种方法可以使用函数指针。
Boost.Function似乎已经弄清楚了,至少在一些编译器上(例如,它不适用于旧版本的VC ++或Borland C ++)。它可以包装函数指针并为它们提取参数。然而,解决方案似乎相当复杂,它涉及使用Boost.PP定义多个模板。如果您想尝试重新实现所有内容,您当然可以尝试,但我认为您也可以使用虚拟Boost.Function包装器来简化操作,例如boost::function<decltype(f)>::second_argument_type
获取第二个参数类型。
答案 1 :(得分:3)
在尝试解决这个问题时,我提出了以下部分解决方案:
#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( ... )
调用的签名,其中“...”是签名。这尤其意味着指向成员函数的指针在这里是一个无效的参数(尽管代码不会对此进行检查),因为这样的指针不能直接“调用”。
最后是失败的测试(在VS 2010中)。全部归因于operator ()
超载。这使得代码大多无用,因为它不适用于bind
的结果。但也许它可以进一步发展。
回答AndréBergner的询问:
function_signature_impl
永远不会衍生自己。它是一种类型模板,仅表示松散耦合的实际类型族。但实际的类型(即使它们属于同一个家族)也是不同的类型。
&T::operator()
是指向operator()
类型的调用运算符(T
)的指针 - 显然。基本上只是一个成员函数指针(其中成员函数恰好是一个调用操作符)。虽然它的decltype
是该指针的类型。这可能看起来微不足道(特别是两者的type_info::name
显示相同),但对于模板,它确实重要,因为一个是指针而另一个是类型(显然)。
需要这个“案例”来涵盖仿函数(对象是“可调用”的类型)。请注意,仅当模板参数function_signature_impl
与列出的“案例”中的任何其他内容不匹配时,才使用此非专业化T
。
我希望在那么久之后我能把它弄好。虽然我不确定我是否真的完全理解它。代码是试验的结果。
答案 2 :(得分:2)
您可以查看Boost函数类型:
答案 3 :(得分:1)
这个答案是SlashLife在freenode上给出的## c ++:
template <typename T, typename Signature>
struct signature_impl;
template <typename T, typename ReturnType, typename... Args>
struct signature_impl<T, ReturnType(T::*)(Args...)>
{
using type = ReturnType(Args...);
};
template <typename T>
using signature_t = signature_impl<T, decltype(&T::operator())>;
需要注意的是,只有在存在唯一的operator()
且它不适用于lambdas时它才有效。