(Not A Real) Warzone

Pascal-ish strings - an update

If you don't like macros, and you use modern C*, you can try flexible array members.

typedef struct {
  size_t length;
  char str[];
} String;

String * string_init(const char * initstr) {
  if (inistr == NULL) return NULL;
  size_t len = strlen(initstr);
  String * str = malloc(sizeof(String) + len + 1);
  if (str == NULL) return NULL;
  str->length = len;
  strcpy(str->str, initstr);
  return str;
}

int main(void) {
  String * greeting = string_init("Hello!");
  printf("Lenght: %zu, text: '%s'\n", greeting->length, greeting->str);
  return RETURN_SUCCESS;
}

* The C standard is C17 now (and soon will be C23), not the C89/C90. Those old standards has been withdrawn by both ANSI/INCITS and ISO/IEC.

GCC cleanup - an update

A major caveat when using GCC __cleanup__ attribute: always initialize your pointers, even with a NULL. Otherwise you could end up with undefined behaviour (actually a "double free or corruption" error message).

void print_positive_result(int i) {
  char * CLEANUPCHAR(s);  // without initialization it holds a random value from the stack
  if (i >= 0) {
    s = malloc(32);
    snprintf(s, 32, "The result: %d", i);
    puts(s);
  } else {
    puts("Value error.");
  }
  return; // with a negative parameter it calls free() to a random memory address - UB
}

Even simpler generics

Actually, structs aren't even necessary for generics in C.

#define GENERIC_INIT(T) uint8_t T[sizeof(size_t)] = {0};
#define GENERIC_VALUE(T, type) *((type*)T)

GENERIC_INIT(t);
GENERIC_VALUE(t, int) = 12;
printf("Integer value: %d\n",  GENERIC_VALUE(t, int));
GENERIC_VALUE(t, float) = 1.5;
printf("Float value: %f\n",  GENERIC_VALUE(t, float));
GENERIC_VALUE(t, char*) = "Hello, world!";
printf("Pointer value: %s\n",  GENERIC_VALUE(t, char*));

GCC cleanup

The GCC compiler has a nice C language attribute that helps you relieve the pain of manual memory management. If a variable you declared with __cleanup__ goes out of scope, the specified function will be called with a pointer to the variable, as argument.

#define CLEANUPCHAR(x) __attribute__((__cleanup__(free_char_buffer))) x

void free_char_buffer(char ** buffer) {
  free(*buffer);
}

void print_result(int i) {
  char * CLEANUPCHAR(s) = malloc(32);
  snprintf(s, 32, "The result: %d", i);
  puts(s);
  return;
}

Pascal-ish strings

The Pascal programming language has length-prefixed string type. Let's try it in C.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define CSTR(lps) lps + sizeof(size_t)
#define CLEN(lps) *((size_t *)lps)

char * new_length_prefixed_string(const char * s) {
  if (s == NULL) return NULL;
  size_t len = strlen(s);
  char * lps = calloc(sizeof(size_t) + len + 1, 1);
  if (lps == NULL) return NULL;
  memcpy(CSTR(lps), s, len);
  CLEN(lps) = len;
  return lps;
}

int main(void) {
  char * lps = new_length_prefixed_string("Hello!");
  if (lps == NULL) return -1;
  printf("String: %s\nLength: %zu\n", CSTR(lps), CLEN(lps));	
  free(lps);
  return 0;
}

Generics in C

Just a simple struct and macro trick.

#include <stdint.h>
#include <stdio.h>

#define GENERIC_VALUE(generic, type) *((type*)generic.value)

typedef struct {
  uint8_t value[8]; // 8 bytes for 64 bit values
} Generic;


int main(void) {
  Generic T = {0};

  GENERIC_VALUE(T, int) = 10;
  printf("int: %d\n", GENERIC_VALUE(T, int));
  GENERIC_VALUE(T, float) = 9.9999F;
  printf("float: %f\n", GENERIC_VALUE(T, float));
  GENERIC_VALUE(T, char*) = "Hello!"; 
  printf("string: %s\n", GENERIC_VALUE(T, char*));
  
  return 0;
}

Win10 UTF-8 locale

"Starting in Windows 10 build 17134 (April 2018 Update), the Universal C Runtime supports using a UTF-8 code page. This means that char strings passed to C runtime functions will expect strings in the UTF-8 encoding. To enable UTF-8 mode, use "UTF-8" as the code page when using setlocale. For example, setlocale(LC_ALL, ".utf8") will use the current default Windows ANSI code page (ACP) for the locale and UTF-8 for the code page. After calling setlocale(LC_ALL, ".UTF8"), you may pass "😊" to mbtowcs and it will be properly translated to a wchar_t string, whereas previously there was not a locale setting available to do this." – https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale

PinePhone

My PinePhone Manjaro Community Edition phone has just arrived. It's a massive dissapointment. The hardware is way below expectations, the available operating systems and software are pre-alpha quality. $200 + shipping cost are down the drain...

wchar_t

Using wide characters and the unicode enabled functions is Win32, you can display proper texts in the cmd shell regardless of codepage settings.

wchar_t buffer[16];
StringCchCopyW(buffer, 16, L"Őrß");
HANDLE std = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleW(std, buffer, wcslen(buffer), NULL, NULL);
CloseHandle(std);