blob: 58fb4e331962574699da5f3541934ea4dd0b8124 [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// 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
//
// https://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.
#pragma once
#include <cstddef>
#include <cstdint>
#include <utility>
#include "pw_rpc/internal/call_context.h"
#include "pw_rpc/internal/lock.h"
namespace pw::rpc {
class Service;
namespace internal {
class Packet;
// Each supported protobuf implementation provides a class that derives from
// Method. The implementation classes provide the following public interface:
/*
class MethodImpl : public Method {
// True if the provided function signature is valid for this method impl.
template <auto kMethod>
static constexpr bool matches();
// Creates a unary method instance.
template <auto kMethod>
static constexpr MethodImpl Unary(uint32_t id, [optional args]);
// Creates a server streaming method instance.
template <auto kMethod>
static constexpr MethodImpl ServerStreaming(uint32_t id, [optional args]);
// Creates a client streaming method instance.
static constexpr MethodImpl ClientStreaming(uint32_t id, [optional args]);
// Creates a bidirectional streaming method instance.
static constexpr MethodImpl BidirectionalStreaming(uint32_t id,
[optional args]);
// Creates a method instance for when the method implementation function has
// an incorrect signature. Having this helps reduce error message verbosity.
static constexpr MethodImpl Invalid();
};
*/
// Method implementations must pass a test that uses the MethodImplTester class
// in pw_rpc/internal/method_impl_tester.h.
class Method {
public:
constexpr uint32_t id() const { return id_; }
// The pw::rpc::Server calls method.Invoke to call a user-defined RPC. Invoke
// calls the invoker function, which handles the RPC request and response
// according to the RPC type and protobuf implementation and calls the
// user-defined RPC function.
//
// The rpc_lock() must be held through creating the call object and released
// before calling into the RPC body.
void Invoke(const CallContext& context, const Packet& request) const
PW_UNLOCK_FUNCTION(rpc_lock()) PW_NO_LOCK_SAFETY_ANALYSIS {
return invoker_(context, request); // The invoker must unlock rpc_lock().
}
protected:
using Invoker = void (&)(const CallContext&, const Packet&);
static constexpr void InvalidInvoker(const CallContext&, const Packet&) {}
constexpr Method(uint32_t id, Invoker invoker) : id_(id), invoker_(invoker) {}
private:
uint32_t id_;
Invoker invoker_;
};
// MethodTraits inspects an RPC implementation function. It determines which
// Method API is in use and the type of the RPC based on the function signature.
// pw_rpc Method implementations specialize MethodTraits for each RPC type.
template <typename Method>
struct MethodTraits {
// Specializations must set Implementation as an alias for their method
// implementation class.
using Implementation = Method;
// Specializations must set kType to the MethodType.
// static constexpr MethodType kType = (method type);
// Specializations for member function types must set Service to an alias to
// for the implemented service class.
// using Service = (derived service class);
// Specializations may provide the C++ types of the requests and responses if
// relevant.
using Request = void;
using Response = void;
};
template <auto kMethod>
using MethodImplementation =
typename MethodTraits<decltype(kMethod)>::Implementation;
template <auto kMethod>
using Request = typename MethodTraits<decltype(kMethod)>::Request;
template <auto kMethod>
using Response = typename MethodTraits<decltype(kMethod)>::Response;
// Function that calls a user-defined RPC function on the given Service.
template <auto kMethod, typename... Args>
constexpr auto CallMethodImplFunction(Service& service, Args&&... args) {
// If the method impl is a member function, deduce the type of the
// user-defined service from it, then call the method on the service.
if constexpr (std::is_member_function_pointer_v<decltype(kMethod)>) {
return (static_cast<typename MethodTraits<decltype(kMethod)>::Service&>(
service).*
kMethod)(std::forward<Args>(args)...);
} else {
return kMethod(std::forward<Args>(args)...);
}
}
} // namespace internal
} // namespace pw::rpc