functions passed by parameters or callback functions

2

Several questions:

  • In which cases are functions callback used?
  • Can they be used in classes?
  • Do they work in a similar way as in javascript ?
  • How is a function passed by means of a parameter?
  • At the moment of declaring the parameters of the function, what is its type?
asked by soldat25 23.02.2017 в 04:24
source

3 answers

4
  

In which cases are the callback functions used?

It is a question too broad to be answered in a concrete way, so it is best to give a broad answer: when necessary.

  • The design pattern Visitor can be implemented with callbacks (although also can be implemented without them).
  • Event response functions are usually implemented by callbacks.
  

Can they be used in classes?

Sure. Like any other data, a callback can be a member of a class, having these functions:

int suma      (int a, int b) { return a + b; }
int resta     (int a, int b) { return a - b; }
int multiplica(int a, int b) { return a * b; }
int divide    (int a, int b) { return a / b; }

And this class that contains a callback :

class Clase
{
    int (callback*)(int, int);
public:
    Clase(int (operación*)(int, int)) : callback(operación) {}
    int operar(int a, int b) { return callback(a, b); }
};

We can do the following:

Clase a(suma), b(resta);
std::cout << a.operar(1, 2) << '\n'; // muestra 3
std::cout << b.operar(1, 2) << '\n'; // muestra -1
  

Do they work in a similar way as in javascript ?

No. There are many differences between JavaScript and C ++ functions:

  • JavaScript is a language of dynamic typing .
  • JavaScript functions accept a number of undetermined parameters.
  • JavaScript functions can be used as functions or as objects.
    • Creating a function with new , it will act as a class, the this of the created object will point to itself and the return value of the function is discarded.
    • Creating a function without new the this points to the global scope (which in browsers will be the object window ) and the return value can be captured by anyone who calls the function.
  

How is a function passed by means of a parameter?

We have seen it two sections ago:

int funcion_que_recibe_una_funcion(int (*parametro)(int, int), int a, int b)
{
    return parametro(a, b);
}

Since C ++ is a language of strong typing , any parameter that points to a function needs to know all the types that are part of the function, that is: the type of return and the types of the parameters. Functions with different parameters and different types of return will have different types. So to create a variable of type function ( callback ) in C ++ we will do the following:

tipo_de_retorno (nombre_de_la_variable)( ... lista de parametros ... );

Therefore ...

// funcion que devuelve int y recibe dos int
int (a)(int, int);
// funcion que no devuelve ni recibe nada
void (b)();
// funcion que devuelve un string y recibe un puntero constante a char y un int
std::string (c)(const char *, int);

Normally this syntax is confusing, so we usually use typedef :

typedef int (i_ii)(int, int);
typedef void (v_v)();
typedef std::string (s_ci)(const char *, int);

int funcion_que_recibe_una_funcion(i_ii *parametro, int a, int b)
{
    return parametro(a, b);
}

void funcion_que_recibe_una_funcion(v_v *parametro)
{
    parametro();
}

std::string funcion_que_recibe_una_funcion(s_ci *parametro, const char *a, int b)
{
    return parametro(a, b);
}

Notice that we have added * to indicate that we are receiving a function pointer; On the other hand the typedef is usually considered confusing too, so the aliases were added from C ++ 11:

using i_ii = int(int, int);
using v_v = void();
using s_ci = std::string(const char *, int);

We can also use template aliases to make function pointers even simpler:

template <typename RETORNO, typename ... PARAMETROS>
using funcion = RETORNO(PARAMETROS ...);

int funcion_que_recibe_una_funcion(funcion<int, int, int> *parametro, int a, int b)
{
    return parametro(a, b);
}

void funcion_que_recibe_una_funcion(funcion<void> *parametro)
{
    parametro();
}

std::string funcion_que_recibe_una_funcion(funcion<std::string, const char *, int> *parametro, const char *a, int b)
{
    return parametro(a, b);
}

And we can also add the pointer directly to the typedef or alias:

typedef int (*p_i_ii)(int, int);
typedef void (*p_v_v)();
typedef std::string (*p_s_ci)(const char *, int);

using ap_i_ii = int(*)(int, int);
using ap_v_v = void(*)();
using ap_s_ci = std::string(*)(const char *, int);

template <typename RETORNO, typename ... PARAMETROS>
using puntero_a_funcion = RETORNO(*)(PARAMETROS ...);
  

When declaring the parameters of the function, what is its type?

The type of the parameters of the function does not change, a parameter of type T will be of type T always.

    
answered by 23.02.2017 / 09:57
source
3
  

In what cases are callback functions used?

There is no strict norm but rather it depends on each project. However, the graphic libraries usually use callbacks to launch the events in front of the user's actions (click on a button, pressed key, etc ...)

  

Do they work in a similar way as in javascript?

Generally speaking, NO. Think that JavaScript has a quite weak typing while in the case of C ++ the opposite happens. This has its implications since uses that can work in JS will not be viable in C ++ because the compiler is going to complain.

Of course there are always mechanisms to bypass these protections, but for the health of your program I do not recommend doing it unless you are very sure of where you are getting.

  

How is a function passed by means of a parameter?

Using C ++ old we could say that a function pointer has the following form:

[tipo de retorno] (*[nombre_variable])([parametros])

An example:

void func(int a, float b)
{
  std::cout << a << ' ' << b;
}

int main()
{
  void (*funcPtr)(int,float) = func;

  funcPtr(1,2.5);
}

And this same thing is repeated if instead of a variable it is an argument:

void func(int a, float b)
{
  std::cout << a << ' ' << b;
}

void funcionDelegada(void (*funcPtr)(int,float))
{
  funcPtr(1,2.5);
}

int main()
{
  funcionDelegada(func);
}

As you see the example starts to be difficult to read. In these cases it is best to declare an alias:

void func(int a, float b)
{
  std::cout << a << ' ' << b;
}

typedef void(*PunteroAFunc)(int,float);

void funcionDelegada(PunteroAFunc funcPtr)
{
  funcPtr(1,2.5);
}

int main()
{
  funcionDelegada(func);
}

However the syntax is still somewhat ugly. This changes from C ++ with the use of std::function (library functional :

void func(int a, float b)
{
  std::cout << a << ' ' << b;
}

using PunteroAFunc = std::function<void(int,float)>;

void funcionDelegada(PunteroAFunc funcPtr)
{
  funcPtr(1,2.5);
}

int main()
{
  funcionDelegada(func);
}

Another advantage of std::function is that it supports both lambdas and objects with the function operator:

int Suma(int a)
{
  return a + 5;
}

struct Acumulador
{
  static int acum;

  int operator()(int a)
  { return a + acum++; }
};

int Acumulador::acum = 1;

using PunteroAFunc = std::function<int(int)>;

int main()
{
  std::vector<PunteroAFunc> funciones
  {
    Acumulador(),                                        // 0 + 1 = 1
    Acumulador(),                                        // 1 + 2 = 3
    Suma,                                                // 3 + 5 = 8
    [](int a) { std::cout << a << std::endl; return a; } // Imprime 8
   };

  int a = 0;
  for( auto func : funciones )
  {
      a = func(a);
  }
}
  

When declaring the parameters of the function, what is its type?

This answer has already been answered implicitly but, to summarize: C ++ is characterized by a strong typing of the data. Everything must be perfectly typed so that the compiler admits it. This also affects function pointers, so both the type that represents that pointer and the types of its arguments must be perfectly defined in the source code.

Strong typing avoids many silly mistakes and, knowing how to take advantage of this feature, you can get a lot of juice out of the language.

    
answered by 23.02.2017 в 09:22
2

Functions callback is the artistic name. From the point of view of language, it is no more than a pointer to a function. And, like any other type of data, it is used when convenient. There is no specific place to use them.

A pointer to a function is defined like this:

TIPO_DEVUELTO ( *NOMBRE )( ARGUMENTOS... );

The qsort( ) function is an example of where function pointers are used:

void qsort( void *base, size_t nmemb, size_t size, int (*compar)( const void *, const void * ) );

The compar argument is a function pointer .

If you try to pass another type of data, the compiler will not leave you. You have to pass it exactly the indicated type:

int myCompar( const void *arg1, const void *arg2 ) {
  ...
  return 0;
}

qsort( arg1, arg2, &myCompar );

In C ++, there are also method pointers:

void (C::* fptr)( int );

that's an example of a member function pointer (method) of class C that receives a int as an argument and does not return anything.

Starting with C ++ 11, you have alternative mechanisms to use function pointers: class std::function :

template< class R, class... Args > class function<R(Args...)>

can be understood as a type of top level from pointer to function, capable of holding both pointers to normal functions, and pointers to member functions.

Also from C ++ 11, you can use lambda functions , which resemble Javascript's anonymous functions; Your explanation deserves a full article.

    
answered by 23.02.2017 в 06:44