C v0.1.0
Installation
Add C to the [Dependencies] table of your package's Rux.toml:
[Dependencies]
C = { Namespace = "Rux", Version = "0.1.0" }
Standard packages are declared by hand: the registry does not serve them, so rux add Rux/C cannot add them.
Dependencies
Core0.1.0
Guide
C standard library bindings, for interoperating with existing native code.
Nothing else in the standard packages depends on this one. Rux/Io, Rux/Memory, and Rux/Math each reach the platform directly instead, so a Rux program links no C runtime unless it asks for one by importing this package.
What it provides
| Module | Covers |
|---|---|
Types | the C ABI's own types, errno access, and the opaque handles |
String | <char8[..].h> — counted memory operations and terminated byte strings |
StdIo | <stdio.h> — streams, printf family, fopen/fclose, positioning |
StdLib | <stdlib.h> — allocation, conversion, process control, sorting, searching |
Math | <math.h> — the elementary functions in both double and float forms |
Time | <time.h> — clocks, calendar time, and timespec |
A declaration that has to match a C interface is written against the Types aliases rather than Rux's own widths: c_int, c_long and c_ulong (which the two data models disagree about), c_char and its two explicit-signedness siblings, size_t, ssize_t, ptrdiff_t, intptr_t, uintptr_t and wchar_t. A stream is a *FILE and a stream position is a *fpos_t, both declared with no fields, so one handle cannot be passed where another was meant. ErrnoLocation returns the address of the calling thread's errno through whichever accessor the platform exports, Errno reads it, ErrnoLocationFor and ErrnoFor name which of the package's runtimes to read, and EDOM, ERANGE and EILSEQ are the three values C itself mandates.
Two C runtimes on Windows
StdLib and Math bind the Universal CRT (ucrtbase.dll); StdIo and Time bind the legacy msvcrt.dll, which is the only one exporting the formatted-output family. They are two loaded modules with two distinct _errno entry points, and CRuntime and StreamRuntime name them. SeparateStreamRuntime is true here and false everywhere else, where both constants name the one runtime.
ErrnoFor(CRuntimeArea::Streams) reads the runtime StdIo and Time are bound to and ErrnoFor(CRuntimeArea::General) the one StdLib and Math are bound to, so a caller reads the errno belonging to the call it actually made. Plain Errno is the General case. On the three Unix systems both areas name the same storage, so passing the area matching the module called is right on all four targets and needs no branch in caller code.
What was measured, on Windows 11 with the current system DLLs. The two _errno functions are different exports at different addresses, and both return the same per-thread storage: today's msvcrt.dll defers to the Universal CRT rather than keeping an errno of its own. Separately, a failing fopen through msvcrt.dll sets no errno at all, in either runtime — its failure is the null FILE* and nothing else. So the rule to follow after a StdIo or Time call is to read the return value, not errno; and where errno is consulted anyway, ErrnoFor is what names the runtime rather than leaving it to whichever accessor happened to be linked. A different Windows may resolve the two differently, which is exactly why the area is passed rather than assumed.
Both StdLib and StdIo are curated rather than complete, and what is left out is left out on purpose. gets is not declared at all: it reads a line into a buffer whose size it is never told, and C11 removed it. The ato* conversions are declared but each one says to prefer the strto* family beside it, which can report both where it stopped and a value out of range. sprintf and the scanf family are marked as writing as much as the format says to; tmpnam as naming a file it does not create; system as running whatever the shell decides its argument means; rand as guaranteeing nothing about its sequence and never to be used for anything an adversary would like to predict.
String holds two families that are not alike. The mem* calls take a length and touch exactly that many bytes; the str* calls take none and stop at the first zero byte, which means the caller has promised there is one. Where both forms exist the counted one is the one to reach for, and Rux/Memory and Rux/Text do these jobs with lengths that are checked. Each declaration records what it requires of its arguments, including the traps worth naming: strncpy does not terminate a source that fills the count, strcat walks the destination from its start on every call, and strtok keeps its position in storage shared by the whole program.
Each module selects the right declarations for the target at compile time, so the same import works across the supported platforms. rux check --target <triple> resolves every declaration here on all eight supported cells, and the Layout test checks the sizes and the runtime-filled structures on whichever one it runs.
Values and pointers
C ABI records such as timespec are ordinary structural values and copy by value. Safe package helpers borrow them: TimespecGet(value, TIME_UTC) takes a mutable reference, so callers pass the value itself. C declarations keep raw pointers where the ABI permits null, returns an address, accepts an untyped buffer, or relies on pointer arithmetic. Those calls inherit C's validity, lifetime, alignment, and overlap requirements; use Rux/Memory, Rux/Text, or Rux/Io when a checked interface exists.
Two things are absent from Math on purpose. fabsf, frexpf, ldexpf and hypotf are header inlines in the Universal CRT rather than exported symbols, so declaring them would link on Unix and fail on Windows. The nexttoward pair takes a long double, which is a different type on every target — 64 bits under the Universal CRT, an 80-bit x87 value on x86-64 Unix, a 128-bit quad on AArch64 — so no one declaration is right everywhere. Rux/Math computes at every width without a C runtime and is the portable answer.
The module names above describe where each declaration comes from; they are not part of an import path.
Example
Declarations are flat under the package, so an import names the function rather than the header it came from:
import C::{ CRuntimeArea, ErrnoFor, ErrnoLocationFor, free, malloc, puts };
func Main() -> int {
// A Rux literal is a `char8[..]` and a C declaration takes a `*char8`, so the address is passed explicitly.
var buffer = malloc(1024);
if buffer == null {
puts("allocation refused".data);
return 1;
}
puts("allocated".data);
free(buffer);
// Clearing before the call is how C's `errno = 0` is spelled; nothing clears it on success. `malloc` comes from
// `StdLib`, so its failure is readable in the `General` area.
*ErrnoLocationFor(CRuntimeArea::General) = 0i32;
let refused = malloc(18446744073709551615u64);
if refused == null && ErrnoFor(CRuntimeArea::General) != 0i32 {
puts("and the refusal reported a reason".data);
}
return 0;
}
This one compiles and runs on all eight target cells; the previous version of it did not compile at all, because it passed a literal where a *char8 was wanted.
Index
Types
| Name | Summary |
|---|---|
time_t | C's time_t: a calendar time as an arithmetic value. |
clock_t | C's clock_t: processor time as an arithmetic value. |
tm | C's tm: a calendar time broken into its components. |
timespec | The public timespec data layout. |
c_schar | C's signed char: the smallest addressable signed integer. |
c_uchar | C's unsigned char, which is also the type a byte of arbitrary storage is read through. |
c_char | C's plain char, whose signedness the standard leaves to the implementation. |
c_short | C's short. |
c_ushort | C's unsigned short. |
c_int | C's int, which is 32 bits on every currently supported target. |
c_uint | C's unsigned int. |
c_longlong | C's long long. |
c_ulonglong | C's unsigned long long. |
c_float | C's float. |
c_double | C's double. |
c_bool | C's _Bool, which occupies one byte and holds zero or one. |
c_long | C's long, 32 bits under this target's LLP64 data model. |
c_ulong | C's unsigned long, 32 bits under this target's LLP64 data model. |
wchar_t | C's wchar_t, a 16-bit UTF-16 code unit on Windows rather than a whole scalar value. |
size_t | C's size_t: the type of a size, a count of bytes, and the result of sizeof. |
ssize_t | POSIX's ssize_t: a size that can also report -1 for failure. |
ptrdiff_t | C's ptrdiff_t: the signed difference between two pointers into one object. |
intptr_t | C's intptr_t: a signed integer wide enough to hold any pointer. |
uintptr_t | C's uintptr_t: an unsigned integer wide enough to hold any pointer. |
FILE | C's FILE: a stream, whose contents belong to the C runtime. |
fpos_t | C's fpos_t: an opaque record of a position within a stream. |
CRuntimeArea | Which of the package's C runtimes a call belongs to. |
Functions
| Name | Summary |
|---|---|
cbrt | Computes cube root. |
ceil | Computes smallest integer not less than the given value. |
copysign | Copies the sign of a floating-point value. |
cos | Computes cosine. |
exp | Computes e raised to the given power. |
fabs | Computes the absolute value of a floating-point value. |
fdim | Computes positive difference of two floating-point values. |
floor | Computes largest integer not greater than the given value. |
fmax | Computes larger of two floating-point values. |
fmin | Computes smaller of two floating-point values. |
fmod | Computes remainder of the floating-point division operation. |
hypot | Computes square root of the sum of the squares of two given numbers. |
ldexp | Multiplies a number by 2 raised to an integer power. |
log | Computes natural (base e) logarithm. |
log2 | Computes base 2 logarithm. |
pow | Computes a number raised to the given power. |
round | Rounds to nearest integer, rounding away from zero in halfway cases. |
scalbln | Computes sine. |
scalblnf | Multiplies a floating-point value by an integral power of two, with a long exponent. |
sin | Computes sine. |
sqrtf | Computes square root. |
sqrt | Computes square root. |
tan | Computes tangent. |
trunc | Rounds to nearest integer not greater in magnitude than the given value. |
fclose | Closes the given file stream. |
fgetc | Reads the next character from the given stream. |
fopen | Opens a file indicated by filename with the given mode. |
fseek | Sets the file position indicator for the stream. |
ftell | Returns the current file position of the stream. |
fwrite | Writes count objects of the given size from a buffer to the stream. |
putchar | Writes a character to stdout. |
puts | Writes a string followed by a newline to stdout. |
remove | Deletes the file identified by the given path. |
sprintf | Prints formatted output to a string. |
sscanf | Reads formatted input from a string. |
abs | Computes the absolute value of an integer value. |
atof | Converts a byte string to a floating point value. |
atoi | Converts a byte string to an integer value. |
atol | Converts a byte string to a long integer value. |
atoll | Converts a byte string to a long long integer value. |
calloc | Allocates memory for an array of num objects of size and zero-initializes it. |
free | Deallocates the memory previously allocated by malloc, calloc or realloc. |
labs | Computes the absolute value of a long integer value. |
llabs | Computes the absolute value of a long long integer value. |
malloc | Allocates size bytes of uninitialized memory. |
rand | Returns a pseudo-random integer value between 0 and RAND_MAX. |
realloc | Changes the size of the memory block pointed to by ptr to size bytes. |
srand | Seeds the pseudo-random number generator used by rand. |
strtol | Converts a byte string to a long integer, reporting where it stopped. |
strtoul | Converts a byte string to an unsigned long integer, reporting where it stopped. |
strtoll | Converts a byte string to a long long integer, reporting where it stopped. |
strtoull | Converts a byte string to an unsigned long long integer, reporting where it stopped. |
strtod | Converts a byte string to a double, reporting where it stopped. |
strtof | Converts a byte string to a float, reporting where it stopped. |
qsort | Sorts an array in place; the comparison must be a strict weak ordering and the sort is not stable. |
bsearch | Finds an element of a sorted array; the array must already be ordered by the same comparison. |
memchr | Finds the first byte equal to ch within the first count bytes. |
memcmp | Compares the first count bytes of two objects. |
memcpy | Copies count bytes from src to dest. |
memmove | Copies count bytes from src to dest, correctly even when the two overlap. |
memset | Writes ch to each of the first count bytes of dest. |
strlen | The length of a byte string, not counting its terminator. |
strcmp | Compares two byte strings. |
strncmp | Compares at most count bytes of two byte strings. |
strcpy | Copies a byte string, terminator included. |
strncpy | Copies at most count bytes of a byte string, padding with zeros. |
strcat | Appends a byte string to the end of another. |
strncat | Appends at most count bytes of a byte string to the end of another. |
strchr | Finds the first occurrence of a byte in a byte string. |
strrchr | Finds the last occurrence of a byte in a byte string. |
strstr | Finds the first occurrence of one byte string within another. |
strspn | The length of the leading run of bytes drawn only from accept. |
strcspn | The length of the leading run of bytes drawn from none of reject. |
strpbrk | Finds the first byte of str that occurs in accept. |
strtok | Splits a byte string into tokens separated by any byte of delimiters. |
clock | Returns the approximate processor time used by the program. |
difftime | Computes the difference in seconds between two calendar times. |
gmtime | Converts a time_t object to calendar time expressed as UTC. |
localtime | Converts a time_t object to calendar time expressed as local time. |
mktime | Converts calendar time to a time_t object, normalizing the tm structure. |
time | Returns the current calendar time since the epoch. |
TimespecGet | Fills ts with the current time on the given base. |
ErrnoLocationFor | The address of the calling thread's errno, in the runtime area names. |
ErrnoFor | The calling thread's current errno value, in the runtime area names. |
ErrnoLocation | The address of the calling thread's errno, in the runtime C::StdLib and C::Math are bound to. |
Errno | The calling thread's current errno value. |
Constants
| Name | Summary |
|---|---|
HUGE_VAL | HUGE_VAL: what a double function returns when the true result is too large to express. |
HUGE_VALF | HUGE_VALF: the float form of HUGE_VAL. |
INFINITY | INFINITY: a positive infinity, for building a value rather than for testing one. |
NAN | NAN: a quiet not-a-number, for building a value rather than for testing one. |
EOF | EOF: what a character-reading call returns instead of a character when the stream has ended or failed. |
SEEK_SET | SEEK_SET: measure the new stream position from the beginning. |
SEEK_CUR | SEEK_CUR: measure the new stream position from the current one. |
SEEK_END | SEEK_END: measure the new stream position from the end. |
_IOFBF | _IOFBF: buffer fully, flushing when the buffer fills. |
_IOLBF | _IOLBF: buffer by line. |
_IONBF | _IONBF: do not buffer. |
BUFSIZ | BUFSIZ: the buffer size setbuf uses. |
FOPEN_MAX | FOPEN_MAX: how many streams the implementation guarantees can be open at once. |
FILENAME_MAX | FILENAME_MAX: the length of the longest file name the implementation guarantees it can open. |
L_tmpnam | L_tmpnam: the buffer size tmpnam needs. |
TMP_MAX | TMP_MAX: how many distinct names tmpnam guarantees before it repeats. |
EXIT_SUCCESS | EXIT_SUCCESS: the status a program returns to mean it did what it was asked. |
EXIT_FAILURE | EXIT_FAILURE: the status a program returns to mean it did not. |
RAND_MAX | RAND_MAX: the largest value rand can return on this target. |
TIME_UTC | TIME_UTC: the time base naming seconds since the epoch, the only one C defines. |
CLOCKS_PER_SEC | CLOCKS_PER_SEC: how many clock ticks make one second on this target. |
CRuntime | The shared object this target's C runtime lives in. |
StreamRuntime | The shared object C::StdIo and C::Time are bound to, which on Windows is a different one. |
SeparateStreamRuntime | Whether the two areas are really two runtimes on this target. |
EDOM | EDOM: an argument outside the domain over which a math function is defined. |
ERANGE | ERANGE: a result too large or too small for the return type to express. |
EILSEQ | EILSEQ: a byte sequence that does not form a valid character. |