سؤال

I'm not sure if it's possible, so that's what I want to find out.

I'd like to create a function which accepts any kind of functor/callable object, but I want to know what the argument type is. ( but not enforce it )

So, this one captures all but doesn't give me the type of the argument:

template < typename T >
void optionA( T );

This one captures most, and has the type of the argument

template < typename T >
void optionB( std::function< void(T) > );

But this one doesn't allow lambdas, so

optionB( [](int){} );

will not compile. Which is somewhat strange, as this will compile:

std::function< void(int) > func = [](int){};
optionB( func );

So is there a way to accept all options and also know which type of argument is expected?

thanks in advance!

-- edit --

The reason I'd like to do this comes from the fact that I want the user of my library to register a callback with a certain type. To me, the most natural way is

auto callback = []( int val ) { cout << "my callback " << val << endl; };
object.register( callback );

(with or without the use of callback as intermediate variable)

Since I need to modify the behaviour based on the type of value the user expects, I need to know what type he/she expects.

هل كانت مفيدة؟

المحلول

Here's an example that will work for most callables including functors and lambdas (although not for generic functors as @Yakk demonstrated in a comment on the question).

The code can also be useful when determining return type and multiple arguments.

template <typename T>
struct func_traits : public func_traits<decltype(&T::operator())> {};

template <typename C, typename Ret, typename... Args>
struct func_traits<Ret(C::*)(Args...) const> {
    using result_type =  Ret;

    template <std::size_t i>
    struct arg {
        using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
    };
};

template <typename T>
void option(T&& t) {
    using traits = func_traits<typename std::decay<T>::type>;

    using return_t = typename traits::result_type;         // Return type.
    using arg0_t = typename traits::template arg<0>::type; // First arg type.

    // Output types.
    std::cout << "Return type: " << typeid(return_t).name() << std::endl;
    std::cout << "Argument type: " << typeid(arg0_t).name() << std::endl;
}

To add support for regular functions add a specialization e.g.

template <typename Ret, typename... Args>
struct func_traits<Ret(*)(Args...)> { /* ... */ }

More useful info: Is it possible to figure out the parameter type and return type of a lambda?

نصائح أخرى

template < typename T >
void option( function< void(T) > )
{
    cout << typeid( T ).name() << endl;
}

template < typename T >
void option( void (*func)(T) )
{
    option( function< void(T) >( func ) );
}

template< typename F, typename A >
void wrapper( F &f, void ( F::*func )( A ) const )
{
    option( function< void(A) >( bind( func, f, placeholders::_1 ) ) );
}

template< typename F, typename A >
void wrapper( F &f, void ( F::*func )( A ) )
{
    option( function< void(A) >( bind( func, f, placeholders::_1 ) ) );
}

template < typename T >
void option( T t )
{
    wrapper( t, &T::operator() );
}

void test( int )
{
}

struct Object
{
    void operator ()( float )
    {
    }
};

int main( int, char *[] )
{
    Object obj;

    option( test );
    option( [](double){} );
    option( obj );

    return 0;
}

Based on information found here c++0x: overloading on lambda arity, which I found through @dyps link

This isn't the best solution, since it requires overloads for const/non-const/volatile etc. It does get the job done in terms of the original problem I was trying to solve...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top