Udp Socket: Wrap client_ calls to hide std::function requirement

Currently, returning responses from UdpSockets involve wrapping the response
error in a std::function<...> bound to the client's OnError/OnSendError/OnRead
functions. This change makes the client_ private and has all responses go
through methods in the UdpSocket class
This provides benefits:
  1) Callers don't need to worry about wrapping responses or pushing them to the
     task runner
  2) We ensure that std::move(...) semantics are used for response types, since
     ensuring this happens has a few unexpected and easy-to-miss edge cases

Change-Id: Iac924c1390c8a488c90382101a47c0273578337e
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/1760111
Commit-Queue: Ryan Keane <rwkeane@google.com>
Reviewed-by: mark a. foltz <mfoltz@chromium.org>
Reviewed-by: Max Yakimakha <yakimakha@chromium.org>
diff --git a/platform/api/udp_socket.cc b/platform/api/udp_socket.cc
index 46b3d9e..ed02683 100644
--- a/platform/api/udp_socket.cc
+++ b/platform/api/udp_socket.cc
@@ -23,5 +23,33 @@
   deletion_callback_ = callback;
 }
 
+void UdpSocket::OnError(Error error) {
+  if (!client_) {
+    return;
+  }
+
+  task_runner_->PostTask([e = std::move(error), this]() mutable {
+    this->client_->OnError(this, std::move(e));
+  });
+}
+void UdpSocket::OnSendError(Error error) {
+  if (!client_) {
+    return;
+  }
+
+  task_runner_->PostTask([e = std::move(error), this]() mutable {
+    this->client_->OnSendError(this, std::move(e));
+  });
+}
+void UdpSocket::OnRead(ErrorOr<UdpPacket> read_data) {
+  if (!client_) {
+    return;
+  }
+
+  task_runner_->PostTask([data = std::move(read_data), this]() mutable {
+    this->client_->OnRead(this, std::move(data));
+  });
+}
+
 }  // namespace platform
 }  // namespace openscreen