Skip to content

Commit 59ef35a

Browse files
committed
Add the section to encode Setters as classes
1 parent 6a06ced commit 59ef35a

File tree

1 file changed

+70
-8
lines changed

1 file changed

+70
-8
lines changed

src/os/README.md

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,21 @@ inspiration for several decisions in this library.
5050
- do not use `OS\ErrnoException` if the error condition would not be indicated
5151
by the `errno` variable in C. Consider adding another similar class, e.g.
5252
add `OS\HErrnoException` if you want to report an error exposed via `h_errno`
53-
- add and use Hack classes (not type aliases) for 'handle'-like parameters and
54-
return values, e.g. `OS\open()` returns a `HH\Lib\FileDescriptor` instead of
55-
an `int`; as well as aiding type safety, this prevents requests from interfering
56-
with resources that belong to another request.
53+
- add and use Hack classes (not type aliases) for long-lived 'handle'-like
54+
parameters and return values, e.g. `OS\open()` returns a
55+
`HH\Lib\FileDescriptor` instead of an `int`; as well as aiding type safety,
56+
this prevents requests from interfering with resources that belong to another
57+
request.
5758
- Avoid `inout` parameters; return tuples instead. For example, prefer
5859
`function mkstemp(string $pattern): (FileDescriptor, string)` to
5960
`function mkstemp(inout string $in_pattern_out_path): FileDescriptor`
6061
- this can aid for common use by allowing string literals, rather than
6162
requiring otherwise-unused locals
62-
- if the primary purpose of a function is to mutate its parameters, this
63-
should be considered case-by-case; for example, if we were to add something
64-
similar to `array_pop()`, it probably should take an `inout` container - but
65-
probably shouldn't be in `OS\`
63+
- if the primary purpose of a set of functions is to create, mutate and
64+
destroy a C data structure, whose reference would not be held by C
65+
libraries, they should be exposed as a `vec`. See [Appendix:
66+
short-lived C pointer encoding](#appendix-short-lived-c-pointer-encoding)
67+
section for more detail.
6668

6769
## Implementation notes
6870

@@ -104,3 +106,63 @@ inspiration for several decisions in this library.
104106
- that said, if an operation is extremely efficient and almost always wanted,
105107
consider doing it natively, automatically. For example, ints in HSL
106108
`sockaddr` are always in host byte order, not network byte order.
109+
110+
## Appendix: C / Hack type mapping cheat sheet
111+
112+
| C type | Hack type |
113+
|---|---|
114+
| `int socket` or `int filedes`, or other long-lived system resources | `HH\Lib\OS\FileDescriptor` or other wrapper classes |
115+
| Setters for `void *` or `struct *` whose reference would not be held by C libraries | `vec`, see [Appendix: encoding lightweight C data types with setters](#appendix-encoding-lightweight-c-data-types-with-setters) |
116+
117+
## Appendix: encoding lightweight C data types with setters
118+
119+
A common practice in C library is to expose a set of functions, including a
120+
constructor, a destructor and several setters, to manipulate a C data type. If
121+
the C data type represents a native resource, e.g. `int filedes`, we should
122+
create a closeable Hack class wrappers for the C native resource, otherwise we
123+
should a create a garbage-collectable mirror in Hack, which creates and destroys
124+
the underlying C data type on demand. The rest of this section describes the
125+
encoding of the garbage-collectable mirror corresponding to the C data type.
126+
127+
### Setters as a `vec` of an interface
128+
129+
The general way to encode a lightweight C data type and its setters is to
130+
consider the data type as a `vec` of setter interface, and to create a class
131+
implements the interface for each setter function. For example, given the
132+
following short-lived C pointer and the related utility functions:
133+
134+
``` c
135+
// The short-lived C pointer
136+
typedef void *posix_spawn_file_actions_t;
137+
138+
// The constructor
139+
int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions);
140+
141+
// The setters
142+
int posix_spawn_file_actions_addchdir(posix_spawn_file_actions_t *file_actions, const char *restrict path);
143+
int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions, int filedes, int newfiledes);
144+
145+
// The destructor
146+
int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions);
147+
```
148+
149+
The corresponding Hack definition should be:
150+
151+
``` hack
152+
<<__Sealed(
153+
posix_spawn_file_actions_addchdir::class,
154+
posix_spawn_file_actions_adddup2::class
155+
)>>
156+
interface PosixSpawnFileActionsSetter {}
157+
final class posix_spawn_file_actions_addchdir implements PosixSpawnFileActionsSetter {
158+
function __construct(readonly public string $path) {}
159+
}
160+
final class posix_spawn_file_actions_adddup2 implements PosixSpawnFileActionsSetter {
161+
function __construct(
162+
readonly public FileDescriptor $filedes,
163+
readonly public int $newfiledes,
164+
) {}
165+
}
166+
167+
type posix_spawn_file_actions_t = vec[PosixSpawnFileActionsSetter];
168+
```

0 commit comments

Comments
 (0)