Использование boost::signals2::trackable с лямбдами

Я использую такой шаблон, С++ 11:

class FooViewController {
    void build() {
        auto label = ...

        network->doWork([] (const Result& r) {
             label->setText(r.text);
        });
    }
}

FooViewController может деконструироваться до завершения doWork, вызывая сбои. Глядя на boost::signals2, я думаю об использовании boost::signals2::trackable, который отлично работает для моих однопоточных целей, с тем преимуществом, что мне не нужно удерживать и управлять своими соединениями напрямую, однако я не уверен, как получить такой решение, работающее с лямбдами.

Вот рабочая лямбда-бесплатная версия:

class Foo : public boost::signals2::trackable {
public:
    void bar() {
        printf("Fire!");
    }
};


Usage:

    boost::signals2::signal<void()> signal;
    {
        Foo test;
        signal.connect(boost::bind(&Foo::bar, &test));
        signal();
    }
    signal();

Output:

    Fired!
    // Note a second 'Fired!' did not occur, which is correct behavior

Две цели:

1-- Я хотел бы сделать что-то вроде:

signal.connect(boost::bind([] {
    printf("Fired!");
}, &test));

Который НЕ будет вызывать лямбду после того, как test будет снесен.

2-- Я не хочу напрямую управлять объектами подключения, возвращаемыми .connect.


person fionbio    schedule 17.08.2015    source источник
comment
Честно говоря, я не знаю, что лямбда покупает вам, когда у вас есть bind() вокруг нее. Можете ли вы просто использовать trackable, так как это все еще связанный первый параметр?   -  person sehe    schedule 18.08.2015
comment
Я копаю лямбды для захватов. Привязка появлялась по мере необходимости при использовании отслеживаемого. В идеале, smart_ptrs и track были бы естественными для signal2 (отслеживание устарело), ​​но в приложении существует существующий механизм пула ссылок/авторелиза. Изучение следующих шагов.   -  person fionbio    schedule 18.08.2015


Ответы (3)


Как видно здесь: "Использование отслеживаемого класса не рекомендуется для нового кода"

Возможно, вместо этого выберите scoped_connection или track.

Пример:

#include <iostream>
#include <memory>

#include <boost/signals2.hpp>


struct short_lived : public boost::signals2::scoped_connection {
public:
   short_lived(const boost::signals2::connection &conn) : boost::signals2::scoped_connection{conn}
   {   }
   ~short_lived() {
      std::cout << "I'm dying...1!" << std::endl;
   }

};

int main() {
   typedef boost::signals2::signal<void()> sig_type;
   sig_type s1;

   {
      /* boost::signals2::scoped_connection */ short_lived conn{s1.connect([]() {
                  std::cout << "Fire1!" << std::endl;
               })};
      s1();
   }
   s1();
   std::cout << std::endl;

   {
      auto lam = []() {
         std::cout << "Fire2!" << std::endl;
      };

      /* shared_ptr with custom deleter that does not delete (since we didn't use new),
         but prints a message */
      std::shared_ptr<decltype(lam)> sptr{&lam, [](decltype(lam) *) { std::cout << "I'm dying...2!" << std::endl; }};
      s1.connect(sig_type::slot_type(lam).track_foreign(sptr));
      s1();
   }
   s1();

   return 0;
}

http://melpon.org/wandbox/permlink/c8LHGIp8ArkKsnWA

person ajneu    schedule 18.11.2015
comment
Хорошо - однако нужно держаться за shared_ptrs, чтобы функции продолжали жить. - person fionbio; 19.11.2015

Вы можете написать что-то подобное, используя общие/слабые указатели:

Жить на Coliru

#include <boost/signals2.hpp>
#include <boost/make_shared.hpp>
#include <boost/weak_ptr.hpp>
#include <iostream>

class Foo : public boost::signals2::trackable {
public:
    void bar() {
        printf("Fire!\n");
    }
};

int main() {
    boost::signals2::signal<void()> signal;
    {
        auto test = boost::make_shared<Foo>();

        signal.connect([wp = boost::weak_ptr<Foo>(test)] 
                { 
                    if (auto sp = wp.lock()) 
                        sp->bar(); 
                    else
                        std::cout << "expired\n";
                }
            );

        signal();
    }
    signal();
}

Отпечатки

Fire!
expired

Строго версия C++11: Live On Coliru

person sehe    schedule 17.08.2015
comment
Спасибо за это @sehe. Для интеллектуальных указателей я бы определенно использовал это или track, однако объекты в этом приложении в настоящее время управляются через настраиваемый объект с подсчетом ссылок для использования в пуле автоматического освобождения. Я думаю, что ищу решение для подделки моего собственного smart_ptr для использования в треке, где кишки будут правильно взаимодействовать с существующим реф-трекером/пулом. Еще не совсем ясно. Все еще смотрит... - person fionbio; 18.08.2015

Нашел ответ со ссылкой на trackable_test.cpp:

struct short_lived : public boost::signals2::trackable {
    ~short_lived() {
        cout << "I'm dying...!" << std::endl;
    }
};

void main() {
    typedef boost::signals2::signal<void()> sig_type;
    sig_type s1;

    short_lived* shorty = new short_lived();
    s1.connect(boost::bind<void>([](const short_lived*) {
        cout << "Fire!" << std::endl;
    }, shorty));
    s1();
    delete shorty;

    s1();
}

Выход

Fire!
I'm dying...!

... и пример с несколькими параметрами (boost::bind Refresh):

typedef boost::signals2::signal<void(int)> sig_type;

// ...same...

s1.connect(boost::bind<void>([](const short_lived*, int cannon) {
    cout << "Fire " << cannon << "!" << std::endl;
}, shorty, _1));
s(1);
delete shorty;
s(2);

Выход

Fire 1!
I'm dying...!
person fionbio    schedule 18.08.2015