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

时间:2011-05-09 13:23:44

标签: c++ metaprogramming

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,但无论如何我无法让它工作。)

4 个答案:

答案 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)

答案 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时它才有效。