Jak przechować slot zwracany przez boost:bind

0

Cześć, sytuacja wygląda tak. Mam jakąś metodę, którą podłączam do sygnału:

auto event = boost::bind(&AppManager::Impl::OnStart, this, _1);
cmdReg.OnStart.connect(event);

I później trzeba to rozłączyć:

auto event = boost::bind(&AppManager::Impl::OnStart, this, _1);
cmdReg.OnStart.disconnect(event);

Aż się prosi, żeby zmienna event była składnikiem klasy. Tylko w żaden sposób nie mogę tego ogarnąć. Próbowałem to deklarować jako:

boost::function<void(MyEventArgs&)> event;

Ale się wykrzacza przy budowaniu (czepia się o operator == gdzieś w boost.) Próbowałem to też deklarować jako slot. Ale wszystko na nic. Jest jakiś sposób na to?

2

Pokaż MRE, bo trywialny przykład jest ok https://godbolt.org/z/7Td3rb6Tz

#include<iostream>
#include <boost/bind.hpp>
#include <boost/function.hpp>

struct WithBoundField
{
    WithBoundField()
    : fkn(boost::bind(&WithBoundField::method, this, _1))
    {}
    
    int method(char a)
    {
        std::cout <<"passed " << a << " to method\n";
        return 0;
    };

    boost::function<int(char)> fkn;

};


int main(int, char*[])
{
    WithBoundField x;
    x.fkn('a');
    return 0;
}

BTW, skoro używasz auto to po co boost, jak masz std::function, std::bind itd. (bo to pewnie C++11)?

1

Bez MCVE ciężko powiedzieć, ale szklana kula każe sprawdzić, czy nie chcesz użyć std::ref albo boost::ref (o ile istnieje), bo bind robi kopię argumentów (w przeciwieństwie do np. lambdy).

0

Tylko dalej coś się nie składa co mówisz, bo connectuje ok:
https://godbolt.org/z/KbP8Pf9Gv

0

Pokazałem cały znaczący kod. Reszta:

struct MyEventArgs
{
	String cmdName; //string z zewnętrznej biblioteki
	bool handled;
};

typedef boost::signals2::signal<void(MyEventArgs&)> OnStartEvent;

class ICommandRegistrator
{
public:
  OnStartEvent OnStart;
}

Później w implementacji:

class CommandRegistrator: public ICommandRegistrator
{
public:
    void Start()
   {
       //Tworzę i wypełniam MyEventArgs, a potem
       OnStart(args);
   }
}

No i z drugiej strony podłączam tak, jak pokazałem wyżej.

0

O coś takiego Ci chodzi?

class CommandRegistrator: public ICommandRegistrator
{
public:
    boost::function<void(MyEventArgs&)> bfkn;

    CommandRegistrator()
    {
        bfkn = boost::bind(boost::ref(OnStart), _1);
    }
    void Start()
   {
       MyEventArgs args{"abcd", false};
    //    OnStart(args);
        bfkn(args);
   }
};
0

Nie, ja się pytam, jak to przechować w innej klasie. Kod z pierwszego postu pochodzi z zupełnie innej klasy, cmdReg jest obiektem CommandRegistrator

0

No tak samo. Funktor (a tym jest OnStart) może być "bound directly"

    CommandRegistrator cmdreg;
    boost::function<void(MyEventArgs&)> boundF;
    boundF = boost::bind(boost::ref(cmdreg.OnStart), _1);
2

@Juhas Ty zakładasz, że siedzimy w twojej głowie lub widzimy twój monitor.
Musisz zdać sobie sprawę, że my nie wiemy co robisz, jaki masz kod i na czym polega twój problem, ergo musisz dostarczyć wszystkie niezbędne informacje.

To zdanie:

Juhas napisał(a):

Aż się prosi, żeby zmienna event była składnikiem klasy. Tylko w żaden sposób nie mogę tego ogarnąć. Próbowałem to deklarować jako:

boost::function<void(MyEventArgs&)> event;

Ale się wykrzacza przy budowaniu (czepia się o operator == gdzieś w boost.) Próbowałem to też deklarować jako slot. Ale wszystko na nic. Jest jakiś sposób na to?

Dostarcz MCVE (minimalny kompletny weryfikowalny przykład) w którym odtwarzasz błąd z brakującym operatorem ==.
Jak nie potrafisz to wklej pełen log kompilatora (od pierwszego błędu do dołu), bo operator == gdzieś w boost to nie jest opis problemu, tylko zgłoszenie dla jasnowidza.

Coś takiego może być dobrym punktem startowym (próbowałem odtworzyć twój problem na podstawie dostarczonych danych, ale wszystko działa).

4

Dobra chyba mam MCVE: https://godbolt.org/z/6oEnqz36a
Po prostu jeden z argumentów, które są potrzebne dla boost::bind są nieporównywalne.
Ergo boost::bind nie jest w stanie wygenerować operatora porównania (równości).
Efekt końcowy jest taki, że próba wykonania disconnect z argumentem jest niemożliwa od wykonania, bo nie ma jak wykonać porównania na tym obiekcie przekazanym do disconnect, więc nie wiadomo, które połączenie należy zerwać.

W danych, które podałeś nie ma żadnego argumentu bind, którego nie da się porównywać. Wygląda na to, że jednak jestem jasnowidzem :).

1

OK, tu jest dokładnie to: https://godbolt.org/z/GKMrjdfbe

1

std::function oraz zwrotki z bind nie implementują interfejsu ==. Musisz napisać własny wrapper na std::function oraz zaimplementować ten operator. To tak na szybko. Jak będę mieć chwilę może popróbuję wieczorem coś wymóżdżyć.

1

No to teraz widać konkretnie w czym problem.
W twoim kodzie, kompilator narzeka, że operator porównania nie może być wybrany bo są możliwe dwie implementacje do wybrania.
Dziś nie mam czasu, żeby zrozumieć skąd dokładnie wzięła się ta dwuznaczność, nie jest to oczywiste.

2

Problem rozwiązany tak jak należy (a nie tak jak próbuje OP): https://godbolt.org/z/E68eYWKbq

class Handler
{
public:
    Handler(Source & source)
    {
        m_scoped_connection = source.OnStart.connect(boost::bind(&Handler::OnStart, this, _1));
    }

private:
  boost::signals2::scoped_connection m_scoped_connection;

  void OnStart(MyEventArgs& args)
  {
      std::cout << "Zaczelo sie...";
  }
};

I to kasując sporo kodu :).

Swoją drogą, będę jeszcze rozkminiał, czemu przykład od OP się nie kompiluje.

0

@MarekR22: zgaduję, :51:6: error: ambiguous overload for 'operator==' wskazuje Ci, że palnąłem głupotę odn. tego, że te typy nie są porównywalne. Jednakże spojrzyj do dokumentacji: https://www.boost.org/doc/libs/1_76_0/doc/html/function/faq.html#id-1.3.17.7.2.1

Comparison between boost::function objects cannot be implemented "well", and therefore will not be implemented.

...i dalej tłumaczą, że na skutek szczegółów implementacyjnych to się wysypie właśnie niejednoznaczności, a nie braku operatora.
Co ciekawe dalej piszą

The Signals library has a way around this.

Ciekawe czy signals2 też...

Przykład coby nie być gołosłownym:
https://godbolt.org/z/n3KvejrqW

0

Sorry za posta pod postem:
poczytałem dokładniej dokumentację boost::function i odn. operatora == (f i g to odp. lewy i prawy jego argument):

True when f stores an object of type Functor and one of the following conditions applies:
g is of type reference_wrapper<Functor> and f.target<Functor>() == g.get_pointer().
g is not of type reference_wrapper<Functor> and function_equals(*(f.target<Functor>()), g).

I wychodzi na to, że o ile samych boost::function porównać nie można, tak można porównać adresy ich bebechów, ergo można przekazać do connect/disconnect referencje (czyli tu: reference wrappery) na te funktory:

class Handler
{
public:
    Handler(Source & source)
      :m_source(source)
    {
        m_event = boost::bind(&Handler::OnStart, this, _1);
        m_source.OnStart.connect(boost::ref(m_event));
        assert(!m_source.OnStart.empty());
    }

    ~Handler()
    {
        m_source.OnStart.disconnect(boost::ref(m_event));
        assert(m_source.OnStart.empty());
    }

https://godbolt.org/z/7dsfdq9dT

1

A z wykorzystaniem boost::signals2::connection?
I trochę nazewnictwo poprawiłem ;). Bez przechowywania referencji do obiektu źródłowego, bez boost:ref.
https://godbolt.org/z/Wr73KsG9e

0

Miałem to kiedyś fajnie zrobione na pierwszych signalsach, i z tego co pamiętam signals2 wszystko utrudniały.
A teraz signals 1 już są chyba usunięte z nowych wersji Boosta :(

1 użytkowników online, w tym zalogowanych: 0, gości: 1