Last active
January 16, 2024 11:57
-
-
Save Vftdan/cbb359a87b467083325714bf5a40514a to your computer and use it in GitHub Desktop.
Slice types in C as anonymous struct types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) 2024, vftdan | |
// SPDX-License-Identifier: MIT | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <limits.h> | |
#define DEBUG_PRINT_VALUE(x, fmt) fprintf(stderr, #x " = " fmt "\n", x); fflush(stderr) | |
#define MIN(a, b) ((a) <= (b) ? (a) : (b)) | |
#define MAX(a, b) ((a) >= (b) ? (a) : (b)) | |
#define SLICE_T(T) struct {T *ptr; ssize_t len;} | |
#define SLICE_LIT(arr) {.ptr = (arr), .len = sizeof(arr) / sizeof(*(arr))} | |
#define SLICE_LIT_STR(str) {.ptr = (str), .len = sizeof(str) / sizeof(*(str)) - 1} | |
#define SLICE_CSTR(str) {.ptr = (str), .len = strlen(str)} | |
#define SINGLETON_SLICE(p) {.ptr = (p), .len = 1} | |
#define SLICE_ARG(T, name) ssize_t name##__len, T* name##__ptr | |
#define SMALL_SLICE_ARG(T, name) int name##__len, T* name##__ptr | |
#define SLICE_TO_ARG(slc) (slc).len, (slc).ptr | |
#define SMALL_SLICE_TO_ARG(slc) (int)MIN(INT_MAX, (slc).len), (slc).ptr | |
#define SLICE_FROM_ARG(arg) {.ptr = arg##__ptr, .len = arg##__len} | |
#define SLICE_ARG_TO_VAR(arg) SLICE_T(__typeof__(*arg##__ptr)) arg = SLICE_FROM_ARG(arg) | |
#define CHRSLC_FMTS "%.*s" | |
#define CHRSLC_FMTA SMALL_SLICE_TO_ARG | |
// If .len is zero, one cannot dereference .ptr ! | |
#define _SUBSLICE_POSITIVE(slc, start, endlen) (__typeof__(slc)) {.ptr = ((slc).ptr ? (slc).ptr + (start) : NULL), .len = (start >= 0) ? MAX(0, MIN((endlen), (slc).len) - (start)) : 0} | |
#define SUBSLICE(slc, start, endlen) _SUBSLICE_POSITIVE(slc, (start) + ((start) >= 0 ? 0 : (slc).len), (endlen) + ((endlen > 0) ? 0 : (slc).len)) | |
#define _SLICE_PTR_AT_POSITIVE(slc, i) (__typeof__((slc).ptr)) ((i) < 0 || (i) >= (slc).len || !(slc).ptr ? NULL : (slc).ptr + (i)) | |
#define SLICE_PTR_AT(slc, i) _SLICE_PTR_AT_POSITIVE(slc, i >= 0 ? i : i + (slc).len) | |
#define _SLICE_SAFE_DEREF(ptr, domsg) (*((ptr) ? (ptr) : ((domsg), fflush(stderr), abort(), (__typeof__(ptr))NULL))) | |
#define SLICE_AT(slc, i) _SLICE_SAFE_DEREF(SLICE_PTR_AT(slc, i), fprintf(stderr, "Failed to access%s slice (%s) of length %zd at index (%s = %zd) at %s (%s:%d)\n", (slc).ptr ? "" : " a NULL", #slc, (slc).len, #i, (ssize_t)(i), __func__, __FILE__, __LINE__)) | |
#define SLICE_FOR_INDICES(slc, i) for (ssize_t i = 0; i < (slc).len; ++i) | |
#define SLICE_CALLOC(T, size) {.ptr = (T*)calloc((size), sizeof(T)), .len = (size)} | |
// Should only be called for non-subsliced slices | |
#define SLICE_FREE(slc) do { if ((slc).ptr) free((slc).ptr); (slc).ptr = NULL; (slc).len = 0; } while(0) | |
// If SLICE_CALLOC was saved into a const SLICE_T | |
#define SLICE_FREE_DANGLE(slc) do { if ((slc).ptr) free((slc).ptr); } while(0) | |
void | |
print_slice_chars(SLICE_ARG(const char, s)) | |
{ | |
SLICE_ARG_TO_VAR(s); | |
while (s.len) { | |
printf("Character: %c\n", SLICE_AT(s, 0)); | |
s = SUBSLICE(s, 1, 0); | |
} | |
} | |
int | |
main(SMALL_SLICE_ARG(char*, args)) | |
{ | |
SLICE_ARG_TO_VAR(args); | |
SLICE_T(char) s; | |
s = args.len < 2 ? (__typeof__(s)) SLICE_LIT_STR("Hello, world!") : (__typeof__(s)) SLICE_CSTR(SLICE_AT(args, 1)); | |
DEBUG_PRINT_VALUE(s.len, "%zd"); | |
printf("The string is: " CHRSLC_FMTS "\n", CHRSLC_FMTA(s)); | |
printf("First 5 characters: " CHRSLC_FMTS "\n", CHRSLC_FMTA(SUBSLICE(s, 0, MIN(5, s.len)))); | |
printf("Last 5 characters: " CHRSLC_FMTS "\n", CHRSLC_FMTA(SUBSLICE(s, -MIN(5, s.len), 0))); | |
print_slice_chars(SLICE_TO_ARG(s)); | |
SLICE_T(int) arr = SLICE_LIT(((int[]) {1, 0, 10, 15})); | |
DEBUG_PRINT_VALUE(arr.len, "%zd"); | |
while (arr.len) { | |
printf("Element: %d\n", SLICE_AT(arr, 0)); | |
arr = SUBSLICE(arr, 1, 0); | |
} | |
SLICE_T(double) vec = SLICE_CALLOC(double, 4); | |
SLICE_AT(vec, 1) = 2.5; | |
SLICE_FOR_INDICES(vec, i) { | |
printf("Double element: %2.2lf\n", SLICE_AT(vec, i)); | |
} | |
SLICE_FREE(vec); | |
printf("Third character: %c\n", SLICE_AT(s, 2)); // Check that we crash without causing UB (use 0..2 character argument) | |
return 0; | |
} | |
// vim: noet sw=0 | |
// -*- indent-tabs-mode: t; -*- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment