Passing functions between classes

Hi

I am wanting to create a class which has a container of function pointers from varying classes and can then call them all.

What would be a way to be able to create such, was thinking of having a generic class to inherit from but no joy with that.....

Could you advise if there is a process name/subject which would help with this problem I could look into. Hope it clear, have put somesort of code together to show what im after.... if you can call it code, capitilised is where im unsure on how to/best way.

Thanks in advance.

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
  class Class1
{
public:
	Class1() {};
	void doSomthing()
};

class Class2
{
public:
	Class2() {};
	void doSomthing()
};

class doSomthingList
{
	doSomthingList() {};
	void addDoSomthing(FUNCTION PTR)
	{
		m_aList.pushback(FUNCTION PTR);
	}

	void doDOSomthing()
	{
		for each in list
			call function;
	}

private:

	Vector<CLASS FUNCTIONS> m_aList;
	
};
void main()
{
	Class1 c1();
	Class2 c2();

	doSomthingList theList();
	theList.addDoSomthing(c1);
	theList.addDoSomthing(c2);

	theList.doDoSomthing()
}
Last edited on
Something along these lines, perhaps:

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
#include <iostream>
#include <functional>
#include <vector>

struct A { void do_some_thing() const { std::cout << "A::do_something\n" ; } };

struct B
{
    int do_some_thing_else( int v )
    {
        std::cout << "B::do_something_else(" << v << ")\n" ;
        return i += v ;
    }
    int i = 0 ;
};

void do_a_third_thing( int v ) { std::cout << "::do_a_third_thing(" << v << ")\n" ; }

struct to_do_list
{
    // https://en.cppreference.com/w/cpp/utility/functional/bind
    template < typename FN, typename... ARGS > void add( FN fn, ARGS&&... args )
    { things_to_be_done.emplace_back( std::bind( fn, std::forward<ARGS>(args)... ) ) ; }

    void do_them_now()
    {
        int cnt = 0 ;
        for( const auto& fn : things_to_be_done ) { std::cout << ++cnt << ". " ; fn() ; }
        things_to_be_done.clear() ;
    }

    // https://en.cppreference.com/w/cpp/utility/functional/function
    std::vector< std::function< void() > > things_to_be_done ;
};

int main ()
{
    A a ;
    B b ;

    to_do_list list ;
    list.add( &A::do_some_thing, std::ref(a) ) ;
    list.add( &B::do_some_thing_else, std::ref(b), 23 ) ;
    list.add( &do_a_third_thing, 999 ) ;
    list.add( [] { std::cout << "closure: do this too\n" ; } ) ;

    list.do_them_now() ;
}

http://coliru.stacked-crooked.com/a/75dd3c966f43a1bc
https://rextester.com/ZMSRH32267
JL Borges, thanks a lot and dam that was fast for all that.....

So looking at this I need to read up on functional and templates. (Like a clear understanding before I start using any sort of code)

One last question if you don't mind, if I was wanting the class itself to pass the function to the list would it be a case of passing this* or someother way to match what the template is after.

ie
1
2
3
4
5
6
struct A
dosomething(){}
passtolist()
{
list.add(this*.dosomthing)
}




Yes, pass the this pointer. For example,

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
#include <iostream>
#include <functional>
#include <vector>

struct B
{
    int do_some_thing_else( int v )
    {
        std::cout << "B::do_something_else(" << v << ")\n" ;
        return i += v ;
    }
    int i = 0 ;
};

void do_a_third_thing( int v ) { std::cout << "::do_a_third_thing(" << v << ")\n" ; }

struct to_do_list
{
    // https://en.cppreference.com/w/cpp/utility/functional/bind
    template < typename FN, typename... ARGS > void add( FN fn, ARGS&&... args )
    { things_to_be_done.emplace_back( std::bind( fn, std::forward<ARGS>(args)... ) ) ; }

    void do_them_now()
    {
        int cnt = 0 ;
        for( const auto& fn : things_to_be_done ) { std::cout << ++cnt << ". " ; fn() ; }
        things_to_be_done.clear() ;
    }

    // https://en.cppreference.com/w/cpp/utility/functional/function
    std::vector< std::function< void() > > things_to_be_done ;
};

struct A
{
    void do_some_thing() const { std::cout << "A::do_something\n" ; }

    void add_self( to_do_list& lst ) const { lst.add( &A::do_some_thing, this ) ; }
};

int main ()
{
    A a ;
    B b ;

    to_do_list list ;

    a.add_self(list) ;
    list.add( &B::do_some_thing_else, std::ref(b), 23 ) ;
    list.add( &do_a_third_thing, 999 ) ;
    list.add( [] { std::cout << "closure: do this too\n" ; } ) ;

    list.do_them_now() ;
}

http://coliru.stacked-crooked.com/a/c4ffc4aa4ee3db03
You could use virtual methods too. This is less flexible, but less complicated too:
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
#include <iostream>
#include <vector>

using std::cout;

class Base {
public:
    virtual void doStuff() { cout << "Base::doStuff\n"; }
};

class C1 : public Base {
public:
    virtual void doStuff() { cout << "C1::doStuff\n"; }
};

class C2 : public Base {
public:
    virtual void doStuff() { cout << "C2::doStuff\n"; }
};

int
main()
{
    C1 c1;
    C2 c2;
    std::vector<Base *> v;
    v.push_back(&c1);
    v.push_back(&c2);

    for (Base *b : v) {
	b->doStuff();
    }
}

Dam templates are confusing........ slowly getting there with them, but one more question.

When calling argument such as

template < typename FN, typename... ARGS > void add( FN fn, ARGS&&... args )

as in above examples, if say I wanted to add an additional argument such as and int, it wont allow me to add at end such as

template < typename FN, typename... ARGS > void add( FN fn, ARGS&&... args, int someInt)

what am I missing.

Also anyone got any videos/links to stuff that explains templates in detail and a way even an old git like me could work out.

Thanks in advance.

> if say I wanted to add an additional argument such as and int, it wont allow me to add at end

With a non-trailing function parameter pack (a parameter pack that is not the last parameter of the function template), the compiler can't deduce the arguments by itself; we need to explicitly specify the template arguments for the pack.

For example:
1
2
3
4
5
6
7
8
template < typename... ARGS > void foo( ARGS&&..., int ) {}

int main()
{
    foo<int,double>( 1, 2.3, 4 ) ; // fine: ARGS explicitly specified as pack of int, double
    
    // foo( 1, 2.3, 4 ) ; // *** error: deduction failed
}

http://coliru.stacked-crooked.com/a/54c16591fbc949c3


In your code, this would have been fine, we have a trailing function parameter pack (the parameter pack is the last parameter of the function template), and the compiler would deduce everything correctly:
template < typename FN, typename... ARGS > void add( FN fn, int someInt, ARGS&&... args )


This is more involved, you may want to ignore it for now:
The idea is to write the function with a trailing parameter pack,
template < typename FN, typename... ARGS > void add( FN, ARGS&&... args )
and then, in the function, extract the last item in the pack as an int

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
#include <iostream>
#include <type_traits>
#include <tuple>

// determine the type of the last variadic parameter
template < typename... > struct last_t {} ;

// only one parameter T, T is the type of the last parameter
template < typename T > struct last_t<T> { using type = T ; } ;

// two or more parameters, discard the first parameter and apply recursively
template < typename FIRST, typename... REST >
struct last_t<FIRST,REST...> : last_t<REST...> {};

template < typename FN, typename... ARGS > void add( FN, ARGS&&... args )
{
    // generate an error if the type of the last argument in the pack can't be converted to int
    static_assert( std::is_convertible< typename last_t<ARGS...>::type, int >::value,
                   "last argument is not convertible to int" ) ;

    constexpr std::size_t N = sizeof...(ARGS) ; // the number of variadic parameters in the pack

    // extract the value at the end of the pack as an int
    const int val = std::get<N-1>( std::tuple<ARGS&&...>( std::forward<ARGS>(args)... ) ) ;

    std::cout << "last int argument: " << val << '\n' ;
}

int main()
{
    add( []{}, 1, 2, "abc", 4 ) ; // last int argument: 4
    add( []{}, 1, 2, "abc", 4, 5, 6 ) ; // last int argument: 6
    add( []{}, 1 ) ; // last int argument: 1
    add( []{}, 1, 2, "abc", 24.5 ) ; // last int argument: 24 (narrowing conversion to int)

    // add( []{}, 1, 2, "abc", 4, "def" ) ; // *** error *** : static_assertion failed
                                            // "last argument is not convertible to int"
}

http://coliru.stacked-crooked.com/a/d9265ee234826db0
Going great so far, got my head around some of this and code working, but now I wish to pass the argument at the stage where I call the function.

So in the cases as above

1
2
3
4
5
6
7
8
9
10
11
struct B
{
    int do_some_thing_else( int v )
    {
        std::cout << "B::do_something_else(" << v << ")\n" ;
        return i += v ;
    }
    int i = 0 ;
};

void do_a_third_thing( int v ) { std::cout << "::do_a_third_thing(" << v << ")\n" ; }


both take an int as an argument which is set when adding them to the list. How would I go about assigning that int when calling the function? and guessing in changing such it would break for struct A as that would not take an argument declared at that point?

from what I can gather it would be this to be changed.....
 
for( const auto& fn : things_to_be_done ) { std::cout << ++cnt << ". " ; fn() ; }



Thanks again for all your help.
Assuming that all the functions to be called would accept an argument, use a placeholder for the argument in the bind expression.
https://en.cppreference.com/w/cpp/utility/functional/placeholders

And then, provide the actual argument when invoking the function (the actual argument replaces the placeholder for the invocation).

For example:

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
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <functional>
#include <vector>

struct A
{
    // **** note: modified to take an int argument
    void do_some_thing( long arg ) const
    { std::cout << "A::do_something(" << arg << ")\n" ; }
};

struct B
{
    int do_some_thing_else( int v )
    {
        std::cout << "B::do_something_else(" << v << ")\n" ;
        return i += v ;
    }
    int i = 0 ;
};

void do_a_third_thing( int v ) { std::cout << "::do_a_third_thing(" << v << ")\n" ; }

struct to_do_list
{
    // https://en.cppreference.com/w/cpp/utility/functional/bind
    // **** note: we use a placeholder for the int argument to be passed later
    template < typename FN, typename... ARGS > void add( FN fn, ARGS&&... args )
    { things_to_be_done.emplace_back( std::bind( fn, std::forward<ARGS>(args)..., std::placeholders::_1 ) ) ; }

    void do_them_now( int with_this_arg ) // *** note: call the functions with_this_arg
    {
        int cnt = 0 ;
        for( const auto& fn : things_to_be_done )
        {
            std::cout << ++cnt << ". " ;
            fn(with_this_arg) ; // *** call passing with_this_arg as the argument
        }
        // things_to_be_done.clear() ; // *** commented out
    }

    // https://en.cppreference.com/w/cpp/utility/functional/function
    // **** note: type is void(int)
    std::vector< std::function< void(int) > > things_to_be_done ;
};

int main ()
{
    A a ;
    B b ;

    to_do_list list ;

    // *** note: we do not specify the int argument at this stage
    list.add( &A::do_some_thing, std::ref(a) ) ;
    list.add( &B::do_some_thing_else, std::ref(b) ) ;
    list.add( &do_a_third_thing ) ;
    list.add( [] ( int arg ) { std::cout << "closure: do this too arg == " << arg << '\n' ; } ) ;

    for( int arg : { 25, 99, -7 } )
    {
        std::cout << "\ncall functions with arg == " << arg << "\n----------------\n" ;
        list.do_them_now(arg) ; // *** pass the argument to call the function with
    }
}

http://coliru.stacked-crooked.com/a/c725b9fe8219b19f
Topic archived. No new replies allowed.