Skip to content

Instantly share code, notes, and snippets.

@AlexCeleste
Last active March 1, 2023 18:56
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 AlexCeleste/ad2db35940a77d7dc8575cf9744402f9 to your computer and use it in GitHub Desktop.
Save AlexCeleste/ad2db35940a77d7dc8575cf9744402f9 to your computer and use it in GitHub Desktop.
// basic array mapper
// we can use it with mis-typed arguments
typedef void (* Mutate) (void *, void *);
typedef void * (* Step) (void *);
void addOneInt (void * in, void * out) { *(int *)out = *(int *)in + 1; }
void addOneFloat (void * in, void * out) { *(float *)out = *(float *)in + 1.0f; }
void * step_float (void * p) { return (float *)p + 1; }
void * step_int (void * p) { return (int *)p + 1; }
int ia[10];
float fa[10];
void map (void * array_in, void * array_out, int size, Mutate mut, Step step) {
void * in = array_in;
void * out = array_out;
for (int i = 0; i < size; ++ i, in = step (in), out = step (out)) {
mut (in, out);
}
}
void incrArrays (void) {
map (ia, ia, 10, addOneInt, step_int);
map (fa, fa, 10, addOneFloat, step_float);
// oh no: this also compiles, because of void*
map (fa, fa, 10, addOneInt, step_float);
map (ia, fa, 10, addOneInt, step_float);
map (ia, fa, 10, addOneInt, step_float);
map (ia, ia, 10, addOneInt, step_float);
map (fa, fa, 10, addOneInt, step_int);
map (ia, ia, 10, addOneFloat, step_int);
map (ia, fa, 10, addOneFloat, step_int);
map (ia, ia, 10, addOneFloat, step_int);
map (ia, ia, 10, addOneFloat, step_int);
map (ia, ia, 10, addOneFloat, step_float);
}
// basic array mapper but with a "step pack" to slightly simplify step fn generation
typedef void (* Mutate) (void const *, void *);
void addOneInt (void const * in, void * out) { *(int *)out = *(int const *)in + 1; }
void addOneFloat (void const * in, void * out) { *(float *)out = *(float const *)in + 1.0f; }
typedef struct Step {
void * (*m_func) (void * p);
void const * (*c_func) (void const * p);
} Step;
#define genStepFuncName(Type, Qual) step_##Type##_##Qual
#define genStepFunc(Type, Qual) static inline void Qual * genStepFuncName (Type, Qual) (void Qual * p) { return (Type Qual *)p + 1; }
#define genStepFuncs(Type) \
genStepFunc(Type, ) \
genStepFunc(Type, const) \
static struct Step const step_##Type = { genStepFuncName (Type, ), genStepFuncName (Type, const) }
genStepFuncs(int);
genStepFuncs(float);
int ia[10];
float fa[10];
void map (void const * array_in, void * array_out, int size, Mutate mut, Step step) {
void const * in = array_in;
void * out = array_out;
for (int i = 0; i < size; ++ i, in = step.c_func (in), out = step.m_func (out)) {
mut (in, out);
}
}
void incrArrays (void) {
map (ia, ia, 10, addOneInt, step_int);
map (fa, fa, 10, addOneFloat, step_float);
// oh no: this also compiles, because of void*
map (fa, fa, 10, addOneInt, step_float);
map (ia, fa, 10, addOneInt, step_float);
map (ia, fa, 10, addOneInt, step_float);
map (ia, ia, 10, addOneInt, step_float);
map (fa, fa, 10, addOneInt, step_int);
map (ia, ia, 10, addOneFloat, step_int);
map (ia, fa, 10, addOneFloat, step_int);
map (ia, ia, 10, addOneFloat, step_int);
map (ia, ia, 10, addOneFloat, step_int);
map (ia, ia, 10, addOneFloat, step_float);
}
// basic array mapper
// enhanced with type checking despite accepting arrays of any type - checks
// that the operand kind of the mapped function matches the array element type
// i.e. map :: ([T], T -> T) -> [T]
typedef void (* Mutate) (void *, void *);
typedef void * (* Step) (void *);
void addOneInt (void * in, void * out) { *(int *)out = *(int *)in + 1; }
void addOneFloat (void * in, void * out) { *(float *)out = *(float *)in + 1.0f; }
void * step_float (void * p) { return (float *)p + 1; }
void * step_int (void * p) { return (int *)p + 1; }
int ia[10];
float fa[10];
void map_impl (void * array_in, void * array_out, int size, Mutate mut, Step step) {
void * in = array_in;
void * out = array_out;
for (int i = 0; i < size; ++ i, in = step (in), out = step (out)) {
mut (in, out);
}
}
#define FunctionDescriptor(Type, Func) union { Func func; Type T; }
#define same_type(A, B) _Generic(1 ? (A) : (B) \
, void *: 0 \
, void const *: 0 \
, void volatile *: 0 \
, void const volatile *: 0 \
, default: 1)
#define check_same_type(A, B) _Static_assert (same_type (A, B), "types must match");
#define check_array_size
#define map(in, out, size, mut, step) do { \
check_same_type ((in), (out)); \
\
check_same_type ((in), &(mut).T); \
check_same_type ((in), &(step).T); \
\
map_impl ((in), (out), (size), (mut).func, (step).func); \
} while (0)
typedef FunctionDescriptor (int, Mutate) MutInt;
typedef FunctionDescriptor (int, Step) StepInt;
typedef FunctionDescriptor (float, Mutate) MutFloat;
typedef FunctionDescriptor (float, Step) StepFloat;
MutInt addOneInt_g;
StepInt stepInt_g;
MutFloat addOneFloat_g;
StepFloat stepFloat_g;
void incrArrays (void) {
map (ia, ia, 10, addOneInt_g, stepInt_g);
map (fa, fa, 10, addOneFloat_g, stepFloat_g);
// no longer compile!
// map (fa, fa, 10, addOneInt, step_float);
// map (ia, fa, 10, addOneInt, step_float);
// map (ia, fa, 10, addOneInt, step_float);
// map (ia, ia, 10, addOneInt, step_float);
// map (fa, fa, 10, addOneInt, step_int);
// map (ia, ia, 10, addOneFloat, step_int);
// map (ia, fa, 10, addOneFloat, step_int);
// map (ia, ia, 10, addOneFloat, step_int);
// map (ia, ia, 10, addOneFloat, step_int);
// map (ia, ia, 10, addOneFloat, step_float);
}
// basic array mapper
// enhanced with type checking despite accepting arrays of any type - checks
// that the operand kind of the mapped function matches the array element type
// i.e. map :: ([T], T -> T) -> [T]
// also simplified with step packs, because argument type descriptor pattern
// doesn't actually care about the type of the callable! So we can apply generic
// type descriptor info to any type, including a function pack or container!
typedef void (* Mutate) (void const *, void *);
void addOneInt (void const * in, void * out) { *(int *)out = *(int const *)in + 1; }
void addOneFloat (void const * in, void * out) { *(float *)out = *(float const *)in + 1.0f; }
typedef struct Step {
void * (*m_func) (void * p);
void const * (*c_func) (void const * p);
} Step;
#define genStepFuncName(Type, Qual) step_##Type##_##Qual
#define genStepFunc(Type, Qual) static inline void Qual * genStepFuncName (Type, Qual) (void Qual * p) { return (Type Qual *)p + 1; }
#define genStepFuncs(Type) \
genStepFunc(Type, ) \
genStepFunc(Type, const) \
static struct Step const step_##Type = { genStepFuncName (Type, ), genStepFuncName (Type, const) }
genStepFuncs(int);
genStepFuncs(float);
int ia[10];
float fa[10];
void map_impl (void const * array_in, void * array_out, int size, Mutate mut, Step step) {
void const * in = array_in;
void * out = array_out;
for (int i = 0; i < size; ++ i, in = step.c_func (in), out = step.m_func (out)) {
mut (in, out);
}
}
#define FunctionDescriptor(Type, Func) union { Func func; Type T; }
#define same_type(A, B) _Generic(1 ? (A) : (B) \
, void *: 0 \
, void const *: 0 \
, void volatile *: 0 \
, void const volatile *: 0 \
, default: 1)
#define check_same_type(A, B) _Static_assert (same_type (A, B), "types must match");
#define check_array_size
#define map(in, out, size, mut, step) do { \
check_same_type ((in), (out)); \
\
check_same_type ((in), &(mut).T); \
check_same_type ((in), &(step).T); \
\
map_impl ((in), (out), (size), (mut).func, (step).func); \
} while (0)
typedef FunctionDescriptor (int, Mutate) MutInt;
typedef FunctionDescriptor (int, Step) StepInt;
typedef FunctionDescriptor (float, Mutate) MutFloat;
typedef FunctionDescriptor (float, Step) StepFloat;
MutInt addOneInt_g;
StepInt stepInt_g;
MutFloat addOneFloat_g;
StepFloat stepFloat_g;
void incrArrays (void) {
map (ia, ia, 10, addOneInt_g, stepInt_g);
map (fa, fa, 10, addOneFloat_g, stepFloat_g);
// no longer compile!
// map (fa, fa, 10, addOneInt, step_float);
// map (ia, fa, 10, addOneInt, step_float);
// map (ia, fa, 10, addOneInt, step_float);
// map (ia, ia, 10, addOneInt, step_float);
// map (fa, fa, 10, addOneInt, step_int);
// map (ia, ia, 10, addOneFloat, step_int);
// map (ia, fa, 10, addOneFloat, step_int);
// map (ia, ia, 10, addOneFloat, step_int);
// map (ia, ia, 10, addOneFloat, step_int);
// map (ia, ia, 10, addOneFloat, step_float);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment