2012-12-26 12 views
7

(. Uwaga:Jak powinno już być jasne, ze znacznikami, to bezwzględnieC++ 03Tak, wiem, to wszystko sprawia, że ​​lambda ból odejdzie (i przynosi nowy rodzaje, założę się), ale jest to system wbudowany, z wersją systemu operacyjnego z lat 90., i powiedziano mi, że powinienem się cieszyć, że mam kompilator C++ 03 (GCC4.1.x, BTW) lub C++ Więc proszę, powstrzymajcie się od publikowania rozwiązań w C++ 11. Nie trzeba go wcierać, naprawdę.
Ponadto, std::bind(), std::function() itd. są oczywiście w wersji std::tr1, ale edytowałemPrefiks, ponieważ uważam, że dodaje do kodu głównie szum.
)Jak to zrobić z std :: bind?

Mam pewne coś podobnego do serwera, z którym muszę się zarejestrować i muszę je dostosować, aby wywoływać podobne, ale nieco inne funkcje obiektu. Te funkcje mają różne listy argumentów. Serwer "wie", że i kiedy próbuję zarejestrować funkcję, akceptuje tylko taką z prawidłowym sygnaturą (tak, jak to jest wymagane), w zależności od jakiegoś magicznego znacznika przekazanego jako argument szablonu.

Oto szkic kodu:

// this I just use 
class server { 
public: 
    template<unsigned int MagicTag> 
    bool register_call(typename some_traits_type<MagicTag>::func_type); 
}; 

// this needs to be called from the server 
class X { 
public: 
    bool foo(); 
    bool bar(std::string&); 
    bool baz(int); 
}; 

// this is the glue 
class Y { 
public: 
    Y(X& x) : x_(x) { 
     register_call<MAGIC_FOO>(&Y::foo ); 
     register_call<MAGIC_BAZ>(&Y::bar, _1); 
     register_call<MAGIC_FBZ>(&Y::baz, _1); 
    } 

private: 
    X&     x_; 

    template<unsigned int MagicTag, typename Function> 
    bool register_call(Function function) { 
     somewhere->register_call<MagicTag>(std::bind(function 
                , this)); 
    } 
    template<unsigned int MagicTag, typename Function, typename PlaceHolder1> 
    bool register_call(Function function, PlaceHolder1 place_holder1) { 
     somewhere->register_call<MagicTag>(std::bind(function 
                , this 
                , place_holder1)); 
    } 

    int foo()    {return x_.foo() ? MAGIC_OK : MAGIC_FAILED;} 
    int bar(std::string& s) {return x_.bar(s) ? MAGIC_OK : MAGIC_FAILED;} 
    int baz(int i)   {return x_.baz(i) ? MAGIC_OK : MAGIC_FAILED;} 
}; 

To faktycznie działa, ale w rzeczywistości istnieje sposób więcej funkcji i robi to jako żmudnych wysiłków copy'n'paste obraża moje poczucie godności i produkuje śmierdzący kod. Ponieważ wszystkie te funkcje robią dokładnie to samo, z tą różnicą, że są to funkcje, które wywołują i argumenty, które mają, lub nie mają, do przekazania, powinienem być w stanie je spakować w jedną sparametryzowaną funkcję, ukrywając różnice za std::bind(). W przeciwnym razie zdecydowałem się na pierwsze wykonanie tego dla wszystkich funkcji bez żadnych parametrów (jak w foo()), co jest przytłaczającą większością.

Więc chciałem trasie wszystkie połączenia z foo() -jak funkcji w X przez jedną funkcję Y::call_it, dokłada żmudny udział:

int call_it(std::function<bool()> f) {return f() ? MAGIC_OK : MAGIC_FAILED;} 

i wiązania odpowiedniej funkcji w X jako argument do niego:

register_call<MAGIC_FOO>(&X::foo); // note the X! 

// (this is wrong) 
somewhere->register_call<MagicCode>(std::bind(std::bind(&Y::call_it 
                 , this 
                 , function) 
               , std::ref(x_)); 

Oczywiście jest to błędne, podobnie jak wszystkie inne moje próby rozwiązania tego problemu. (Gram tylko z std::bind() przez 10 tygodni, więc proszę o zachowanie mnie). W końcu zagubiłam się w niesamowitym labiryncie zabawnych komunikatów o błędach z szablonowych wnętrzności, które mogą zmusić dorosłego mężczyznę do łez i przez co najmniej roku powinni karmić kurczęcia i jego dalszą rodzinę.

Tak więc przed I kill myself out of sheer frustration i sierotą moje dzieci - jak mogę to zrobić?

+0

Nigdy nie używałem std :: bind. Jak to się ma do spowiedzi. Ale nie chcesz 'register_call (std :: bind (& Y :: call_it, this, std :: bind (ptmf, std :: ref (x_)));'? – rici

+0

@rici: Tak, oczywiście, masz rację. [Już to widziałem] (http://chat.stackoverflow.com/transcript/10?m=6878122#6878122.) To po prostu, że waliłem głową w ścianę godzin, a to jest po prostu zdezorientowany stan w środku 1001. postaraj się, aby to działało, kiedy zdecydowałem, że muszę o to zapytać. Problem polega jednak na tym, że nie mogę go uruchomić w inny sposób , albo. _Sigh._ – sbi

+1

Chyba dlatego nigdy nie użyłem std :: bind. Metadebugging szablonów jest szalony, albo doprowadza cię do szaleństwa, co jest prawdopodobnie powodem, dla którego wszystkie standardowe implementacje bibliotek są poniżej standardu. Powodzenia! – rici

Odpowiedz

3

Z tego, co zbieram, chcę wywołać Y::call_it() z odpowiednio oprawionym obiektem std::function. Biorąc pod uwagę, że twoja wewnętrzna funkcja przyjmuje różne liczby argumentów, konieczne jest utworzenie generatora std::function<bool()> dla przypadków, w których przekazywane są dodatkowe argumenty.Zakładając, że obiekt X może być związany w czasie rejestracji, rejestracja funkcji składowej bez dodatkowych argumentów jest prosta:

template <int Magic, typename RC> 
void register_call(RC (X::*member)()) { 
    somewhere->register_call<Magic>(
     std::bind(&Y::call_it, this, 
      std::function<bool()>(std::bind(member, 
              std::ref(this->x_))))); 
} 

Po przejściu jeden lub więcej argumentów, konieczne jest stworzenie std::function<bool()> sprzeciwu po chwili rozmowy, ponieważ dodatkowy argument musi być związany. Nie sądzę, że można to zrobić bez funkcji pomocnika, ale można to zrobić z jednej funkcji pomocnika za liczbą argumentów:

template <typename RC, typename Arg0> 
static std::function<bool()> 
bind_argument(RC (X::*member)(Arg0), X& x, Arg0 const& arg0) { 
    return std::bind(member, std::ref(x), arg0); 
} 
template <int Magic, typename RC, 
      typename Arg0, typename PlaceHolder> 
void register_call(RC (X::*member)(Arg0), PlaceHolder pc) { 
    somewhere->register_call<Magic>(
     typename some_traits_type<Magic>::type(
      std::bind(&Y::call_it, this, 
         std::bind(&bind_argument<RC, Arg0>, member, 
           std::ref(this->x_), pc)))); 
} 

Funkcja pomocnika tworzy funkcję z dodatkowym argumentem jest związany. Zauważ, że funkcja, która jest związana, jest skonstruowana tak, aby była tego samego typu, co oczekiwana przez funkcję rejestru: Jest to konieczne, na przykład, aby utworzyć funkcję przyjmującą dodatkowe, ignorowane argumenty.

Poniżej znajduje się program testowy, którego użyłem do sprawdzenia, czy rzeczy się kompilują. Nie mam kompilatora C++ 2003 z TR1 na ręce i skompilowałem kod z kompilatorem C++ 2011. Jednak nie sądzę, użyłem żadnego z rozszerzenia C++ 2011, który nie jest dostępny z C++ 2003 z TR1.

#include <functional> 

enum { 
    MAGIC_OK, 
    MAGIC_FAILED, 
    MAGIC_FOO, 
    MAGIC_BAR, 
    MAGIC_FBZ, 
    MAGIC_BAZ 
}; 

template <int> struct server_traits; 
template <> struct server_traits<MAGIC_FOO> { 
    typedef std::function<bool()> type; 
}; 
template <> struct server_traits<MAGIC_BAR> { 
    typedef std::function<bool(std::string&)> type; 
}; 
template <> struct server_traits<MAGIC_FBZ> { 
    typedef std::function<bool(long)> type; 
}; 
template <> struct server_traits<MAGIC_BAZ> { 
    typedef std::function<bool(std::string, long)> type; 
}; 


// this I just use 
class server { 
public: 
    template<unsigned int MagicTag> 
    bool register_call(typename server_traits<MagicTag>::type) { 
     return true; 
    } 
}; 

server s; 
server* somewhere = &s; 

// this needs to be called from the server 
class X { 
public: 
    bool foo() { return true; } 
    bool bar(std::string&) { return true; } 
    bool baz(int) { return true; } 
}; 

// this is the glue 
class Y { 
public: 
    Y(X& x) : x_(x) { 
     register_call<MAGIC_FOO>(&X::foo ); 
     register_call<MAGIC_BAR>(&X::bar, std::placeholders::_1); 
     register_call<MAGIC_FBZ>(&X::baz, std::placeholders::_1); 
     register_call<MAGIC_BAZ>(&X::baz, std::placeholders::_2); 
    } 

private: 
    X& x_; 

    int call_it(std::function<bool()> f) { 
     return f() ? MAGIC_OK : MAGIC_FAILED; 
    } 

    template <int Magic, typename RC> 
    void register_call(RC (X::*member)()) { 
     somewhere->register_call<Magic>(
      std::bind(&Y::call_it, this, 
       std::function<bool()>(std::bind(member, 
               std::ref(this->x_))))); 
    } 
    template <typename RC, typename Arg0> 
    static std::function<bool()> 
    bind_argument(RC (X::*member)(Arg0), X& x, Arg0 const& arg0) { 
     return std::bind(member, std::ref(x), arg0); 
    } 
    template <int Magic, typename RC, 
       typename Arg0, typename PlaceHolder> 
    void register_call(RC (X::*member)(Arg0), PlaceHolder pc) { 
     somewhere->register_call<Magic>(
      typename server_traits<Magic>::type(
       std::bind(&Y::call_it, this, 
          std::bind(&bind_argument<RC, Arg0>, member, 
            std::ref(this->x_), pc)))); 
    } 
}; 

int main() 
{ 
    X x; 
    Y y(x); 
} 
+0

Dzięki, Dietmar! Że te wywołania do 'std :: bind()' nie gniazdują dobrze bez obsady, którą już znalazłem [na czacie] (http://chat.stackoverflow.com/transcript/10?m=6878274#6878274) . Nadal jednak walczę z kompilatorem nad funkcjami, biorąc argumenty, więc twoje rozwiązanie jest naprawdę bardzo pożądane. – sbi

+0

Niestety, nie mogę również skompilować kodu. Dla funkcji pobierającej 'std :: string &' kompilator nie wypowiada 'żadnej pasującej funkcji dla wywołania 'bind (, bool (X :: * &) (std :: string &), std :: reference_wrapper , std :: _ Placeholder <1> &) ". Są jeszcze dwa błędy.Wydaje się być zdenerwowany przez 'Arg0 const i arg0', który nie pasuje ani do' int', ani 'std :: string &' well. – sbi

+0

Z pewnością kompiluje dla mnie używanie gcc. Próba skompilowania kodu z clang kończy się niepowodzeniem z powodu błędu, który wydaje się być z wersji libcxx używam. Jednak spróbuj zmienić deklarację 'bind_argument()', aby usunąć 'const &' na parametrze 'Arg0' (" const & "jest pozostawieniem po próbie wyprowadzenia parametru i" Arg0 "jest poprawne wpisz tak. –

1

z linku kanału czat, wygląda jakbyś gotowane problemu się do faktu, że zagnieżdżona wiążą się nie kompiluje:

bind(&Y::call_it, this, bind(&X::foo, ref(x_))) 

ponieważ kompilator nie może wydedukować wpisz sygnaturę wewnętrznego wiązania() (jako funkcja < bool()> w tym przypadku). To może działać zamiast:

bind(&Y::call_it, this, function<bool()>(bind(&X::foo, ref(x_)))) 

a jeśli tak, to masz coś podobnego

template<unsigned int MagicTag, typename Function > 
bool register_call(Function func) { 
    somewhere->register_call<MagicTag>(
     bind(&Y::call_it, this, function<bool()>(bind(func, ref(x_))))); 
} 

chociaż mam wrażenie, że drugi parametr szablonu może nie być konieczne jakoś. Kluczową ideą jest umieszczenie funkcji std :: pomiędzy dwoma std :: bind.

Powiązane problemy