Просмотр исходного кода

Add an example that listens on a port. (#6634)

Mostly generated by Gemini; TODO annotations added for cases where we
should support a better way of doing various parts of this.

Assisted-by: Gemini 3 Pro

---------

Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
Richard Smith 3 месяцев назад
Родитель
Сommit
f3f498498f
2 измененных файлов с 110 добавлено и 0 удалено
  1. 5 0
      examples/interop/cpp/BUILD
  2. 105 0
      examples/interop/cpp/socket.carbon

+ 5 - 0
examples/interop/cpp/BUILD

@@ -8,3 +8,8 @@ carbon_binary(
     name = "hello_world",
     srcs = ["hello_world.carbon"],
 )
+
+carbon_binary(
+    name = "socket",
+    srcs = ["socket.carbon"],
+)

+ 105 - 0
examples/interop/cpp/socket.carbon

@@ -0,0 +1,105 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+import Core library "io";
+import Cpp library "<arpa/inet.h>";
+import Cpp library "<netinet/in.h>";
+import Cpp library "<stdio.h>";
+import Cpp library "<string_view>";
+import Cpp library "<sys/socket.h>";
+import Cpp library "<unistd.h>";
+
+import Cpp inline '''
+#define SOCKADDR_IN_SIZE sizeof(struct sockaddr_in6)
+
+// Work around htons being a macro, not a function, on MacOS.
+// TODO: This should not be necessary once we can import function-like macros.
+inline unsigned short htons_wrap(unsigned short n) {
+  return htons(n);
+}
+''';
+
+fn Perror(s: str) {
+  // TODO: Should we allow passing a string literal to a `const char*`
+  // parameter?
+  Cpp.perror(Cpp.std.data(s));
+}
+
+// Basic example of using POSIX networking functions via interop.
+//
+// Opens a socket listening on port 8081, waits for a single connection, then
+// reads a single chunk of up to 256 bytes from it and echoes it to the
+// terminal.
+fn Run() -> i32 {
+  // TODO: `Cpp.SOCK_STREAM as i32` should probably be allowed.
+  // TODO: Should this be an implicit conversion?
+  let server_fd: i32 = Cpp.socket(Cpp.AF_INET6, (Cpp.SOCK_STREAM as u32) as i32, 0);
+  if (server_fd == -1) {
+    Perror("socket failed");
+    return 1;
+  }
+
+  // TODO: Should be able to zero-initialize this with `= ()` or `= {}`.
+  var address: Cpp.sockaddr_in6;
+  Cpp.memset(&address, 0, Cpp.SOCKADDR_IN_SIZE);
+  // TODO: Should this be valid without a cast? `AF_INET6` is defined to an
+  // integer literal in glibc at least, but we import it as an `i32`.
+  address.sin6_family = Cpp.AF_INET6 as Cpp.sa_family_t;
+  // TODO: This leads to a link error referring to `in6addr_any.1`.
+  // address.sin6_addr = Cpp.in6addr_any;
+  let port: u16 = 8081;
+  address.sin6_port = Cpp.htons_wrap(port);
+
+  // TODO: Should be able to cast directly.
+  let address_ptr: Cpp.sockaddr* =
+      (&address as Cpp.void*) unsafe as Cpp.sockaddr*;
+  // TODO: Should have an implicit conversion from
+  // T* to Optional(const T*).
+  // TODO: Should expose `sizeof` somehow.
+  let bind_result: i32 = Cpp.bind(
+      server_fd, address_ptr as const Cpp.sockaddr*, Cpp.SOCKADDR_IN_SIZE as u32
+  );
+
+  if (bind_result == -1) {
+      Perror("bind failed");
+      return 1;
+  }
+
+  if (Cpp.listen(server_fd, 1) == -1) {
+    Perror("listen failed");
+    return 1;
+  }
+
+  // TODO: Add a better `Core.Print`.
+  Core.PrintStr("Server is listening on port ");
+  Core.Print(port as i32);
+
+  var addr_len: Cpp.socklen_t = 0;
+  let socket_fd: i32 = Cpp.accept(
+      server_fd,
+      address_ptr,
+      &addr_len
+  );
+
+  if (socket_fd == -1) {
+      Perror("accept failed");
+      return 1;
+  }
+
+  Core.PrintStr("Connection accepted!\n");
+
+  var buffer: array(char, 256);
+  let len: Cpp.ssize_t = Cpp.read(socket_fd, &buffer[0], 256);
+  if (len < 0) {
+    Perror("read failed");
+    return 1;
+  }
+
+  Cpp.write(1, &buffer[0], len as Cpp.size_t);
+
+  Cpp.close(socket_fd);
+  Cpp.close(server_fd);
+
+  return 0;
+}