Skip to content

Instantly share code, notes, and snippets.

@Vftdan
Last active January 16, 2024 11:57
Show Gist options
  • Save Vftdan/cbb359a87b467083325714bf5a40514a to your computer and use it in GitHub Desktop.
Save Vftdan/cbb359a87b467083325714bf5a40514a to your computer and use it in GitHub Desktop.
Slice types in C as anonymous struct types
// 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