Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Iox #27 add typed client and server api [stacked PR #5] #1089

Merged
merged 8 commits into from
Feb 22, 2022
85 changes: 85 additions & 0 deletions iceoryx_posh/include/iceoryx_posh/internal/popo/client_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#ifndef IOX_POSH_POPO_CLIENT_IMPL_HPP
#define IOX_POSH_POPO_CLIENT_IMPL_HPP

#include "iceoryx_posh/capro/service_description.hpp"
#include "iceoryx_posh/internal/popo/base_client.hpp"
#include "iceoryx_posh/internal/popo/request_deleter.hpp"
#include "iceoryx_posh/internal/popo/response_deleter.hpp"
#include "iceoryx_posh/internal/popo/rpc_interface.hpp"
#include "iceoryx_posh/internal/popo/typed_port_api_trait.hpp"
#include "iceoryx_posh/popo/client_options.hpp"
#include "iceoryx_posh/popo/request.hpp"
#include "iceoryx_posh/popo/response.hpp"
#include "iceoryx_posh/popo/trigger_handle.hpp"
#include "iceoryx_posh/runtime/posh_runtime.hpp"

namespace iox
{
namespace popo
{
template <typename Req, typename Res, typename BaseClientT = BaseClient<>>
class ClientImpl : public BaseClientT, public RpcInterface<Request<Req>>
{
using RequestTypeAssert = typename TypedPortApiTrait<Req>::Assert;
using ResponseTypeAssert = typename TypedPortApiTrait<Res>::Assert;

public:
/// @brief Constructor for a client
/// @param[in] service is the ServiceDescription for the new client
/// @param[in] clientOptions like the queue capacity and queue full policy by a client
explicit ClientImpl(const capro::ServiceDescription& service, const ClientOptions& clientOptions = {}) noexcept;
elBoberido marked this conversation as resolved.
Show resolved Hide resolved
ClientImpl(const ClientImpl&) = delete;
ClientImpl(ClientImpl&&) = delete;
ClientImpl& operator=(const ClientImpl&) = delete;
ClientImpl& operator=(ClientImpl&&) = delete;

/// @brief Get a Request from loaned shared memory and construct the data with the given arguments.
/// @param[in] args Arguments used to construct the data.
/// @return An instance of the Request that resides in shared memory or an error if unable to allocate memory to
/// loan.
/// @details The loaned Request is automatically released when it goes out of scope.
template <typename... Args>
cxx::expected<Request<Req>, AllocationError> loan(Args&&... args) noexcept;

/// @brief Sends the given Request and then releases its loan.
/// @param request to send.
void send(Request<Req>&& request) noexcept override;
MatthiasKillat marked this conversation as resolved.
Show resolved Hide resolved
budrus marked this conversation as resolved.
Show resolved Hide resolved

/// @brief Take the Response from the top of the receive queue.
/// @return Either a Response or a ChunkReceiveResult.
/// @details The Response takes care of the cleanup. Don't store the raw pointer to the content of the Response, but
/// always the whole Response.
cxx::expected<Response<const Res>, ChunkReceiveResult> take() noexcept;

private:
using BaseClientT::port;

cxx::expected<Request<Req>, AllocationError> loanUninitialized() noexcept;

using RequestSampleDeleter = RequestDeleter<typename BaseClientT::PortType>;
RequestSampleDeleter m_requestDeleter{port()};
using ResponseSampleDeleter = ResponseDeleter<typename BaseClientT::PortType>;
ResponseSampleDeleter m_responseDeleter{port()};
};
} // namespace popo
} // namespace iox

#include "iceoryx_posh/internal/popo/client_impl.inl"

#endif // IOX_POSH_POPO_CLIENT_IMPL_HPP
81 changes: 81 additions & 0 deletions iceoryx_posh/include/iceoryx_posh/internal/popo/client_impl.inl
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) 2021 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#ifndef IOX_POSH_POPO_CLIENT_IMPL_INL
#define IOX_POSH_POPO_CLIENT_IMPL_INL

#include "iceoryx_posh/internal/popo/client_impl.hpp"

namespace iox
{
namespace popo
{
template <typename Req, typename Res, typename BaseClientT>
ClientImpl<Req, Res, BaseClientT>::ClientImpl(const capro::ServiceDescription& service,
const ClientOptions& clientOptions) noexcept
: BaseClientT(service, clientOptions)
{
}

template <typename Req, typename Res, typename BaseClientT>
cxx::expected<Request<Req>, AllocationError> ClientImpl<Req, Res, BaseClientT>::loanUninitialized() noexcept
{
auto result = port().allocateRequest(sizeof(Req), alignof(Req));
if (result.has_error())
{
return cxx::error<AllocationError>(result.get_error());
}
auto requestHeader = result.value();
auto payload = mepoo::ChunkHeader::fromUserHeader(requestHeader)->userPayload();
auto request = cxx::unique_ptr<Req>(reinterpret_cast<Req*>(payload), m_requestDeleter);
return cxx::success<Request<Req>>(Request<Req>{std::move(request), *this});
}

template <typename Req, typename Res, typename BaseClientT>
template <typename... Args>
cxx::expected<Request<Req>, AllocationError> ClientImpl<Req, Res, BaseClientT>::loan(Args&&... args) noexcept
{
return std::move(
MatthiasKillat marked this conversation as resolved.
Show resolved Hide resolved
loanUninitialized().and_then([&](auto& request) { new (request.get()) Req(std::forward<Args>(args)...); }));
}

template <typename Req, typename Res, typename BaseClientT>
void ClientImpl<Req, Res, BaseClientT>::send(Request<Req>&& request) noexcept
{
// take the ownership of the chunk from the Request to transfer it to `sendRequest`
auto payload = request.release();
auto* requestHeader = static_cast<RequestHeader*>(mepoo::ChunkHeader::fromUserPayload(payload)->userHeader());
port().sendRequest(requestHeader);
}

template <typename Req, typename Res, typename BaseClientT>
cxx::expected<Response<const Res>, ChunkReceiveResult> ClientImpl<Req, Res, BaseClientT>::take() noexcept
{
auto result = port().getResponse();
if (result.has_error())
{
return cxx::error<ChunkReceiveResult>(result.get_error());
}
auto responseHeader = result.value();
auto payload = mepoo::ChunkHeader::fromUserHeader(responseHeader)->userPayload();
auto response = cxx::unique_ptr<const Res>(static_cast<const Res*>(payload), m_responseDeleter);
return cxx::success<Response<const Res>>(Response<const Res>{std::move(response)});
elBoberido marked this conversation as resolved.
Show resolved Hide resolved
}

} // namespace popo
} // namespace iox

#endif // IOX_POSH_POPO_CLIENT_IMPL_INL
86 changes: 86 additions & 0 deletions iceoryx_posh/include/iceoryx_posh/internal/popo/server_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#ifndef IOX_POSH_POPO_SERVER_IMPL_HPP
#define IOX_POSH_POPO_SERVER_IMPL_HPP

#include "iceoryx_posh/capro/service_description.hpp"
#include "iceoryx_posh/internal/popo/base_server.hpp"
#include "iceoryx_posh/internal/popo/request_deleter.hpp"
#include "iceoryx_posh/internal/popo/response_deleter.hpp"
#include "iceoryx_posh/internal/popo/rpc_interface.hpp"
#include "iceoryx_posh/internal/popo/typed_port_api_trait.hpp"
#include "iceoryx_posh/popo/request.hpp"
#include "iceoryx_posh/popo/response.hpp"
#include "iceoryx_posh/popo/server_options.hpp"
#include "iceoryx_posh/popo/trigger_handle.hpp"
#include "iceoryx_posh/runtime/posh_runtime.hpp"

namespace iox
{
namespace popo
{
template <typename Req, typename Res, typename BaseServerT = BaseServer<>>
class ServerImpl : public BaseServerT, public RpcInterface<Response<Res>>
{
using RequestTypeAssert = typename TypedPortApiTrait<Req>::Assert;
using ResponseTypeAssert = typename TypedPortApiTrait<Res>::Assert;

public:
/// @brief Constructor for a sserver
/// @param[in] service is the ServiceDescription for the new server
/// @param[in] serverOptions like the queue capacity and queue full policy by a server
explicit ServerImpl(const capro::ServiceDescription& service, const ServerOptions& serverOptions = {}) noexcept;
elBoberido marked this conversation as resolved.
Show resolved Hide resolved
ServerImpl(const ServerImpl&) = delete;
ServerImpl(ServerImpl&&) = delete;
ServerImpl& operator=(const ServerImpl&) = delete;
ServerImpl& operator=(ServerImpl&&) = delete;

/// @brief Take the Request from the top of the receive queue.
/// @return Either a Request or a ServerRequestResult.
/// @details The Request takes care of the cleanup. Don't store the raw pointer to the content of the Request, but
/// always the whole Request.
cxx::expected<Request<const Req>, ServerRequestResult> take() noexcept;

/// @brief Get a Response from loaned shared memory and construct the data with the given arguments.
/// @param[in] request The request to which the Response belongs to, to determine where to send the response
/// @param[in] args Arguments used to construct the data.
/// @return An instance of the Response that resides in shared memory or an error if unable to allocate memory to
/// loan.
/// @details The loaned Response is automatically released when it goes out of scope.
template <typename... Args>
cxx::expected<Response<Res>, AllocationError> loan(const Request<const Req>& request, Args&&... args) noexcept;

/// @brief Sends the given Response and then releases its loan.
/// @param response to send.
void send(Response<Res>&& response) noexcept override;

private:
using BaseServerT::port;

cxx::expected<Response<Res>, AllocationError> loanUninitialized(const Request<const Req>& request) noexcept;

using RequestSampleDeleter = RequestDeleter<typename BaseServerT::PortType>;
RequestSampleDeleter m_requestDeleter{port()};
using ResponseSampleDeleter = ResponseDeleter<typename BaseServerT::PortType>;
ResponseSampleDeleter m_responseDeleter{port()};
};
} // namespace popo
} // namespace iox

#include "iceoryx_posh/internal/popo/server_impl.inl"

#endif // IOX_POSH_POPO_SERVER_IMPL_HPP
84 changes: 84 additions & 0 deletions iceoryx_posh/include/iceoryx_posh/internal/popo/server_impl.inl
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#ifndef IOX_POSH_POPO_SERVER_IMPL_INL
#define IOX_POSH_POPO_SERVER_IMPL_INL

#include "iceoryx_posh/internal/popo/server_impl.hpp"

namespace iox
{
namespace popo
{
template <typename Req, typename Res, typename BaseServerT>
inline ServerImpl<Req, Res, BaseServerT>::ServerImpl(const capro::ServiceDescription& service,
const ServerOptions& serverOptions) noexcept
: BaseServerT(service, serverOptions)
{
}

template <typename Req, typename Res, typename BaseServerT>
cxx::expected<Request<const Req>, ServerRequestResult> ServerImpl<Req, Res, BaseServerT>::take() noexcept
{
auto result = port().getRequest();
if (result.has_error())
{
return cxx::error<ServerRequestResult>(result.get_error());
}
auto requestHeader = result.value();
auto payload = mepoo::ChunkHeader::fromUserHeader(requestHeader)->userPayload();
auto request = cxx::unique_ptr<const Req>(static_cast<const Req*>(payload), m_requestDeleter);
return cxx::success<Request<const Req>>(Request<const Req>{std::move(request)});
}

template <typename Req, typename Res, typename BaseServerT>
cxx::expected<Response<Res>, AllocationError>
ServerImpl<Req, Res, BaseServerT>::loanUninitialized(const Request<const Req>& request) noexcept
{
const auto* requestHeader = &request.getRequestHeader();
auto result = port().allocateResponse(requestHeader, sizeof(Res), alignof(Res));
if (result.has_error())
{
return cxx::error<AllocationError>(result.get_error());
}
auto responseHeader = result.value();
auto payload = mepoo::ChunkHeader::fromUserHeader(responseHeader)->userPayload();
auto response = cxx::unique_ptr<Res>(reinterpret_cast<Res*>(payload), m_responseDeleter);
return cxx::success<Response<Res>>(Response<Res>{std::move(response), *this});
}

template <typename Req, typename Res, typename BaseServerT>
template <typename... Args>
cxx::expected<Response<Res>, AllocationError> ServerImpl<Req, Res, BaseServerT>::loan(const Request<const Req>& request,
Args&&... args) noexcept
{
return std::move(loanUninitialized(request).and_then(
[&](auto& response) { new (response.get()) Res(std::forward<Args>(args)...); }));
}

template <typename Req, typename Res, typename BaseServerT>
void ServerImpl<Req, Res, BaseServerT>::send(Response<Res>&& response) noexcept
{
// take the ownership of the chunk from the Response to transfer it to `sendResponse`
auto payload = response.release();
auto* responseHeader = static_cast<ResponseHeader*>(mepoo::ChunkHeader::fromUserPayload(payload)->userHeader());
port().sendResponse(responseHeader);
}

} // namespace popo
} // namespace iox

#endif // IOX_POSH_POPO_SERVER_IMPL_INL
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#ifndef IOX_POSH_POPO_TYPED_PORT_API_TRAIT_HPP
#define IOX_POSH_POPO_TYPED_PORT_API_TRAIT_HPP

#include <type_traits>

namespace iox
{
namespace popo
{
/// @brief This type trait ensures that the template parameter for Publisher, Subscriber, Client and Server fulfill
/// specific constrains.
/// @code
/// template <typename Data>
/// class Producer
/// {
/// using DataTypeAssert = typename TypedPortApiTrait<Data>::Assert;
/// public:
/// // ...
/// }
/// @endcode
/// @note `typename TypedPortApiTrait<T>::Assert` has to be used otherwise the compiler ignores the static_assert's
template <typename T>
struct TypedPortApiTrait
{
static_assert(!std::is_void<T>::value, "Must not be void. Use the untyped API for void types");
static_assert(!std::is_const<T>::value, "Must not be const");
static_assert(!std::is_reference<T>::value, "Must not be a reference");
static_assert(!std::is_pointer<T>::value, "Must not be a pointer");
using Assert = void;
};
} // namespace popo
} // namespace iox

#endif // IOX_POSH_POPO_TYPED_PORT_API_TRAIT_HPP
Loading