#c #metaprogramming #template-meta-programming #c 20 #coroutine
Вопрос:
Можно ли определить тип поля класса на основе значения, присвоенного ему в функции самого класса?
Я пишу оболочку для библиотеки ozo, которая основана на boost.Библиотека Asio для того, чтобы использовать ее с сопрограммами c 20. Такая странная необходимость возникла из-за невозможности установить тип поля conn, которое должно быть возвращено.
Тип, который мне нужен, выводится при вызове оператора () для объекта ozo :: request_op <Initiator> reqOp;
.Обратный вызов передается оператору (), где вторым аргументом является переменная нужного мне типа. Тип, определенный для второго аргумента обратного вызова, должен быть определен для ожидающего поля класса conn.
В данном случае я определил тип соединения ConnectionPtr, но это неверно, потому что оно подходит только для одного конкретного случая. Мне нужно сделать этот функтор более универсальным. Существует также вариант передачи conn из await_suspend в await_resume, но, насколько я знаю, это можно сделать только через класс awaiter.
using ConnectionPtr = std::shared_ptr<ozo::pooled_connection<
yamail::resource_pool::handle<ozo::connection_rep<ozo::oid_map_t<>,
ozo::none_t>>, boost::asio::io_context::executor_type>>;
template<typename Initiator>
class RequestOp {
private:
ozo::request_op<Initiator> reqOp;
private:
template<typename P, typename Q, typename TimeConstraint, typename Out>
struct Awaiter {
const RequestOp<Initiator> amp;reqOp;
P amp;amp;provider;
Q amp;amp;query;
TimeConstraint t;
Out out;
ozo::error_code ec;
ConnectionPtr conn;
bool await_ready() {
return false;
}
void await_suspend(std::coroutine_handle<> coro) {
reqOp.reqOp(std::move(provider), std::move(query), t, out, [this, coro](ozo::error_code ec,
auto conn) mutable {
this->ec = ec;
this->conn = conn;
coro.resume();
});
}
decltype(auto) await_resume() {
return std::pair<ConnectionPtr, ozo::error_code>{conn, ec};
}
};
public:
template<typename P, typename Q, typename TimeConstraint, typename Out>
decltype(auto) operator()(P amp;amp;provider, Q amp;amp;query, TimeConstraint t, Out out) const {
return Awaiter<P, Q, TimeConstraint, Out>{*this, std::forward<P>(provider), std::forward<Q>(query), t, out};
}
};
constexpr RequestOp<ozo::detail::initiate_async_request> Request;
Я понимаю, что все типы в c определяются во время компиляции. Тип, передаваемый обратному вызову, также определяется во время компиляции. Проблема в том, что я не знаю, как сообщить компилятору, какой тип следует определить для поля conn. Хотя во время компиляции тип, который мне нужен, уже известен, он определен для второго аргумента функции обратного вызова, переданной в запрос operator ()
Конечная цель состоит в том, чтобы передать conn в await_resume и вернуться из него, но, к сожалению, await_resume не принимает аргументов, и это должно быть сделано через класс awaiter. Может быть, есть другие способы?
Комментарии:
1. Примечание:
decltype(auto)
обычно в этом нет необходимости, чистыйauto
так же хорош.decltype(auto)
это один из случаев представления, когда круглые скобки вокруг возвращаемых значений являются релевантными и могут приводить к висячим ссылкам.2. «Тип поля класса» всегда определяется во время компиляции, и вы уже должны это знать. Во время выполнения в этом никогда не бывает никакой тайны. Это единственный способ работы C . Если вы все еще считаете, что вам нужно «определить» тип какого-либо поля, вам необходимо предоставить дополнительную информацию о том, что вам неясно, или перефразировать свой вопрос, потому что, по крайней мере, ответ будет «вы уже знаете это, иначе ваша программа на C не будет компилироваться».
3. Я понимаю, что все типы в c определяются во время компиляции. Тип, передаваемый обратному вызову, также определяется во время компиляции. Проблема в том, что я не знаю, как сообщить компилятору, какой тип следует определить для поля conn. Хотя во время компиляции нужный мне тип уже известен, он определен для второго аргумента функции обратного вызова, переданной в запрос operator (). @Самваршавчик
4. Тип «второго аргумента функции обратного вызова», по-видимому, таков
Q
. Это то, чем это объявлено. Если параметр функции обратного вызова сам по себе является функцией, и вы хотите знать тип ее аргумента, то это простая задача для помощника по специализации, но вы должны быть в состоянии объяснить эту проблему гораздо более простым способом, используя гораздо более простой пример, без привлечения специализированной сторонней библиотеки, но используя только стандартные, собственные типы C .5.
decltype(conn)
я бы отдал его тебе. Возможноstd::remove_cvref_t<decltype(conn)>
, А.
Ответ №1:
В общем случае вы не можете этого сделать, потому что тип параметра conn
определяется в другом месте, кодом, который в некоторых случаях может быть объявлен после определения этого класса. Если функция, принимающая обратный вызов , возвращает что-то, тип которого основан на типе, возвращаемом обратным вызовом, вы можете написать что-то вроде decltype(reqOp.reqOp(…,[](auto x) {return x;}))
, но в противном случае информация о том, какой тип был передан, теряется. (Существуют приемы метапрограммирования с отслеживанием состояния, которые можно использовать для фильтрации информации с помощью аналогичного трюка, но их лучше избегать.)