23 Temmuz 2019 Salı

Function Pointer

Giriş
C++ ile iki çeşit function pointer var. İlki global bir fonksiyona pointer. Bu klasik C ile aynı. İkincisi ise Function Pointer to Member Method. Yani bir sınıfın/struct'ın içinde tanımlı metoda pointer.

Function Pointer Tanımlama
Function Pointer tanımlarken * (asteriks) kullanmak zorunlu değil. Açıklaması şöyle.
16.1 Overloadable declarations [over.load]
(3.3) Parameter declarations that differ only in that one is a function type and the other is a pointer to the same function type are equivalent. That is, the function type is adjusted to become a pointer to function type (11.3.5).
Örnek
Şöyle yaparız
double fin_diff(double f(double), double x, double h  = 0.01) {
  return (f(x+h)-f(x)) / h;
}
veya şöyle yaparız
double fin_diff(double (*f)(double), double x, double h  = 0.01);
Örnek
Elimizde şöyle bir kod olsun.
void (*fptr)() = f;
void (*fptr)() = &f;
void (*fptr)() = &&f;
void (*fptr)() = &&&f;
Şöyle yaparız.
fptr();
(*fptr)();
(**fptr)();
(***fptr)();
Function Pointer Object Değildir
Açıklaması şöyle.
An object is a region of storage. [ Note: A function is not an object, regardless of whether or not it occupies storage in the way that objects do. —end note ]
Aralarındaki farklardan birisi şöyle. Object'e pointer void* haline getirilebilir ve tekrar Object'e pointer yapılabilir. Function pointer için bu kural geçerli değil. Açıklaması şöyle
A lot of the difference comes down to pointers and addressing. In C++¹ pointers to functions and pointers to objects are strictly separate kinds of things.

C++ requires that you can convert a pointer to any object type into a pointer to void, then convert it back to the original type, and the result will be equal to the pointer you started with. In other words, regardless of exactly how they do it, the implementation has to ensure that a conversion from pointer-to-object-type to pointer-to-void is lossless, so no matter what the original was, whatever information it contained can be recreated so you can get back the same pointer as you started with by conversion from T* to void * and back to T*.

That's not true with a pointer to a function though--if you take a pointer to a function, convert it to void *, and then convert it back to a pointer to a function, you may lose some information in the process. You might not get back the original pointer, and dereferencing what you do get back gives you undefined behavior (in short, don't do that).

For what it's worth, you can, however, convert a pointer to one function to a pointer to a different type of function, then convert that result back to the original type, and you're guaranteed that the result is the same as you started with.
Function Pointer'ın Başka Başka Function Pointer'a Cast Edilmesi
Açıklaması şöyle
While you can cast function pointers to other function pointers and back, calling a function through a pointer that doesn't match its signature is undefined behavior. 
Function Pointer To Overloaded Method
Açıklaması şöyle.
static_cast may also be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type
Örnek
Elimizde şöyle bir kod olsun.
int (*fun)(int);
Şu kod derlenmez.
fun = (1 ? std::toupper : std::tolower);   // ERROR, invalid overload
Şöyle yaparız.
chr2fun = (str2modus == STR2UP ? static_cast<int(*)(int)>(std::toupper) 
                               : static_cast<int(*)(int)>(std::tolower));
Function Pointer To Global Method
Function Pointer To Global Method yazısına taşıdım.

Function Pointer to Member Method
Klasik function pointer ile aynı şekilde tanımlanır.

typedef return_type (classname::*methodname)(methodparameters)
Klasik function pointer'dan farklı olarak .* veya ->* şeklinde çağırılır. Şöyle yaparız
(d.*f)(5);  // for objects or references
(p->*f)(5); // for pointers
1. Parametre almayan Member Function Pointer 
Şöyle yaparız
struct foo
{
  int x() { return 5; }
};

int (foo::*ptr)() = &foo::x;
foo myFoo;
cout << (myFoo.*ptr)() << '\n'; // prints "5"
2. Tek parametre alan Member Function Pointer
Şöyle yaparız
#include <iostream>
using namespace std;

class D {
public:
  void foo ( int a ) {
    cout << "D" << endl;
  }

  int data;
};

typedef void ( D::*  Func)(int);

int main ( void ) 
{
  D d;

  Func f = &D::foo;
  f(&d, 5);

  return 1;
}
3. İki Parametre Alan Member Function Pointer
Şöyle yaparız.
#include <iostream>

using namespace std;
class ABC{
private:
  int x =3;
  int add2num(int a, int b){
    return a+b+x;
  }

  int worker(int (ABC::*fun)(int a, int b), int a, int b){
    return (this->*fun)(a,b);
  }
public:
  int doSomething(int a, int b){
    return worker(&ABC::add2num, a, b);
  }
};

int main() {
  ABC test;
  cout << test.doSomething(3,5) << endl;
  return 0;
}
Karmaşık Tanımlamalar
Karmaşık Tanımlamalar - Function Pointer yazısına taşıdım.

Modern C++11 ile Template Alias ve Variadic Templates
Bence çok karışık bir kod ama şöyle de yapılabilir. Önce template bir fuction pointer yaratılır
template <typename R, typename ...ARGS> using function = R(*)(ARGS...);
Daha sonra functionlar tanımlanır.
void foo()                { ... }
int bar(int)              { ... }
double baz(double, float) { ... }

int main()
{
  function<void>                  f1 = foo;
  function<int, int>              f2 = bar;
  function<double, double, float> f3 = baz;

  f1(); f2({}); f3({}, {});
  return 0;
}
Overload'lar ile de çalışıyor.
void overloaded(int)      { std::cout << "int version\n"; }
void overloaded(double)   { std::cout << "double version\n"; }

int main()
{
  function<void, int>    f4 = overloaded;
  function<void, double> f5 = overloaded;

  f4({}); // int version
  f5({}); // double version
  return 0;
}

Hiç yorum yok:

Yorum Gönder