std :: async с шаблонными функциями-членами с указателем общего доступа - PullRequest
1 голос
/ 09 июля 2019

Я работаю над реализацией многопоточности в проекте, но наталкиваюсь на стену, когда речь заходит о некоторых более сложных применениях std :: async. Я хочу вызвать функцию-член на шаблонном объекте и передать аргумент в качестве параметра, но не могу заставить его работать, когда шаблон содержит общий указатель.

Довольно просто использовать std :: async для функций-членов и даже шаблонных функций-членов. Я видел много ответов о переполнении стека для этого конкретного использования. Я даже сам запускал несколько тестов:

#include <thread>
#include <future>
#include <iostream>

class Bar
{
  public:
    Bar () {}
    double data;
};

template <typename T>
class Foo
{
  public:
    Foo () {}
    T set_data (T d) { data = d; return data; }
  private:
    T data;
};

#include "./foo.h"
#include <memory>

int main (int argc, char **argv)
{
  /**
   * Works fine
   */
  Foo<int> foo1;
  auto fut1 = std::async(std::launch::async, &Foo<int>::set_data, &foo1, 42);

  fut1.wait();
  std::cout << fut1.get() << std::endl;

  return 0;
}

Этот пример прекрасно компилируется в gcc 7.4.0 и возвращает 42, как и ожидалось.

Моя проблема возникает, когда я использую shared_ptr в шаблоне. Используя те же классы Foo и Bar сверху:

#include "./foo.h"
#include <memory>

int main (int argc, char **argv)
{
  /**
   * Doesn't work
   */
  auto foo2 = std::make_shared<Foo<std::shared_ptr<Bar>>>;
  auto br = std::make_shared<Bar>;
  auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data, &foo2, bar);

  fut2.wait();
  std::cout << fut2.get()->data << std::endl;

  return 0;
}

Я получаю эту ошибку при компиляции g++ -pthread test.cpp -o test

test.cpp: In function ‘int main(int, char**)’:
test.cpp:20:94: error: no matching function for call to ‘async(std::launch, std::shared_ptr<Bar> (Foo<std::shared_ptr<Bar> >::*)(std::shared_ptr<Bar>), Foo<std::shared_ptr<Bar> >*, std::shared_ptr<Bar> (*&)())’
   auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data, &foo2, bar);
                                                                                              ^
In file included from ./foo.h:2:0,
                 from test.cpp:1:
/usr/include/c++/7/future:1712:5: note: candidate: template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_Args>::type ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...)
     async(launch __policy, _Fn&& __fn, _Args&&... __args)
     ^~~~~
/usr/include/c++/7/future:1712:5: note:   template argument deduction/substitution failed:
/usr/include/c++/7/future: In substitution of ‘template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_Args>::type ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...) [with _Fn = std::shared_ptr<Bar> (Foo<std::shared_ptr<Bar> >::*)(std::shared_ptr<Bar>); _Args = {Foo<std::shared_ptr<Bar> >*, std::shared_ptr<Bar> (*&)()}]’:
test.cpp:20:94:   required from here
/usr/include/c++/7/future:1712:5: error: no type named ‘type’ in ‘class std::result_of<std::shared_ptr<Bar> (Foo<std::shared_ptr<Bar> >::*(Foo<std::shared_ptr<Bar> >*, std::shared_ptr<Bar> (*)()))(std::shared_ptr<Bar>)>’
/usr/include/c++/7/future:1745:5: note: candidate: template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_Args>::type ...)>::type> std::async(_Fn&&, _Args&& ...)
     async(_Fn&& __fn, _Args&&... __args)
     ^~~~~
/usr/include/c++/7/future:1745:5: note:   template argument deduction/substitution failed:
/usr/include/c++/7/future: In substitution of ‘template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_Args>::type ...)>::type> std::async(_Fn&&, _Args&& ...) [with _Fn = std::launch; _Args = {std::shared_ptr<Bar> (Foo<std::shared_ptr<Bar> >::*)(std::shared_ptr<Bar>), Foo<std::shared_ptr<Bar> >*, std::shared_ptr<Bar> (*&)()}]’:
test.cpp:20:94:   required from here
/usr/include/c++/7/future:1745:5: error: no type named ‘type’ in ‘class std::result_of<std::launch(std::shared_ptr<Bar> (Foo<std::shared_ptr<Bar> >::*)(std::shared_ptr<Bar>), Foo<std::shared_ptr<Bar> >*, std::shared_ptr<Bar> (*)())>’

Я подумал, что это может быть из-за того, что ссылка & не работала в правильном порядке со всеми этими угловыми скобками для шаблонов, поэтому я попытался использовать несколько скобок:

#include "./foo.h"
#include <memory>

int main (int argc, char **argv)
{
  /**
   * Doesn't work
   */
  Foo<std::shared_ptr<Bar>> foo2;
  Bar bar;
  auto fut2 = std::async(std::launch::async, &(Foo<std::shared_ptr<Bar>>::set_data), &foo2, bar);

  fut2.wait();
  std::cout << fut2.get().data << std::endl;

  return 0;
}

Что приводит к более короткой ошибке, которую я тоже не понимаю.

test.cpp: In function ‘int main(int, char**)’:
test.cpp:20:75: error: invalid use of non-static member function ‘T Foo<T>::set_data(T) [with T = std::shared_ptr<Bar>]’
   auto fut2 = std::async(std::launch::async, &(Foo<std::shared_ptr<Bar>>::set_data), &foo2, bar);

Я смущен, почему общие указатели вдруг имеют значение, и я предполагаю, что это связано с выводом типа? Любая помощь приветствуется.

EDIT

Спасибо тем, кто откликнулся, вот решение. Скобки не обязательны, а некоторые shared_ptrs отсутствуют.

#include "./foo.h"
#include <memory>

int main (int argc, char **argv)
{
  Foo<std::shared_ptr<Bar>> foo2;
  auto bar = std::make_shared<Bar>(2.5);
  auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data), &foo2, bar;

  fut2.wait();
  std::cout << fut2.get()->data << std::endl;

  return 0;
}

Ответы [ 4 ]

2 голосов
/ 09 июля 2019

В вашем коде просто есть некоторые опечатки, в первом shared_ptr примере вы передаете shared_ptr<Foo> вместо необработанного ptr Foo* своей функции.Во втором примере первый аргумент Foo* является правильным, а второй - Bar вместо shared_ptr<Bar>.Вот вам рабочий пример:

class Bar
{
  public:
    Bar () {}
    double data;
};

template <typename T>
class Foo
{
  public:
    Foo () {}
    T set_data (T d) { data = d; return data; }
  private:
    T data;
};

#include <future>

TEST(xxx, yyy) {
    Foo<std::shared_ptr<Bar> > foo;
    auto bar = std::make_shared<Bar>();
    bar->data = 42;
    auto futureResult = std::async(std::launch::async,
        &Foo<std::shared_ptr<Bar> >::set_data, &foo, bar);
    std::cout << futureResult.get()->data << std::endl;
}

В качестве примечания: если вам нужно запустить асинхронную операцию с некоторыми данными внутри вашего Foo класса, лучше предоставьте интерфейс, который выполняет эту асинхронную операцию внутри вашего класса.и возвращает будущее.

std::future<T> runAsyncOper(const T& data);
2 голосов
/ 09 июля 2019

Есть некоторые проблемы, я думаю, что использование лямбды может помочь вам уточнить, что происходит:

int main (int argc, char **argv)
{
  Foo<std::shared_ptr<Bar>> foo2;
  Bar bar;

  auto op = [foo2, bar]() mutable {return foo2.set_data(std::make_shared<Bar>(bar));};
  auto fut2 = std::async(std::launch::async, op);

  fut2.wait();
  std::cout << fut2.get()->data << std::endl;

  return 0;
}

Вы должны перейти на set_data a shared_ptr на Foo. Во-вторых, set_data не является константным, поэтому вам нужна изменяемая лямбда. Наконец, будущее при возврате через get() даст вам от shared_ptr до Bar, поэтому вам нужен оператор ->. Вы можете сделать код более эффективным, перемещая Foo2 и Bar внутри лямбды, но я пытаюсь сделать ответ простым, в частности потому, что я не знаю, хотите ли вы, в вашем случае, использовать повторно Foo2 и Bar, но вы можете рассмотреть возможность перемещения внутри лямбды.

Что касается вашего конкретного кода, следующее компилируется в g ++ 9.1 с C ++ 14, см. https://godbolt.org/z/DFZLtb

int main (int argc, char **argv)
{
  Foo<std::shared_ptr<Bar>> foo2;
  Bar bar;
  auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data, &foo2, std::make_shared<Bar>());

  fut2.wait();
  std::cout << fut2.get()->data << std::endl;

  return 0;
}

Вам необходимо предоставить shared_ptr<Bar> в качестве аргумента, а не Bar, и вам нужно удалить скобки вокруг Foo<std::shared_ptr<Bar>>::set_data.

0 голосов
/ 09 июля 2019

Проблема в том, что вы передали неверные типы аргументов std::async и использовали неправильно std::make_shared.

Минимальное изменение кода:

  auto foo2 = std::make_shared<Foo<std::shared_ptr<Bar>>>();
  auto bar = std::make_shared<Bar>();
  auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data, foo2, bar);

  fut2.wait();
  std::cout << fut2.get()->data << std::endl;

https://wandbox.org/permlink/3YXG56ahFKrZs8GB

0 голосов
/ 09 июля 2019

Ваш первый пример с общим указателем не передает общий указатель на std :: async в качестве последнего параметра, он передает указатель на функцию (вы не добавили скобки)

int main()
{
    /**
    * Works :)
    */
    Foo<std::shared_ptr<Bar>> foo2;

    auto br = std::make_shared<Bar>();

    auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data, &foo2, br);

    fut2.wait();
    std::cout << fut2.get()->data << std::endl;

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...