Skip to content

Instantly share code, notes, and snippets.

@DanielGibson
Last active January 11, 2019 16:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanielGibson/5e7c8a4e015bd26094358c93a09f9554 to your computer and use it in GitHub Desktop.
Save DanielGibson/5e7c8a4e015bd26094358c93a09f9554 to your computer and use it in GitHub Desktop.
C++ Flexible Array example
/*
* (C) 2019 Daniel Gibson
*
* This software is dual-licensed to the public domain and under the following
* license: you are granted a perpetual, irrevocable license to copy, modify,
* publish, and distribute this file as you see fit.
* No warranty implied; use at your own risk.
*/
#include <stdlib.h> // size_t
#include <type_traits> // std::is_final
#include <utility> // std::forward
#include <new> // placement new
#include <cassert> // assert()
template<typename AT>
struct fma {
size_t count;
//AT arr[];
// Note: at least clang++ demands the flexible array to have a "trvial destructor"
// and g++ also gives an error if it doesn't, so use char[] and emplace in it
alignas(AT) char buf[];
fma() : count(0) {}
void init(size_t c, const AT& x) {
count = c;
AT* arr = (AT*)buf;
for(size_t i=0; i<c; ++i) {
new(&arr[i])AT(x);
}
}
~fma() {
AT* arr = (AT*)buf;
for(size_t i=0; i<count; ++i) {
arr[i].~AT();
}
}
AT* begin() const
{
return (AT*)buf;
}
AT* end() const
{
AT* arr = (AT*)buf;
return arr+count;
}
AT& operator[](size_t idx)
{
assert(idx < count);
AT* arr = (AT*)buf;
return arr[idx];
}
const AT& operator[](size_t idx) const
{
assert(idx < count);
const AT* arr = (const AT*)buf;
return arr[idx];
}
};
template<typename T, typename AT, fma<AT> T::*MEMBER, typename... Args>
T* NewFlexArray(size_t count, const AT& atInit, Args&&... args)
{
static_assert(std::is_final<T>::value, "You must not derive from classes that have a flexible array at the end, so make them final!");
void* mem = malloc(sizeof(T)+count*sizeof(AT));
T* ret = new(mem) T (std::forward<Args>(args)...);
//ret.*MEMBER.init(count, atInit); somehow doesn't seem to work
fma<AT>& fa = ret->*MEMBER;
fa.init(count, atInit);
/* OR use naming convention to make the template simpler, like:
ret->init_fma(countm atInit);
OR: ret->get_fma().init(count, atInit);
OR: ret->arr.init(count, atInit); */
return ret;
}
template <typename T>
void DeleteFlexArray(T* x)
{
x->~T(); // fma destructor will be called implicit as it's a member of T
free(x);
}
// test code:
#include <stdio.h>
struct Bar {
int a, b;
Bar() : a(1), b(2)
{ printf("Bar()\n"); }
Bar(const Bar& other) : a(other.a), b(other.b)
{ printf("Bar(other) %d %d\n", a, b); }
~Bar() { printf("~Bar()\n"); }
};
struct Foo final {
float x,y,z;
fma<Bar> a;
/* OR use a naming convention, like:
void init_fma(size_t count, const Bar& x) {
a.init(count, x);
}
OR: fma<Bar>& get_fma() { return a; }
OR: make it convention that the fma must always be called "arr" or whatever
*/
Foo(float x_, float y_, float z_) : x(x_), y(y_), z(z_)
{ printf("Foo(%f %f %f)\n", x, y, z); }
};
int main(int argc, char** argv)
{
int count = 5;
if(argc > 1)
count = atoi(argv[1]);
Foo* f = NewFlexArray<Foo, Bar, &Foo::a>(count, Bar(), 1.23f, 2.34f, 3.45f);
printf("f->a[] = {");
for(Bar& b : f->a) {
printf(" {%d, %d},", b.a, b.b);
}
printf(" };\n");
printf("f->a[3] = {%d, %d}\n", f->a[3].a, f->a[3].b);
DeleteFlexArray(f);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment