Skip to content

Instantly share code, notes, and snippets.

@uucidl
Last active December 14, 2017 08:01
Show Gist options
  • Save uucidl/abc25729ae775fcf45bf094b14e2cdb8 to your computer and use it in GitHub Desktop.
Save uucidl/abc25729ae775fcf45bf094b14e2cdb8 to your computer and use it in GitHub Desktop.
Opaque struct example
#pragma once
/* @language: c11 */
#include <stddef.h>
#include <stdint.h>
#if defined(__cplusplus)
#define ARRAY_ALIGNAS alignas(8)
#else
#include <stdalign.h>
#define ARRAY_ALIGNAS _Alignas(8)
#endif
/**
* Opaque representation which allows creation on the stack, allocation etc.
*
* NOTE: This isn't how I would write an array type. I wouldn't make the members
* of such type opaque, in general. This is an example which I would use for types
* that need opacity, such as fat platform layers (access to audio APIs, databases)
* holding resources, or for types that would be slow to compile.
*
* Pros:
* - collaborating types are not leaked,
* - no forward declarations necessary,
* - implementation details are hidden
*
* I.e. hopefully you get good compilation insulation.
*
* Cons:
* - size has to be adjusted as needed (which might be a pro as it makes
* you aware of the size of a given data-type)
*/
struct Array
{
ARRAY_ALIGNAS uint8_t data[sizeof (uint8_t*) + 2*8];
};
size_t ArraySize(struct Array* array, size_t element_size);
void* ArrayAllocate(struct Array* array, size_t element_size, size_t index);
void* ArrayGet(struct Array* array, size_t element_size, size_t index);
#undef ARRAY_ALIGNAS
extern "C" {
#include "00array.h"
}
// type-safe interface to array.h
//
// This shows you can combine a type-erased core implementation and a safe, templated interface.
template <typename T>
struct SafeArray : Array
{
friend size_t ArraySize(SafeArray* self) {
return ArraySize(static_cast<Array*>(self), sizeof (T));
}
friend T* ArrayAllocate(SafeArray* self, size_t index) {
return static_cast<T*>(ArrayAllocate(static_cast<Array*>(self), sizeof (T), index));
}
friend T* ArrayGet(SafeArray* self, size_t index) {
return static_cast<T*>(ArrayGet(static_cast<Array*>(self), sizeof (T), index));
}
};
#include "00array.hpp"
#include <string>
int main(int argc, char** argv)
{
SafeArray<char*> args = {};
for (int i = 0; i < argc - 1; ++i)
{
*ArrayAllocate(&args, i) = argv[1 + i];
}
for (int i = 0; i < ArraySize(&args); ++i)
{
std::printf("%d - %s\n", i, *ArrayGet(&args, i));
}
return 0;
}
#!/usr/bin/env bash
set -x
clang++ -m32 -g -Wall -Werror -std=c++11 -fsanitize=address 00main.cpp array.cpp -o array_ex32
clang++ -m64 -g -Wall -Werror -std=c++11 -fsanitize=address 00main.cpp array.cpp -o array_ex64
// @language: c++11
//
// implementation of the `array.h` interface in c++
extern "C" {
#include "00array.h"
}
#include <cstdlib>
#include <cstring>
struct ArrayImpl
{
uint8_t* start;
uint64_t size;
uint64_t capacity;
};
static_assert(sizeof (ArrayImpl) == sizeof (Array), "opaque struct needs to conform");
// we use the memcpy idiom to do type punning between the opaque and the
// transparent representation.
//
// Compilers will remove the extra `memcpy` when optimizing this code, so this
// effectively is kind of reinterpret_cast that's impervious to the
// strict-aliasing rule.
size_t ArraySize(struct Array* array, size_t element_size)
{
ArrayImpl self;
std::memcpy(&self, array, sizeof (ArrayImpl));
return self.size / element_size;
}
void* ArrayAllocate(struct Array* array, size_t element_size, size_t index)
{
ArrayImpl self;
std::memcpy(&self, array, sizeof (ArrayImpl));
auto const needed_size = element_size * (1 + index);
if (needed_size > self.capacity) {
auto next_capacity = 2*needed_size;
self.start = static_cast<uint8_t*>(
std::realloc(self.start, next_capacity));
self.capacity = next_capacity;
}
if (needed_size > self.size) {
self.size = needed_size;
}
std::memcpy(array, &self, sizeof (Array));
return self.start + element_size * index;
}
void* ArrayGet(struct Array* array, size_t element_size, size_t index)
{
ArrayImpl self;
std::memcpy(&self, array, sizeof (ArrayImpl));
return self.start + element_size * index;
}
@uucidl
Copy link
Author

uucidl commented Apr 7, 2017

@uucidl
Copy link
Author

uucidl commented Aug 8, 2017

@uucidl
Copy link
Author

uucidl commented Dec 14, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment