如何确定可调用对象的函数签名

How to determine function signature of a callable object?

本文关键字:函数 对象 何确定 调用      更新时间:2023-10-16

有没有办法在C++中确定可调用对象的函数签名?

请考虑以下事项:

template< typename F >
void fun(F f)
{
    // ...
}

假设fun仅使用可调用的"事物"进行调用。

fun里面,我想知道函数f的签名是什么。这应该适用于函数指针、引用、包装器、lambda、绑定、函数对象(前提是它们只有一个operator ())等。我对Visual Studio 2010 SP 1有限制,但即使不在该编译器上工作,我也对标准解决方案感兴趣。

(函数签名Return_Type ([Arg1_Type [, Arg2_Type [, ... ] ] ]);与std::function/boost::function相同。

至少知道f的返回值的部分解决方案具有一定的价值。(我已经尝试过std::result_of但在我尝试过的任何情况下都无法让它工作。

在 C++0x 兼容编译器上,您至少可以使用 decltype(f()) 获取 f() 的结果类型。Visual C++ 2010 应该支持 decltype,尽管我自己还没有检查过。至于获取参数类型,我不确定是否有一种方法可以使用函数指针。

编辑

Boost.Function似乎已经弄清楚了,至少在某些编译器上是这样(例如,它不适用于旧版本的VC++或Borland C++)。它可以包装函数指针并为其提取参数。然而,该解决方案似乎相当复杂,它涉及使用 Boost.PP 定义多个模板。如果你想尝试重新实现所有内容,你当然可以尝试,但我认为你也可以使用一个虚拟的 Boost.Function 包装器来让事情变得更容易,例如 boost::function<decltype(f)>::second_argument_type获取第二个参数类型。

你可以看看提升函数类型:

http://www.boost.org/doc/libs/1_46_1/libs/function_types/doc/html/boost_functiontypes/introduction.html

在尝试解决此问题时,我想出了以下部分解决方案:

#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;
}

(没有添加用于再次检查不正确参数的代码。

这个想法是,function_signature< decltype( f ) >::type应该是f( ... )的签名,其中"......"是签名。这尤其意味着指向成员函数的指针在这里是一个无效的参数(尽管代码没有对此进行检查),因为这样的指针不能直接"调用"。

最后是失败的测试(在VS 2010中)。这一切都是由于operator ()超载。这使得该代码大多无用,因为它不适用于 bind 的结果。但也许它可以进一步发展。


回答安德烈·伯格纳的问题:

function_signature_impl从来都不是从自身衍生出来的。它是一个类型模板,仅表示实际类型的松散耦合族。但实际的类型(即使你属于同一个家族)是不同的类型。

显然,&T::operator()是指向类型为T的呼叫运算符(operator())的指针。基本上只是一个成员函数指针(其中成员函数恰好是调用运算符)。虽然decltype是该指针的类型。这可能看起来微不足道(尤其是两者type_info::name显示相同),但对于模板来说,这确实很重要,因为一个是指针,而另一个是类型(显然)。

需要此"案例"来覆盖函子(对象"可调用"的类型)。请注意,仅当模板参数T与列出的"案例"中的其他任何参数不匹配时,才会使用此非专用function_signature_impl

我希望在这么长时间之后我做对了。虽然我不确定我是否真正完全理解过它。代码是实验的结果。

这个答案是 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()时才有效,并且不适用于 lambda。

您可以使用std::is_invocable_r