Function pointer (callback)

Hi,

A short summary
---------------------
How to create a function (F), which takes any arbitrary function (callback), with any arbitrary (number and type) parameters as arguments. The only thing F should do is to simply call callback function with arguments.

A detailed description
-------------------------

I am trying to create a library which has a function, say mylibfun(). The function has following parameters: 2 integers, then a function pointer (callback), and the arguments to function pointer.

//something like this
void mylibfun(int,int, void (*callback)(....), cb_arguments.......)

My intention is that user can call mylibfun, with any function as callback, which any number of parameters of any type. The only thing fixed is that, mylibfun will have at-least 3 arguments, 2 integers and one function pointer. I don’t know the extract type of function pointer, because the function is arbitrary. The same applies to cb_arguments.

mylibfun() will do some processing based on the first two integers and then call callbackfunction() with the cb_arguments. I never need to know or use anything from cb_arguments

so code for mylibfun will something look like this

1
2
3
4
5
6
7
8
9
10
11
mylibfun(int p1, int p2, void (*callback)(...), cb_arguments)
{

for(i=p1;i<p2;i++)
{
//do something 
}

(*callback)(cb_arguments);

}


The major problem I am facing is specifying a generic function type. Can anyone suggest how can I proceed with function pointers. Is there another way to do this.

Thanks in Advance

Last edited on
C++ has a rather nice feature in std::function. You could use templated pointers if you so desired, but std::function generally results in things being neater.

Code example from the reference page on this site:
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
// function example
#include <iostream>     // std::cout
#include <functional>   // std::function, std::negate

// a function:
int half(int x) {return x/2;}

// a function object class:
struct third_t {
  int operator()(int x) {return x/3;}
};

// a class with data members:
struct MyValue {
  int value;
  int fifth() {return value/5;}
};

int main () {
  std::function<int(int)> fn1 = half;                    // function
  std::function<int(int)> fn2 = &half;                   // function pointer
  std::function<int(int)> fn3 = third_t();               // function object
  std::function<int(int)> fn4 = [](int x){return x/4;};  // lambda expression
  std::function<int(int)> fn5 = std::negate<int>();      // standard function object

  std::cout << "fn1(60): " << fn1(60) << '\n';
  std::cout << "fn2(60): " << fn2(60) << '\n';
  std::cout << "fn3(60): " << fn3(60) << '\n';
  std::cout << "fn4(60): " << fn4(60) << '\n';
  std::cout << "fn5(60): " << fn5(60) << '\n';

  // stuff with members:
  std::function<int(MyValue&)> value = &MyValue::value;  // pointer to data member
  std::function<int(MyValue&)> fifth = &MyValue::fifth;  // pointer to member function

  MyValue sixty {60};

  std::cout << "value(sixty): " << value(sixty) << '\n';
  std::cout << "fifth(sixty): " << fifth(sixty) << '\n';

  return 0;
}


Reference page:
http://www.cplusplus.com/reference/functional/function/

Article about them:
http://probablydance.com/2012/12/16/the-importance-of-stdfunction/

Have fun!

-Albatross
@Albatross: Thank you for the reply, but I dont think this will solve the problem. The problem is that (line no 20 to 24) you are specifying the type somehow. For me its a library function, I dont have any idea of the type nor on number of arguments
Oh, wait. I see. Hmm.

I'm sure that what you want to do is possible. std::thread has shown that. Is there anything preventing you from making your mylibfun a function template? You'll likely need to make it a variadic template anyway to allow it to be called with an arbitrary number of arguments, assuming you want to support non-POD types.

-Albatross
It looked like an interesting problem. This is what I came up with:

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

void cb1(int a, double b, char c)
{
    std::cout << "cb1( " << a << ", " << b << ", " << c << ")\n";
}

void cb2(const char* a, const char* b)
{
	std::cout << "cb2( \"" << a << "\", \"" << b << "\")\n" ; 
}

template <typename ...Args>
void mylibfun(int, int, std::function<void(Args...)>&& func, Args... args)
{
    func(args...);
}

template <typename ...Args>
void mylibfun(int a, int b, void f(Args...), Args... args)
{
    mylibfun(a, b, std::function<void(Args...)>(f), args...);
}

int main()
{
    mylibfun(1, 2, cb1, 1, 2.0, 'c');
    mylibfun(3, 4, cb2, "abc", "xyz") ;
}


http://ideone.com/JTMqxW
@cire Thankyou for the reply. I think there is a template problem. I will explain this below

(@albatross and @cire) Since mylib is compiled separately, during compilation of mylib, the actual type resolution of mylibfun cannot take place. Hence the exact functions are not generated. This can be shown by splitting the compilation to two steps. (May be I am wrong about the actual reason, but I don’t know how to clear the error below)

I modified cire's example. here you can see the problem

temptest.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

#include "header.hpp"

void cb1(int a, double b, char c)
{
    std::cout << "cb1( " << a << ", " << b << ", " << c << ")\n";
}

void cb2(const char* a, const char* b)
{
	std::cout << "cb2( \"" << a << "\", \"" << b << "\")\n" ; 
}


int main()
{
    mylibfun(1, 2, cb1, 1, 2.0, 'c');
    mylibfun(3, 4, cb2, "abc", "xyz") ;
    myprint();
}	



header.hpp
1
2
3
4
5
6
7
8
#include <iostream>
#include <functional>
#include <utility>

void myprint();

template <typename ...Args>
void mylibfun(int a, int b, void f(Args...), Args... args);


header.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "header.hpp"

template <typename ...Args>
void mylibfun(int, int, std::function<void(Args...)>&& func, Args... args)
{
    func(args...);
}

template <typename ...Args>
void mylibfun(int a, int b, void f(Args...), Args... args)
{
    mylibfun(a, b, std::function<void(Args...)>(f), args...);
}

void myprint()
{
	std::cout<<"I am here only to check something\n";
}




commands


1
2
3
g++ -c temptest.cpp -std=c++11
g++ -c header.cpp  -std=c++11
g++ header.o  temptest.o -std=c++11


error

1
2
3
4
5
temptest.o: In function `main':
temptest.cpp:(.text+0x115): undefined reference to `void mylibfun<int, double, char>(int, int, void (*)(int, double, char), int, double, char)'
temptest.cpp:(.text+0x134): undefined reference to `void mylibfun<char const*, char const*>(int, int, void (*)(char const*, char const*), char const*, char const*)'
collect2: error: ld returned 1 exit status
 

Last edited on
Put the definition of the template in the header; the compiler needs to see it.

And use perfect forwarding. http://thbecker.net/articles/rvalue_references/section_07.html

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
// ************* header.hpp ****************
#include <functional>

template < typename FN, typename... ARGS >
void mylibfun( int a, int b, FN&& fn, ARGS&&... args )
{ if( a<b ) std::bind( std::forward<FN>(fn), std::forward<ARGS>(args)... )() ; }

// ...

// ************* temptest.cpp **************
// #include "header.hpp"
#include <iostream>
#include <string>
#include <cstdio>

int main()
{
    std::string str = "abcdefgh" ;
    mylibfun( 0, 5, &std::string::push_back, std::ref(str), '*' ) ;
    mylibfun( 0, 5, &std::ostream::write,  std::ref(std::cout), str.c_str(), str.size() ) ;
    mylibfun( 0, 5, &std::ostream::put, std::ref(std::cout), '\n' ) ;

    std::string other = "123456" ;
    mylibfun( 0, 5, &std::string::swap, std::ref(str), std::ref(other) ) ;
    mylibfun( 0, 5, &std::printf, "str: %s\n", str.c_str() ) ;
    std::cout << "other: " << other << '\n' ;
}

http://coliru.stacked-crooked.com/a/09b116da28e0a6d1
Last edited on
@JLBorges: nice trick :) your solution will work for me.

Just out of curiosity (may be not the right place to as this question) is there a "C" way of achieving the same?
You could probably do something dodgy with va_arg and void pointers, but I don't really know how. The real question is, why would you ever need a "C" way of doing the same thing?
@NT3

There are some programs which cannot be handled by g++. These are mainly due to simple things. For eg consider this line (this is from a benchmark suite)

void* new = NULL;

gcc happily compiles this. Where as for c++ "new" is an operator. so g++ shows error.

Since I am building a library, it would be nice to be compatible with c. Its not an absolute requirement for me though.
Last edited on
> is there a "C" way of achieving the same?

If the set of signatures of functions that can be called is known apriori, it is fairly straightforward (though typically verbose) to implement a set of forwarding wrappers in C.

If not - "with any function as callback" - I do not know of a type-safe way that it can be done in C.
@JLBorges

I dont put any limitation on the function, so I dont have all the signatures. I think at least for now I will stay with c++. For me supporting generic function is an absolute requirement where as supporting c is optional ( but would be quite nice )
There is one more thing

The callback function expects me to add one more argument to the argument list before calling it.

wrt cire's example, the function call is made using the following code

func(args...);

How can I add one more parameter to args. something like

func(args...,1);

The above is causing error
> There is one more thing
> The callback function expects me to add one more argument to the argument list before calling it.

How can you even think of adding one more argument since you do not have the function signature?

What would be the type of that argument? Even if you make it a template type parameter, how do you intend to create an object of that type? Is it to be passed by value, passed by reference, passed by reference to const, or passed by rvalue reference? Is it the first argument, or is it the second argument, or is it the last argument? What if the function has default values for trailing parameters, and the caller had used defaults for more than one of them? What if it a function like std::printf() with a variable number of arguments of unspecified types?
@JLBorges

The parameter is always an integer and will be passed by value. I have the flexibility to pass this as the first parameter or the last parameter.

Unknown signature problem: The functions which I am looking wont have a default value for trailing parameters nor will it have variable number of arguments. This is because, these functions are generated my one of my compiler passes. For simplification assume that my compiler pass1, generates a function which do not have default values nor variable number of arguments. The pass2 operates on these functions, and simply adds a incoming parameters (as the first or last). Now I have to replace all the original function calls with the new one, which requires the extra parameter. For simplicity, please ignore all the details. Lets assume, we know the signature, and we just want to add one more parameter to the args list before calling the function.
Last edited on
> Lets assume, we know the signature, and we just want to add
> one more parameter to the args list before calling the function.

The structure would be the same as earlier; with an extra int argument added to the call either at the beginning or at the end.

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
// ************* header.hpp ****************
#include <functional>

// call function with one extrar (int by value) last parameter
template < typename FN, typename... ARGS >
void mylibfun_add_tail( int a, int b, FN&& fn, ARGS&&... args )
{
    if( a<b )
    {
        const int extra_param = a + b ;
        // call function with an additional int as the last argument
        std::bind( std::forward<FN>(fn), std::forward<ARGS>(args)..., extra_param )() ;
    }
}

// call function with one extrar (int by value) first parameter
template < typename FN, typename... ARGS >
void mylibfun_add_head( int a, int b, FN&& fn, ARGS&&... args )
{
    if( a<b )
    {
        const int extra_param = b - a ;
        // call function with an additional int as the first argument
        std::bind( std::forward<FN>(fn), extra_param, std::forward<ARGS>(args)... )() ;
    }
}

// ************* temptest.cpp **************
// #include "header.hpp"
#include <iostream>
#include <string>

void foo( std::string& str, char ch, int sz )
{
    std::cout << "foo( '" << str << "', '" << ch << "', " << sz << " )\n" ;
    str += std::string( sz, ch ) ;
}

void bar( int sz, std::string& str, char ch )
{
    std::cout << "bar( " << sz << ", '" << str << "', '" << ch << "' )\n" ;
    str = std::string( sz, ch ) + ' ' + str ;
}

int main()
{
    std::string str = "hello world" ;
    std::cout << "str: '" << str << "'\n------------------------\n" ;

    mylibfun_add_tail( 1, 4, foo, std::ref(str), '!' ) ;
    std::cout << "str: '" << str << "'\n------------------------\n" ;

    mylibfun_add_head( 1, 4, bar, std::ref(str), '>' ) ;
    std::cout << "str: '" << str << "'\n------------------------\n" ;
}

http://coliru.stacked-crooked.com/a/68fb705ffcecdcb2
That solves the problem. Thanks everyone and a special thanks to JLBorges, and Cire
Topic archived. No new replies allowed.