Last active
January 11, 2019 16:55
-
-
Save DanielGibson/5e7c8a4e015bd26094358c93a09f9554 to your computer and use it in GitHub Desktop.
C++ Flexible Array example
This file contains hidden or 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
/* | |
* (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