2012-06-14 9 views
16

Próbuję znaleźć sposób, w jaki sposób można przypisać wskaźnik funkcji do funkcji o różnej liczbie argumentów.C++: Wskaźnik funkcji do funkcji ze zmienną liczbą argumentów

Mam pętlę while, która przyjmuje wiele różnych funkcji jako instrukcję warunkową, więc zamiast pisać wiele pętli while z dokładnie tym samym kodem w środku, chciałbym mieć jeden ze wskaźnikiem funkcji. Wszystkie funkcje mają format bool f(...). Myślę, że jakiś kod najlepiej ilustruje, co mam na myśli:

int a, b, c, d; 
MyClass* my_class; 

typedef bool (MyClass::*my_fun_t)(); 
my_fun_t my_fun; 

if (condition1) 
    my_fun = &MyClass::function_one(); 
else if (condition2) 
    my_fun = &MyClass::function_two(a, b); 
else if (condition3) 
    my_fun = &MyClass::function_three(a, b, c); 
else if (condition4) 
    my_fun = &MyClass::function_four(a, b, c, d); 

while ((my_class->*my_fun)()) 
{ ... } 

To oczywiście nie działa, ponieważ funkcje mają różne podpisy. Czy to w ogóle możliwe, aby działał w podobny sposób? Czy funktoidy są czymś, na co powinienem patrzeć?

+0

Skąd wiesz, jak zastosować ten wskaźnik funkcji, jeśli nie wiesz, ile argumentów potrzeba? – mfontanini

+0

Zauważ, że twoja składnia przypisania do wskaźnika funkcji jest w każdym razie niepoprawna, wywołujesz * funkcje, przyjmujesz wynik, dostajesz wskaźnik do jego tymczasowego przechowywania i przypisujesz * to * do wskaźnika funkcji, który zadeklarowałeś wcześniej. –

+0

Tak, wiem, że nie jest to poprawna składnia. Po prostu pokazuje ten pomysł. – jaho

Odpowiedz

7

Można użyć std::function<> i std::bind().

#include <functional> 

using std::placeholders::_1; 

typedef std::function<bool(MyClass&)> my_fun_t; 
my_fun_t my_fun; 

if (condition1) 
    my_fun = std::bind(&MyClass::function_one, _1); 
else if (condition2) 
    my_fun = std::bind(&MyClass::function_two, _1, a, b); 
else if (condition3) 
    my_fun = std::bind(&MyClass::function_three, _1, a, b, c); 
else if (condition4) 
    my_fun = std::bind(&MyClass::function_four, _1, a, b, c, d); 

while (my_fun(my_class)) { ... } 

Zakładamy, że będziesz używać C++ 11. Jeśli nie możesz użyć C++ 11, ale możesz użyć TR1, zastąp wszystkie std:: przez std::tr1::. Istnieje również Boost implementation.

+1

Doskonale, dzięki. Nie mogę używać C++ 11. Jakiś powód, by preferować tr1 zamiast boost (którego używam w każdym razie)? – jaho

+1

@Marian TR1 jest wbudowany w standardową bibliotekę, doładowanie nie jest. – kennytm

1

Próbuję znaleźć sposób, w jaki sposób można przypisać wskaźnik funkcji do funkcji o różnej liczbie argumentów.

Nie możesz. Wskaźniki funkcji są specyficzne dla jednej funkcji podpisu. Jest to całkowicie logiczne: w jaki sposób powołać się na taką funkcję? (Tak, C umożliwia wywoływanie funkcji bez określania ich deklaracji ile parametrów funkcja ma, ale to nie działa w C++, ponieważ podważa system typu.)

Czy functoids coś mam patrzeć?

Ogólnie tak, ale nie rozwiązują tego problemu.

+0

Hmm, ale tylko "sygnatura" funkcji ze zmiennymi argumentami jest nadal unikalna, prawda? Część argumentów zmiennych "..." jest co najmniej kamuflażem "va_list" używanym w implementacji funkcji. Wywołanie takiej funkcji za pomocą wskaźnika funkcji nie powinno (ani nie powinno) różnić się od wywoływania takiej funkcji w zwykły sposób (ze zmienną listą argumentów). –

+0

@ g-makulik Zmienne argumenty działają, ale to konkretny podpis, to nie to samo, co powiązanie z różnymi sygnaturami funkcji. –

+0

Ahh, widzę twój punkt !. Ale OP nie wspomniał o wiążącym wydźwięku (może czytać, że jego "pseudo" kod jest nieprawidłowy). Wykorzystując definicje interfejsów (polimorfizm), nadal można osiągnąć to, co OP chce (zamierzone). –

3

Chcesz std::function, obiekt funkcji polimorficznej i std::bind do tworzenia obiektów funkcji przez powiązanie argumentów z parametrami innych funktorów.

Jeśli nie możesz użyć C++ 11, to boost::function i boost::bind są równoważne, choć nieco bardziej restrykcyjne.

std::function<bool()> my_fun; 

if (condition1) 
    my_fun = std::bind(&MyClass::function_one, my_class); 
else if (condition2) 
    my_fun = std::bind(&MyClass::function_two, my_class, a, b); 
else if (condition3) 
    my_fun = std::bind(&MyClass::function_three, my_class, a, b, c); 
else if (condition4) 
    my_fun = std::bind(&MyClass::function_four, my_class, a, b, c, d); 

while (my_fun()) 
{ ... } 
+1

Jeśli możesz używać C++ 11, możesz także użyć funkcji lambda i wyciągnąć zmienne z otaczającego zakresu leksykalnego. Jak sądzicie, cokolwiek wolicie. – user268396

+0

@ user268396: Prawdopodobnie. Na razie utknąłem na kompilatorze, który nie obsługuje lambdas, więc niewiele o nich wiem. –

+0

Rozwiązanie doładowania działa u mnie. Dziękuję Ci. – jaho

4

Działa to dla mnie:

#include <iostream> 
#include <cstdarg> 

using namespace std; 

class MyInterface 
{ 
public: 
    virtual bool func(int argc, ...) = 0; 
}; 

class MyImpl : public MyInterface 
{ 
public: 
    virtual bool func(int argc, ...); 
}; 

bool MyImpl::func(int argc, ...) 
{ 
    va_list varargs; 
    va_start(varargs,argc); 
    cout << "Arguments passed:" << endl; 
    for(int i = 0; i < argc; ++i) 
    { 
     // expect double values 
     double val = va_arg(varargs,double); 
     cout << val << endl; 
    } 
    va_end(varargs); 
    return true; 
} 

typedef bool (MyInterface::*MyFunc)(int, ...); 

int main() { 

    MyImpl impl; 
    MyInterface* interface = &impl; 
    MyFunc pfunc = &MyInterface::func; 

    if(!(interface->*pfunc)(2,double(3.14),double(2.72))) 
    { 
     return 1; 
    } 
    return 0; 
} 

wyjściowa:

Arguments passed: 
3.14 
2.72 

Oczywiście można zadeklarować i użyć wskaźników funkcji dla (member-) funkcji przy użyciu zmiennych argumentów.

Powiązane problemy