std::function as class member?

Is it possible to use a functor like std::function<T* (Args&& ... args)> as class member? I've seen many examples using type erasure for the return value of the functor to store it as class member std::function<void* ()>. But my callbacks will also have an arbitary list of arguments.
> Is it possible to use a functor like std::function<T* (Args&& ... args)> as class member?

Yes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <functional>
#include <string>

template < typename RESULT_TYPE, typename... ARG_TYPES > struct A
{
    std::function< RESULT_TYPE( ARG_TYPES&&... ) > fn ;

    RESULT_TYPE operator() ( ARG_TYPES&&... args ) const { return fn( std::forward<ARG_TYPES>(args)... ) ; }
};

int main()
{
    A< std::string::size_type, const std::string& > strlen { &std::string::size } ;
    A< std::ostream&, std::ostream&, int, char > setwf =
        { [] ( std::ostream& stm, int w, char f ) -> std::ostream& { stm.width(w) ; stm.fill(f) ; return stm ; } } ;

    const std::string str = "hello world!" ;
    setwf( std::cout, 5, '*' ) << strlen(str) << '\n' ;
}

http://coliru.stacked-crooked.com/a/053bd8badfae2448
Thanks this looks good. Is there a way to define an interface for this templated class, to use it in other classes also as member or store it in a container?
I think I've a solution. But I'm not sure if this is the right way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
template <typename R, typename... Args>
class GenericCallback;

class Callback
{
protected:
    Callback(){}	
	
public:	
	virtual ~Callback(){}

    template <typename R, typename... Args>
    R* run(Args &&... args)
    {	
		/* Validate Return type and arguments, if not equals to
		   generic callback types throw exception */
	
		auto C = static_cast<GenericCallback<R, Args...> *>(this);
	    if(C!=0){
			return (*C)(std::forward<Args>(args)...);
		}
    }
	
	template <typename R, typename... Args>
    static Callback* create(std::function<R* (Args&& ... args)> delegate)
    {
		return new GenericCallback<R, Args...>(delegate);
    }	
};

template <typename R, typename... Args>
class GenericCallback : public Callback
{
public:
	GenericCallback(std::function<R* (Args&& ... args)> func)
	:m_functor(func){}
	
	virtual ~GenericCallback(){}
	
	R* operator() (Args&& ... args){
		return m_functor(std::forward<Args>(args)...);
	}

private:
	std::function<R* (Args&& ... args)> m_functor = nullptr;
};
Use dynamic_cast<> perhaps? (throw std::bad_cast instead of UB)

1
2
3
4
5
6
7
8
9
class Callback
{
    // ... 
    
    template < typename R, typename... Args > R* run( Args&&... args ) // may throw std::bad_cast
    { return dynamic_cast< GenericCallback< R, Args&&... >& >(*this)( std::forward<Args>(args)... ) ; }

    // ....
};
I've used static_cast because any dynamic_cast fails, if I try:
auto C = dynamic_cast<brGenericDelegate<R, Args&&... >* >(this);
What do you mean with UB?
> dynamic_cast fails, if I try:

dynamic_cast fails if the type is not accurate.

Announcing the type would simplify the code.
As in this example: http://www.cplusplus.com/forum/general/142773/#msg753676


> What do you mean with UB?

Undefined behaviour.
Hmm, I'm not sure what you mean.

This kind of type announcing is new for me, I only used typedefs (or new c++11 using mechanism) to simplify my code (i.e. on map definitions). Could you give me more information what I've to announce where and why? Thx in advance.
> Hmm, I'm not sure what you mean.

When "references to references" occur in template code, 'reference collapsing' rules apply.

For instance, with template < typename T > void foo( T&& arg )
the type T could be any one of plain unadorned T, reference to T T&, or reference to const T const T&.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <type_traits>

template < typename T > void foo( T&& )
{
    if( std::is_same< T, int >::value ) std::cout << "int\n" ;
    else if( std::is_same< T, int& >::value ) std::cout << "int&\n" ;
    else if( std::is_same< T, const int& >::value ) std::cout << "const int&\n" ;
}

int main()
{
    foo(100) ; // int
    int i = 45 ; foo(i) ; // int&
    const int j = 7 ; foo(j) ; // const int&
}

http://coliru.stacked-crooked.com/a/87bf33565e61fdf7

So, in template <typename R, typename... Args> R* run(Args &&... args
we may not get the right derived type with GenericCallback<R, Args...>

std::remove_reference<> and std::remove_cv<> has to be first applied to each type in the parameter pack.



> This kind of type announcing is new for me,
> I only used typedefs (or new c++11 using mechanism) to simplify my code

The simplest solution (one not requiring any metaprogramming) would be:

a. Avoid reference collapsing altogether by passing all parameters by value.

b. Simulate passing by reference by wrapping the parameter in std::reference_wrapper<>

For instance:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <functional>
#include <type_traits>

template < typename R, typename... ARGS > struct callback ;
template < typename TAG > struct type_announcer ;
template < typename R, typename... ARGS > using callback_type = std::function< R(ARGS...) > ;

struct callback_base
{
    virtual ~callback_base() = default ;

    template < typename R, typename... ARGS >
    static callback_base* create( typename callback<R,ARGS...>::fn_type fn ) { return new callback<R,ARGS...>(fn) ; }

    template < typename R, typename... ARGS > R call( ARGS... args ) const
    {
        using derived_type = callback<R,ARGS...> ;
        return dynamic_cast< const derived_type& >(*this)(args...) ;
    }
};

template < typename R, typename... ARGS > struct callback : callback_base
{
    using fn_type = callback_type< R, ARGS... >;

    callback( fn_type f ) : fn(f) {}

    R operator() ( ARGS... args ) const { return fn(args...) ; }

    const fn_type fn ;
};

int main()
{
    auto foo = callback_base::create<int,int,int>( [] ( int a, int b ) { return a + b ; } ) ;
    try
    {
       std::cout << foo->call<int>( 23, 78 ) << " ok\n" ; // 101 ok
       std::cout << foo->call<int>( 23.45 ) << " ok\n" ; // throws bad_cast
    }
    catch( std::exception& e ) { std::cerr << e.what() << '\n' ; } // std::bad_cast

    auto bar = callback_base::create<std::string,std::string,char,char>( [] ( std::string a, char b, char c ) { return a + b  + c ; } ) ;
    std::string str = "abcd" ;
    std::cout << bar->call<std::string>( str, 'e', '!' ) << '\n' ; // abcde!

    auto baz = callback_base::create< void, std::reference_wrapper<int> >( [] ( int& a )  { ++a ; } ) ;
    int k = 99 ;
    baz->call<void>( std::ref(k) ) ; // pass wrapped int
    std::cout << k << '\n' ; // 100

    auto foobar = callback_base::create<void,int*>( [] ( int* a )  { if(a) ++*a ; } ) ;
    foobar->call<void>( std::addressof(k) ) ;
    std::cout << k << '\n' ; // 101
}

http://coliru.stacked-crooked.com/a/2b208f32bde4814f
Thanks for the great explanation! I think I got it now and see I've to learn much more about templating.
Topic archived. No new replies allowed.