Jak działa parametr szablonu funkcji std::? (realizacja)

In Bjarne Stroustrup 's home page (C++11 FAQ):

struct X { int foo(int); };

std::function<int(X*, int)> f;
f = &X::foo; //pointer to member

X x;
int v = f(&x, 5); //call X::foo() for x with 5
Jak to działa? W jaki sposób std::function wywołuje Foo member function?

Parametr szablonu to int(X*, int), Czy &X::foo jest konwertowany z wskaźnika funkcji member do wskaźnika funkcji non-member ?!

(int(*)(X*, int))&X::foo //casting (int(X::*)(int) to (int(*)(X*, int))

Dla wyjaśnienia: wiem, że nie musimy rzucać żadnego wskaźnika, aby użyć std::function, ale nie wiem, w jaki sposób wewnętrzne std:: function obsługuje tę niezgodność pomiędzy wskaźnikiem funkcji członkai wskaźnikiem funkcji nieczłonkowej. Nie wiem jak standard pozwala nam zaimplementować coś w rodzaju std:: function !

Author: Gyorgy Szekely, 2010-08-21

4 answers

Dzięki za wszystkie odpowiedzi.

Znalazłem dobry przykład z sekcji 14.8.2.5-21 standardu:

template<class> struct X { };
template<class R, class ... ArgTypes> struct X<R(int, ArgTypes ...)> { };
template<class ... Types> struct Y { };
template<class T, class ... Types> struct Y<T, Types& ...> { };
template<class ... Types> int f(void (*)(Types ...));
void g(int, float);
// uses primary template
X<int> x1;
// uses partial specialization; ArgTypes contains float, double
X<int(int, float, double)> x2;
// uses primary template
X<int(float, int)> x3;
// use primary template; Types is empty
Y<> y1;
// uses partial specialization; T is int&, Types contains float, double
Y<int&, float&, double&> y2;
// uses primary template; Types contains int, float, double
Y<int, float, double> y3;
// OK; Types contains int, float
int fv = f(g);

Mówi, że dzięki specjalizacji szablonów możemy analizować parametry szablonu typu funkcji (super)! Poniżej znajduje się brudny/prosty przykład na temat tego, jak może działać std::function:

template<class T> struct Function { };

template<class T, class Obj, class... Args>
struct Function<T(Obj*, Args...)> // Parsing the function type
{
    enum FuncType
    {
        FuncTypeFunc,
        FuncTypeMemFunc
    };
    union FuncPtr
    {
        T(*func)(Obj*, Args...);
        T(Obj::*mem_func)(Args...);
    };

    FuncType m_flag;
    FuncPtr m_func_ptr;

    Function(T(*func)(Obj*, Args...)) // void(*)(Funny*, int, int)
    {
        m_flag = FuncTypeFunc;
        m_func_ptr.func = func;
    }
    Function(T(Obj::*mem_func)(Args...)) // void(Funny::*)(int, int)
    {
        m_flag = FuncTypeMemFunc;
        m_func_ptr.mem_func = mem_func;
    }

    void play(Obj* obj, Args... args)
    {
        switch(m_flag)
        {
          case FuncTypeFunc:
            (*m_func_ptr.func)(obj, args...);
            break;
          case FuncTypeMemFunc:
            (obj->*m_func_ptr.mem_func)(args...);
            break;
        }
    }
};

Użycie:

#include <iostream>

struct Funny
{
    void done(int i, int j)
    {
        std::cout << "Member Function: " << i << ", " << j << std::endl;
    }
};

void done(Funny* funny, int i, int j)
{
    std::cout << "Function: " << i << ", " << j << std::endl;
}

int main(int argc, char** argv)
{
    Funny funny;
    Function<void(Funny*, int, int)> f = &Funny::done; // void(Funny::*)(int, int)
    Function<void(Funny*, int, int)> g = &done; // void(*)(Funny*, int, int)
    f.play(&funny, 5, 10); // void(Funny::*)(int, int)
    g.play(&funny, 5, 10); // void(*)(Funny*, int, int)
    return 0;
}

Edit: dzięki Tomkowi za dobrą podpowiedź o unions, powyższy przykład został zmieniony na funkcję member/non-member wskaźnik w jednej (Nie dwóch) zmiennej.


Edit: Martin York ma rację, deklaracja switch nie była dobrym pomysłem w powyższym przykładzie, więc całkowicie zmieniłem przykład, aby działał lepiej:

template<class T> class Function { };

template<class Res, class Obj, class... ArgTypes>
class Function<Res (Obj*, ArgTypes...)> // Parsing the function type
{
    union Pointers // An union to hold different kind of pointers
    {
        Res (*func)(Obj*, ArgTypes...); // void (*)(Funny*, int)
        Res (Obj::*mem_func)(ArgTypes...); // void (Funny::*)(int)
    };
    typedef Res Callback(Pointers&, Obj&, ArgTypes...);

    Pointers ptrs;
    Callback* callback;

    static Res call_func(Pointers& ptrs, Obj& obj, ArgTypes... args)
    {
        return (*ptrs.func)(&obj, args...); // void (*)(Funny*, int)
    }
    static Res call_mem_func(Pointers& ptrs, Obj& obj, ArgTypes... args)
    {
        return (obj.*(ptrs.mem_func))(args...); // void (Funny::*)(int)
    }

  public:

    Function() : callback(0) { }
    Function(Res (*func)(Obj*, ArgTypes...)) // void (*)(Funny*, int)
    {
        ptrs.func = func;
        callback = &call_func;
    }
    Function(Res (Obj::*mem_func)(ArgTypes...)) // void (Funny::*)(int)
    {
        ptrs.mem_func = mem_func;
        callback = &call_mem_func;
    }
    Function(const Function& function)
    {
        ptrs = function.ptrs;
        callback = function.callback;
    }
    Function& operator=(const Function& function)
    {
        ptrs = function.ptrs;
        callback = function.callback;
        return *this;
    }
    Res operator()(Obj& obj, ArgTypes... args)
    {
        if(callback == 0) throw 0; // throw an exception
        return (*callback)(ptrs, obj, args...);
    }
};

Użycie:

#include <iostream>

struct Funny
{
    void print(int i)
    {
        std::cout << "void (Funny::*)(int): " << i << std::endl;
    }
};

void print(Funny* funny, int i)
{
    std::cout << "void (*)(Funny*, int): " << i << std::endl;
}

int main(int argc, char** argv)
{
    Funny funny;
    Function<void(Funny*, int)> wmw;

    wmw = &Funny::print; // void (Funny::*)(int)
    wmw(funny, 10); // void (Funny::*)(int)

    wmw = &print; // void (*)(Funny*, int)
    wmw(funny, 8); // void (*)(Funny*, int)

    return 0;
}
 31
Author: M. Sadeq H. E.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-09 23:00:56

Jak to robi to (moim zdaniem) pozostaje niezdefiniowany (ale nie mam tu kopii standardu).

Ale biorąc pod uwagę wszystkie różne możliwości, które należy objąć, mam wrażenie, że rozszyfrowanie dokładnej definicji tego, jak to działa, byłoby naprawdę trudne: więc nie zamierzam próbować.

Ale myślę, że chciałbyś wiedzieć, jak działają funktory i są stosunkowo proste. Oto szybki przykład.

Funktory:

Są to obiekty, które zachowują się jak funkcje.
Są one bardzo przydatne w kodzie szablonu, ponieważ często pozwalają na wymienne używanie obiektów lub funkcji. Wspaniałą rzeczą w funkcjach jest jednak to, że mogą utrzymać stan (coś w rodzaju zamknięcia biednego człowieka).

struct X
{
     int operator()(int x) { return doStuff(x+1);}
     int doStuff(int x)    { return x+1;}
};

X   x;  // You can now use x like a function
int  a = x(5);

Możesz użyć faktu, że funktor hold state przechowuje takie rzeczy jak parametry, obiekty lub wskaźnik do metod member (lub dowolna ich kombinacja).

struct Y // Hold a member function pointer
{
    int (X::*member)(int x);
    int operator(X* obj, int param) { return (obj->*member)(param);}
};
X  x;
Y  y;
y.member = &X::doStuff;
int a = y(&x,5);

Lub nawet pójść dalej i powiązać parametry. Więc teraz wszystko, czego potrzebujesz, to jeden z parametry.

struct Z
{
    int (X::*member)(int x);
    int  param;
    Z(int (X::*m)(int), int p) : member(m), param(p) {}

    int operator()(X* obj)  { return (obj->*member)(param);}
    int operator()(X& obj)  { return (obj.*member)(param);}
};

Z z(&X::doStuff,5);

X x;
int a = z(x);
 3
Author: Martin York,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-15 09:56:33

G++ wydaje się mieć związek, który może przechowywać wskaźnik funkcji, wskaźnik członka lub wskaźnik void, który prawdopodobnie wskazuje na funktor. Dodaj przeciążenia, które odpowiednio znaczą, który członek Związku jest ważny i ciężki rzut do zupy, a następnie działa...

 2
Author: Tomek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-20 21:34:14

Nie są wskaźnikami funkcji. Do tego istnieje STD:: function. Owija wszystkie typy, które mu podasz. Powinieneś sprawdzić boost:: bind-jest on często używany do wywoływania wskaźników funkcji member jako (this, args).

 1
Author: Puppy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-20 21:31:53