Type predicates and introspection
The core enabler of Avendish is the ability to filter struct definitions according to predicates.
That is, given:
struct Foo {
int16_t x;
float y;
int32_t z;
std::string txt;
};
we want to filter for instance all integral types, and get:
tuple<int16_t, int32_t> filtered = filter_integral_types(Foo{});
or run code on the members and other similar things.
Fields introspection
The template fields_introspection<T>
allows to introspect the fields of a struct in a generic way.
Doing something for each member of a struct without an instance
fields_instrospection<T>::for_all(
[] <std::size_t Index, typename T> (avnd::field_reflection<Index, T>) {
// Index is the index of the struct member, e.g. 1 for y in the above example
// T is the type of the struct member
});
Doing something for each member of a struct with an instance
Foo foo;
fields_instrospection<T>::for_all(
foo,
[] <typename T> (T& t) {
// t will be x, y, z, txt.
});
Doing something for the Nth member of a struct without an instance
This function lifts a run-time index into a compile-time value. This is useful for instance for mapping a run-time parameter id coming from a DAW, into a field, such as when a parameter changes.
fields_instrospection<T>::for_nth(
index,
[] <std::size_t Index, typename T> (avnd::field_reflection<Index, T>) {
// Index is the index of the struct member, e.g. 1 for y in the above example
// T is the type of the struct member
});
Doing something for each member of a struct with an instance
Same as above but with an actual instance.
Foo foo;
fields_instrospection<T>::for_nth(
foo,
index,
[] <typename T> (T& t) {
// t will be x, y, z, txt.
});
Getting the index of a member pointer
Foo foo;
avnd::index_in_struct(foo, &Foo::z) == 2;
Predicate introspection
The type predicate_introspection<T, Pred>
allows similar operations on a filtered subset of the struct members.
For instance, given the predicate:
template<typename Field>
using is_int<Field> = std::integral_constant<bool, std::is_integral_v<Field>>;
then
using ints = predicate_introspection<Foo, is_int>;
will allow to query all the int16_t
and int32_t
members of the struct.
This example is assumed for all the cases below.
predicate_introspection<Foo, is_int>
will work within this referential: indices will refer to
these types. That is, the element 0 will be int16_t
and element 1 will be int32_t
.
Multiple methods are provided to go from and to indices in the complete struct, to indices in the filtered version:
member | x | y | z | txt |
field index | 0 | 1 | 2 | 3 |
filtered index | 0 | - | 1 | - |
Doing something for each member of a struct without an instance
predicate_instrospection<T, P>::for_all(
[] <std::size_t Index, typename T> (avnd::field_reflection<Index, T>) {
// Called for x, z
// Index is 0 for x, 1 for z
});
Doing something for each member of a struct with an instance
Foo foo;
predicate_instrospection<T, P>::for_all(
foo,
[] <typename T> (T& t) {
// Called for x, z
});
// This version also passes the compile-time index
Foo foo;
predicate_instrospection<T, P>::for_all_n(
foo,
[] <std::size_t Index, typename T> (T& t, avnd::predicate_index<Index>) {
// x: Index == 0
// y: Index == 1
});
// This version passes both the field index and the filtered index
Foo foo;
predicate_instrospection<T, P>::for_all_n(
foo,
[] <std::size_t LocalIndex, std::size_t FieldIndex, typename T> (T& t, avnd::predicate_index<LocalIndex>, avnd::field_index<FieldIndex>) {
// x: LocalIndex == 0 ; FieldIndex == 0
// y: LocalIndex == 1 ; FieldIndex == 2
});
// This version will return early if the passed lambda returns false
Foo foo;
bool ok = predicate_instrospection<T, P>::for_all_unless(
foo,
[] <typename T> (T& t) -> bool {
return some_condition(t);
});
Doing something for the Nth member of a struct without an instance
Two cases are possible depending on whether one has an index in the struct, or an index in the filtered part of it:
predicate_instrospection<T, P>::for_nth_raw(
field_index,
[] <std::size_t Index, typename T> (avnd::field_reflection<Index, T>) {
// field_index == 0: x
// field_index == 1: nothing
// field_index == 2: z
// field_index == 3: nothing
});
predicate_instrospection<T, P>::for_nth_mapped(
filtered_index,
[] <std::size_t Index, typename T> (avnd::field_reflection<Index, T>) {
// filtered_index == 0: x
// filtered_index == 1: z
});
Doing something for each member of a struct with an instance
Same as above but with an actual instance.
Foo foo;
predicate_instrospection<T, P>::for_nth_raw(
foo,
field_index,
[] <std::size_t Index, typename T> (T& t) {
// field_index == 0: x
// field_index == 1: nothing
// field_index == 2: z
// field_index == 3: nothing
});
Foo foo;
predicate_instrospection<T, P>::for_nth_mapped(
foo,
filtered_index,
[] <std::size_t Index, typename T> (T& t) {
// filtered_index == 0: x
// filtered_index == 1: z
});
Getting the type of the Nth element
// int16_t
using A = typename predicate_instrospection<T, P>::nth_element<0>;
// int32_t
using B = typename predicate_instrospection<T, P>::nth_element<1>;
Getting the Nth element
Foo foo;
int16_t& a = predicate_instrospection<T, P>::get<0>(foo);
int32_t& b = predicate_instrospection<T, P>::get<1>(foo);
Getting a tuple of the elements
Foo foo;
// Get references:
std::tuple<int16_t&, int32_t&> tpl = predicate_instrospection<T, P>::tie(foo);
// Get copies:
std::tuple<int16_t, int32_t> tpl = predicate_instrospection<T, P>::make_tuple(foo);
// Apply a function to each and return the tuple of that
std::tuple<std::vector<int16_t>, std::vector<int32_t>> tpl =
predicate_instrospection<T, P>::filter_tuple(
foo,
[] <typename T>(T& member) { return std::vector<T>{member}; });
Getting the index of the filtered element
// Go from index in the filtered members to index in the struct
predicate_instrospection<T, P>::map<0>() == 0;
predicate_instrospection<T, P>::map<1>() == 2;
predicate_instrospection<T, P>::map<...>() == compile-error;
// Go from index in the host struct to index in the filtered members
predicate_instrospection<T, P>::unmap<0>() == 0;
predicate_instrospection<T, P>::unmap<1>() == compile-error;
predicate_instrospection<T, P>::unmap<2>() == 1;
predicate_instrospection<T, P>::unmap<...>() == compile-error;