Tradias/asio-grpc

Compilation Error with Asio-GRPC and MSVC 14.39 - work_tracking_completion_handler.hpp(54,98): error C2182: 'work_': this use of 'void' is not valid

Closed this issue · 3 comments

  • msvc : 143(14.39)
  • C++ : 20
  • boost : 1.85, 1.86
  • grpc : 1.60
  • asio-grpc : 3.2

I'm encountering a compilation error when attempting to use Asio-GRPC in combination with MSVC 14.39, C++20, Boost 1.85/1.86, and grpc 1.60. The following code snippet results in an error during compilation:

grpc::Status status;
  helloworld::Greeter::Stub stub{grpc::CreateChannel(host, grpc::InsecureChannelCredentials())};
  agrpc::GrpcContext grpc_context;
  asio::co_spawn(
      grpc_context,
      [&]() -> asio::awaitable<void>
      {
          using RPC = example::AwaitableClientRPC<&helloworld::Greeter::Stub::PrepareAsyncSayHello>;
          grpc::ClientContext client_context;
          helloworld::HelloRequest request;
          request.set_name("world");
          helloworld::HelloReply response;
          status = co_await RPC::request(grpc_context, stub, client_context, request, response);
          std::cout << status.ok() << " response: " << response.message() << std::endl;
      },
      example::RethrowFirstArg{}
  );

  grpc_context.run();

the following compilation error is produced:`

D:\dev\asio-grpc\include\agrpc\detail\work_tracking_completion_handler.hpp(54,98): error C2182: 'work_': this use of 'void' is not valid
1>(compiling source file '../../../grpc/GrpcLeaderService.cpp')
1> D:\dev\asio-grpc\include\agrpc\detail\work_tracking_completion_handler.hpp(54,98):
1> the template instantiation context (the oldest one first) is
1> D:\dev\app\common\grpc\GrpcLeaderService.cpp(1291,53):
1> see reference to function template instantiation 'boost::asio::awaitable<T,Executor> agrpc::b::detail::ClientRPCUnaryBase<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *),boost::asio::use_awaitable_t::executor_with_default<agrpc::b::BasicGrpcExecutor<std::allocator,1>>>::request<boost::asio::use_awaitable_t>(agrpc::b::GrpcContext &,StubT &,grpc::ClientContext &,const RequestT &,ResponseT &,CompletionToken &&)' being compiled
1> with
1> [
1> T=grpc::Status,
1> Executor=boost::asio::any_io_executor,
1> StubT=Grpc::TaskService::Stub,
1> RequestT=Grpc::ServiceTask,
1> ResponseT=Grpc::ServiceTask,
1> CompletionToken=boost::asio::use_awaitable_tboost::asio::any_io_executor
1> ]
1> D:\dev\asio-grpc\include\agrpc\client_rpc.hpp(134,24):
1> see reference to function template instantiation 'boost::asio::awaitable<T,Executor> agrpc::b::detail::async_initiate_sender_implementation<agrpc::b::detail::ClientUnaryRequestSenderInitiation,agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>,CompletionToken>(agrpc::b::GrpcContext &,const Initiation &,Implementation &&,CompletionToken &&)' being compiled
1> with
1> [
1> T=grpc::Status,
1> Executor=boost::asio::any_io_executor,
1> ResponseT=Grpc::ServiceTask,
1> CompletionToken=boost::asio::use_awaitable_tboost::asio::any_io_executor,
1> Initiation=agrpc::b::detail::ClientUnaryRequestSenderInitiationGrpc::ServiceTask,
1> Implementation=agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>
1> ]
1> D:\dev\asio-grpc\include\agrpc\detail\initiate_sender_implementation.hpp(60,22):
1> see reference to function template instantiation 'boost::asio::awaitable<T,Executor> boost::asio::async_initiate<CompletionToken,agrpc::b::detail::StatusSenderImplementationBase::Signature,agrpc::b::detail::SubmitSenderImplementationOperation,const Initiation&,Implementation>(agrpc::b::detail::SubmitSenderImplementationOperation &&,boost::asio::use_awaitable_t &,const Initiation &,Implementation &&)' being compiled
1> with
1> [
1> T=grpc::Status,
1> Executor=boost::asio::any_io_executor,
1> CompletionToken=boost::asio::use_awaitable_tboost::asio::any_io_executor,
1> Initiation=agrpc::b::detail::ClientUnaryRequestSenderInitiationGrpc::ServiceTask,
1> Implementation=agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>
1> ]
1> D:\dev\boost_lib\boost\asio\async_result.hpp(629,65):
1> see reference to function template instantiation 'boost::asio::awaitable<T,Executor> boost::asio::async_result<boost::asio::use_awaitable_t,agrpc::b::detail::StatusSenderImplementationBase::Signature>::initiate<Initiation,agrpc::b::detail::ClientUnaryRequestSenderInitiation,Implementation>(Initiation,boost::asio::use_awaitable_t,agrpc::b::detail::ClientUnaryRequestSenderInitiation,Implementation)' being compiled
1> with
1> [
1> T=grpc::Status,
1> Executor=boost::asio::any_io_executor,
1> Initiation=agrpc::b::detail::SubmitSenderImplementationOperation,
1> ResponseT=Grpc::ServiceTask,
1> Implementation=agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>
1> ]
1> D:\dev\boost_lib\boost\asio\impl\awaitable.hpp(402,8):
1> while compiling class template member function 'auto boost::asio::detail::awaitable_frame_base::await_transform(Function,std::enable_if<std::is_convertible<result_of<Function(boost::asio::detail::awaitable_frame_base )>::type,boost::asio::detail::awaitable_thread>::value,void>::type *)'
1> with
1> [
1> Executor=boost::asio::any_io_executor
1> ]
1> D:\dev\boost_lib\boost\asio\impl\awaitable.hpp(405,11):
1> see reference to alias template instantiation 'boost::asio::result_of_t<boost::asio::async_result<boost::asio::use_awaitable_tboost::asio::any_io_executor,agrpc::b::detail::StatusSenderImplementationBase::Signature>::initiate::<lambda_1>(boost::asio::detail::awaitable_frame_base *)>' being compiled
1> with
1> [
1> Executor=boost::asio::any_io_executor
1> ]
1> D:\dev\boost_lib\boost\asio\detail\type_traits.hpp(131,44):
1> see reference to class template instantiation 'boost::asio::result_of<boost::asio::async_result<boost::asio::use_awaitable_tboost::asio::any_io_executor,agrpc::b::detail::StatusSenderImplementationBase::Signature>::initiate::<lambda_1> (boost::asio::detail::awaitable_frame_base *)>' being compiled
1> with
1> [
1> Executor=boost::asio::any_io_executor
1> ]
1> D:\dev\boost_lib\boost\asio\detail\type_traits.hpp(128,37):
1> see reference to class template instantiation 'std::invoke_result<F,boost::asio::detail::awaitable_frame_base >' being compiled
1> with
1> [
1> F=boost::asio::async_result<boost::asio::use_awaitable_tboost::asio::any_io_executor,agrpc::b::detail::StatusSenderImplementationBase::Signature>::initiate::<lambda_1>,
1> Executor=boost::asio::any_io_executor
1> ]
1> C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\type_traits(1843,38):
1> see reference to alias template instantiation 'std::_Decltype_invoke_nonzero<_Callable,boost::asio::detail::awaitable_frame_base
,>' being compiled
1> with
1> [
1> _Callable=boost::asio::async_result<boost::asio::use_awaitable_tboost::asio::any_io_executor,agrpc::b::detail::StatusSenderImplementationBase::Signature>::initiate::<lambda_1>,
1> Executor=boost::asio::any_io_executor
1> ]
1> C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\type_traits(1635,1):
1> see reference to function template instantiation 'boost::asio::detail::awaitable_handler<Executor,grpc::Status> *boost::asio::async_result<boost::asio::use_awaitable_t,agrpc::b::detail::StatusSenderImplementationBase::Signature>::initiate::<lambda_1>::operator ()<boost::asio::detail::awaitable_frame_base>(_T1 *) const' being compiled
1> with
1> [
1> Executor=boost::asio::any_io_executor,
1> _T1=boost::asio::detail::awaitable_frame_baseboost::asio::any_io_executor
1> ]
1> D:\dev\boost_lib\boost\asio\impl\use_awaitable.hpp(286,16):
1> see reference to function template instantiation 'boost::asio::detail::awaitable_handler<Executor,grpc::Status> *boost::asio::async_result<boost::asio::use_awaitable_t,agrpc::b::detail::StatusSenderImplementationBase::Signature>::do_init<Initiation,agrpc::b::detail::ClientUnaryRequestSenderInitiation,Implementation>(boost::asio::detail::awaitable_frame_base *,Initiation &,boost::asio::use_awaitable_t,agrpc::b::detail::ClientUnaryRequestSenderInitiation &,Implementation &)' being compiled
1> with
1> [
1> Executor=boost::asio::any_io_executor,
1> Initiation=agrpc::b::detail::SubmitSenderImplementationOperation,
1> ResponseT=Grpc::ServiceTask,
1> Implementation=agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>
1> ]
1> D:\dev\boost_lib\boost\asio\impl\use_awaitable.hpp(276,14):
1> see reference to function template instantiation 'void agrpc::b::detail::SubmitSenderImplementationOperation::operator ()<boost::asio::detail::awaitable_handler<Executor,grpc::Status>,agrpc::b::detail::ClientUnaryRequestSenderInitiation,agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>>(CompletionHandler &&,const Initiation &,Implementation &&)' being compiled
1> with
1> [
1> Executor=boost::asio::any_io_executor,
1> ResponseT=Grpc::ServiceTask,
1> CompletionHandler=boost::asio::detail::awaitable_handlerboost::asio::any_io_executor,grpc::Status,
1> Initiation=agrpc::b::detail::ClientUnaryRequestSenderInitiationGrpc::ServiceTask,
1> Implementation=agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>
1> ]
1> D:\dev\asio-grpc\include\agrpc\detail\initiate_sender_implementation.hpp(42,17):
1> see reference to function template instantiation 'void agrpc::b::detail::submit_sender_implementation_operation<CompletionHandler,Initiation,Implementation>(agrpc::b::GrpcContext &,CompletionHandler &&,const Initiation &,Implementation &&)' being compiled
1> with
1> [
1> CompletionHandler=boost::asio::detail::awaitable_handlerboost::asio::any_io_executor,grpc::Status,
1> Initiation=agrpc::b::detail::ClientUnaryRequestSenderInitiationGrpc::ServiceTask,
1> Implementation=agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>
1> ]
1> D:\dev\asio-grpc\include\agrpc\detail\sender_implementation_operation.hpp(143,13):
1> see reference to function template instantiation 'agrpc::b::detail::SenderImplementationOperation<Implementation,DecayedHandler> *agrpc::b::detail::allocate_operation<agrpc::b::detail::SenderImplementationOperationTemplate::Type,CompletionHandler,agrpc::b::GrpcContext&,const Initiation&,Implementation>(Handler &&,agrpc::b::GrpcContext &,const Initiation &,Implementation &&)' being compiled
1> with
1> [
1> Implementation=agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>,
1> CompletionHandler=boost::asio::detail::awaitable_handlerboost::asio::any_io_executor,grpc::Status,
1> Initiation=agrpc::b::detail::ClientUnaryRequestSenderInitiationGrpc::ServiceTask,
1> Handler=boost::asio::detail::awaitable_handlerboost::asio::any_io_executor,grpc::Status
1> ]
1> D:\dev\asio-grpc\include\agrpc\detail\allocate.hpp(93,70):
1> see reference to function template instantiation 'agrpc::b::detail::AllocationGuard<std::allocator_traits<_Alloc>::rebind_traits> agrpc::b::detail::allocate(const Allocator &,Args &&...)' being compiled
1> D:\dev\asio-grpc\include\agrpc\detail\allocate.hpp(90,66):
1> see reference to alias template instantiation 'agrpc::b::detail::RebindAllocatorTraits<Op,std::allocator>' being compiled
1> D:\dev\asio-grpc\include\agrpc\detail\allocate.hpp(29,83):
1> see reference to class template instantiation 'std::allocator_traits<std::allocator>' being compiled
1> C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xmemory(750,27):
1> see reference to class template instantiation 'std::_Default_allocator_traits<_Alloc>' being compiled
1> with
1> [
1> _Alloc=std::allocator
1> ]
1> C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xmemory(709,30):
1> while compiling class template member function 'void std::_Default_allocator_traits<_Alloc>::deallocate(_Alloc &,agrpc::b::detail::SenderImplementationOperation<Implementation,DecayedHandler> *const ,const std::_Default_allocator_traits<_Alloc>::size_type)'
1> with
1> [
1> _Alloc=std::allocator,
1> Implementation=agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>
1> ]
1> D:\dev\asio-grpc\include\agrpc\detail\allocate.hpp(82,27):
1> see the first reference to 'std::_Default_allocator_traits<_Alloc>::deallocate' in 'agrpc::b::detail::AllocationGuard<std::allocator_traits<std::allocator>>::destroy_deallocate_using_traits'
1> with
1> [
1> _Alloc=std::allocator
1> ]
1> D:\dev\asio-grpc\include\agrpc\detail\allocate.hpp(62,44):
1> see the first reference to 'agrpc::b::detail::AllocationGuard<std::allocator_traits<std::allocator>>::destroy_deallocate_using_traits' in 'agrpc::b::detail::AllocationGuard<std::allocator_traits<std::allocator>>::~AllocationGuard'
1> D:\dev\asio-grpc\include\agrpc\detail\allocate_operation.hpp(51,9):
1> see the first reference to 'agrpc::b::detail::AllocationGuard<std::allocator_traits<std::allocator>>::~AllocationGuard' in 'agrpc::b::detail::allocate_operation'
1> C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xmemory(718,25):
1> see reference to variable template 'const size_t _New_alignof<agrpc::b::detail::SenderImplementationOperation<agrpc::b::detail::ClientUnaryRequestSenderImplementation<&Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask>,boost::asio::detail::awaitable_handlerboost::asio::executor,grpc::Status > >' being compiled
1> C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xmemory(82,63):
1> see reference to class template instantiation 'agrpc::b::detail::SenderImplementationOperation<Implementation,DecayedHandler>' being compiled
1> with
1> [
1> Implementation=agrpc::b::detail::ClientUnaryRequestSenderImplementation<std::unique_ptr<grpc::ClientAsyncResponseReaderGrpc::ServiceTask,std::default_delete<grpc::ClientAsyncResponseReaderGrpc::ServiceTask>> Grpc::TaskService::Stub::PrepareAsyncPostRequestServiceTask(grpc::ClientContext *,const Grpc::ServiceTask &,grpc::CompletionQueue *)>
1> ]
1> D:\dev\asio-grpc\include\agrpc\detail\sender_implementation_operation.hpp(36,56):
1> see reference to class template instantiation 'agrpc::b::detail::WorkTrackerboost::asio::executor,false' being compiled
`

The error trace is extensive, but seems related to boost::asio::awaitable and issues with work_ being used as void. A key observation is that boost::asio::any_io_executor does not support the boost::asio::execution::outstanding_work_t::tracked_t property. As a result, when using boost::asio::prefer_result<Executor, Property>::type, the evaluated type ends up being void.

Would it be possible to get some guidance on whether this is an implementation issue in Asio-GRPC or a misuse on my part? Any insights would be greatly appreciated.

Thanks for the report. This error occurs only when you define BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT. This seems to enable some backwards compatibility, according to the documentation:

Polymorphic I/O Executor
The any_io_executor type alias is the default runtime-polymorphic executor for all I/O objects. This type alias points to the execution::any_executor<> template with a set of supportable properties specified for use with I/O.
This new name may break existing code that directly uses the old polymorphic wrapper, executor. If required for backward compatibility, ASIO_USE_TS_EXECUTOR_AS_DEFAULT can be defined, which changes the any_io_executor type alias to instead point to the executor polymorphic wrapper.

I suspect that your code or some third-party code defines this preprocessor definition. I can change the code to make it compatible. In the meantime, check where it is defined and if it is coming from an open-source library then let me know the name, I am curious how/why they are defining it.

I checked, and it is not included in any other library. It seems the issue arises because I'm using the BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT option while using other Boost libraries (tcp, v1, process, timer, etc.) in my ongoing project. If I remove the defined BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT, various errors occur in the previous code. I would greatly appreciate it if you could make this option compatible.