Слабое связывание с использованием c ++ 17 - PullRequest
0 голосов
/ 29 апреля 2018

Я работаю над платформой обработки, где обратные вызовы регистрируются для событий, и чтобы гарантировать, что обратный вызов не вызывается для объекта, который был удален, я хотел бы использовать слабый захват, а не захват по ссылке. Работать с использованием C++14 и shared_from_this() не составило труда, но как правильно это сделать, используя C++17 и weak_from_this().

Приведенный ниже пример ничего не печатает при использовании C++17. Я использую g ++ 6.3.0-18

#define CXX17  // When this is defined, nothing is printed
#ifdef CXX17
# include <experimental/memory>
# include <experimental/functional>
  template <typename T>
  using enable_shared_from_this = std::experimental::enable_shared_from_this<T>;
#else
# include <memory>
# include <functional>
  template <typename T>
  using enable_shared_from_this = std::enable_shared_from_this<T>;
#endif

#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <iostream>

struct A : enable_shared_from_this<A> {
  int a;
  A() : a(7) {}
  auto getptr() {
#ifdef CXX17
    return this->weak_from_this();
#else
    auto sptr = shared_from_this();
    auto wptr = std::weak_ptr<decltype(sptr)::element_type>(sptr);
    sptr.reset();  // Drop strong referencing
    return wptr;
#endif
  }
};

std::condition_variable condition;
std::mutex mutex;
std::atomic<bool> start0{false};
std::atomic<bool> start1{false};

std::shared_ptr<A> g_a;

static void thread_func0() {
  auto w_a = g_a->getptr();

  std::unique_lock<std::mutex> lock {mutex};
  condition.wait(lock, [&]() {
    return start0.load();
  });
  std::this_thread::sleep_for(std::chrono::microseconds(10));
  if (auto t = w_a.lock()) {
    std::cout << t->a << std::endl;
  }
}

static void thread_func1() {
  std::unique_lock<std::mutex> lock {mutex};
  condition.wait(lock, [&]() {
      return start1.load();
    });
  std::this_thread::sleep_for(std::chrono::microseconds(10000));
  g_a = nullptr;
}

int main() {
  g_a = std::make_shared<A>();

  std::thread thread0(thread_func0);
  std::thread thread1(thread_func1);

  start0 = true;
  start1 = true;
  condition.notify_all();

  thread0.join();
  thread1.join();

  return 0;
}

1 Ответ

0 голосов
/ 29 апреля 2018

Вот более упрощенный пример:

#include <experimental/memory>
#include <iostream>

template <typename T>
using enable_shared_from_this = std::experimental::enable_shared_from_this<T>;

struct A : enable_shared_from_this<A> {
  int a;
  A() : a(7) {}
};

int main() {
    auto sp = std::make_shared<A>();

    auto wp = sp->weak_from_this();
    if (auto s = wp.lock()) {
        std::cout << s->a << std::endl;
    }
}

Это ничего не печатает. Зачем? В конечном счете, причина заключается в том, что это std::enable_shared_from_this, а не какой-то другой тип, который вы сами можете предоставить: класс shared_ptr должен включить эту функцию. Новая функциональность является экспериментальной, поэтому std::shared_ptr не включался - поэтому базовый weak_ptr никогда не инициализировался. Такого просто не бывает, поэтому wp здесь всегда "пусто" weak_ptr.

С другой стороны, std::experimental::shared_ptr включает подписку на эту функцию. Вам нужно использовать shared_ptr, соответствующий вашему enable_shared_from_this - std::experimental::shared_ptr.

Там нет std::experimental::make_shared (или, по крайней мере, насколько я мог найти), но механизм согласия в любом случае не основан на этом - он просто основан на любой конструкции shared_ptr. Так что если вы измените:

auto sp = std::make_shared<A>();

до:

auto sp = std::experimental::shared_ptr<A>(new A);

Тогда механизм согласия совпадает с типом shared_ptr и делает все правильно, вы получаете действительные weak_ptr (std::experimental::weak_ptr), lock() дает вам совместное владение базовым A и программа печатает 7.

...