A record class that holds named elements in a generic way.
Motivation
A set of name-type pairs, for example { age : int, name : string
}, is generally called a record. In C++, these are usually declared using classes or structs:
struct person
{
int age;
};
The shortcoming is that such a struct provides no generic structure. Imagine you would like to write a generic function that prints such a struct, given you already know how to print ints and strings. A concrete implementation would look like this:
void print(std::ostream &_stream, person const &_person)
{
_stream << "age: " << _person.age << ", "
<< "name: " << _person.name;
}
This, however, only works for the concrete type person. To find a more abstract representation, we need to specify which elements a struct has in a generic way. To this end, we first of all need to create unique types that represent element names which we call labels. Such a label together with a unique tag type is created by FCPPT_RECORD_MAKE_LABEL:
A record element is represented by fcppt::record::element. It gets a label together with a type as its template parameters:
using age_element = fcppt::record::element<age, int>;
using name_element = fcppt::record::element<name, std::string>;
Finally, a record object consists of a list of elements:
using person = fcppt::record::object<age_element, name_element>;
Before we continue, here is a small example of how to use such a record:
person test_person{age{} = 1,
name{} = std::string(
"test")};
std::cout << fcppt::record::get<age>(test_person) << '\n';
std::cout << fcppt::record::get<age>(test_person) << '\n';
The initialization syntax is further explained in Initialization. The functions fcppt::record::get and fcppt::record::set are used to read and write elements.
To return to our goal of creating a generic output function, we first create a function print_label that given a record and a label prints the element for the given label to a stream:
template <typename Label, typename... Elements>
void print_label(std::ostream &_stream, fcppt::record::object<Elements...> const &_record)
{
_stream
<< ": "
}
The function fcppt::record::element_vector returns an fcppt::mpl::list::object of the elements of a record. Such a list can be used by fcppt::algorithm::loop, which will call a function for every type in the sequence. We use this to call print_label for every element of the record:
template <typename... Elements>
void print(std::ostream &_stream, fcppt::record::object<Elements...> const &_record)
{
[&_stream, &_record]<typename L, typename T>(fcppt::tag<fcppt::record::element<L, T>>)
{ print_label<L>(_stream, _record); });
}
Now, if we were to add another member to the person record, say address, we wouldn't have to change our print function.
Initialization
There are two ways to initialize a record: First, we can initialize every element separately. This is done by the special syntax
record{label_1{} = value_1, ..., label_n{} = value_n}
Here, it is important that all labels of the record appear exactly once. Also, the types of the values have to match the types of the record.
person const test_person{
age{} = 42
};
person const test_person{
age{} = 42,
age{} = 10
};
person const test_person{
age{} = 42,
};
The second way to initialize a record is via the fcppt::record::init function. It is a generic function that calls a function for every element of the record (from first to last), which can be used to initialize a record in a generic way. Assume that we have the values for our record in a stream:
std::istringstream stream{"42 test"};
We then read the corresponding elements of the record from the stream:
[&stream]<typename L, typename T>(fcppt::record::element<L, T>)
{
if (!(stream >> input))
{
throw std::runtime_error{"failure"};
}
return std::move(input);
})};
The lambda is first called for age_element and second for name_element because of the order they appear in the declaration. So, in the example, age will be 42 and name will be "test".
Labels with an argument
Unlike normal labels, there are also labels that take an additional argument. Consider a sprite that can have a fixed number of textures t_1,...,t_n which will be rendered in order. To create a label that is parameterized, we use FCPPT_RECORD_MAKE_LABEL_ARG:
Next, we create a type function that takes an unsigned integer I to the I'th texture element:
template <unsigned I>
using sprite_element = fcppt::record::element<texture<I>, std::string>;
Finally, to declare our sprite with N textures, we use a std::integer_sequence to create a record with types sprite_element<0>, ..., sprite_element<N-1>.
template <typename T>
struct sprite_from_ints;
template <unsigned... Ints>
struct sprite_from_ints<std::integer_sequence<unsigned, Ints...>>
{
using type = fcppt::record::object<sprite_element<Ints>...>;
};
template <unsigned N>
using sprite = sprite_from_ints<std::make_integer_sequence<unsigned, N>>::type;
Here is an example that shows how such a sprite can be initialized:
using sprite_type = sprite<2U>;
sprite_type const test_sprite{
texture<0>{} = std::string("ground"), texture<1>{} = std::string("clouds")};
Header files
|
| template<fcppt::mpl::list::object_concept List> |
| using | fcppt::mpl::list::unique = fcppt::mpl::set::to_list<fcppt::mpl::set::from_list_relaxed<List>> |
| | Removes duplicates from a list.
|
| template<fcppt::mpl::list::object_concept Records> |
| using | fcppt::record::all_disjoint |
| | Tests if multiple records have disjoint label sets.
|
| template<typename RecordL, typename RecordR> |
| using | fcppt::record::are_disjoint |
| | Tests if two records have disjoint label sets.
|
| template<typename Record1, typename Record2> |
| using | fcppt::record::are_equivalent |
| | Tests if two records have the same element maps.
|
| template<typename RecordL, typename RecordR> |
| using | fcppt::record::disjoint_product = fcppt::record::detail::disjoint_product<RecordL, RecordR>::type |
| | The product of two disjoint records.
|
| template<typename Type> |
| using | fcppt::record::element_init_tag = fcppt::record::detail::element_init_tag<Type>::type |
| | The tag type of fcppt::record::element_init.
|
| template<typename Type> |
| using | fcppt::record::element_init_type = fcppt::record::detail::element_init_type<Type>::type |
| | The value type of fcppt::record::element_init.
|
| template<typename Record> |
| using | fcppt::record::element_map |
| | A metafunction computing the element map of a record.
|
| template<typename Record> |
| using | fcppt::record::element_tag_tuple |
| | A metafunction returning the elements of record as a fcppt::tuple::object of fcppt::tag types.
|
| template<typename Element> |
| using | fcppt::record::element_to_label = fcppt::record::detail::element_to_label<Element>::type |
| | Extracts the type of an fcppt::record::element.
|
| template<typename Element> |
| using | fcppt::record::element_to_type = fcppt::record::detail::element_to_type<std::remove_cv_t<Element>>::type |
| | Extracts the label of an element.
|
| template<typename Record> |
| using | fcppt::record::element_vector = fcppt::record::detail::element_vector<Record>::type |
| | A metafunction returning the elements of record as an MPL vector.
|
| template<fcppt::mpl::list::object_concept List> |
| using | fcppt::record::from_list = fcppt::mpl::list::as<fcppt::record::object, List> |
| | Declares a record using an mpl::list.
|
| template<typename Record, typename Label> |
| using | fcppt::record::has_label = fcppt::mpl::set::contains<fcppt::record::label_set<Record>, Label> |
| | Tests if a record has an fcppt::record::label.
|
| template<typename... Args> |
| using | fcppt::record::is_vararg_ctor |
| | Checks if a parameter pack is suitable for record initialization.
|
| template<typename Record> |
| using | fcppt::record::label_set |
| | The set of labels of a record.
|
| template<typename Record, typename Label> |
| using | fcppt::record::label_value_type |
| | Metafunction that computes the value type of a label inside a record.
|
| template<typename Record, typename Function> |
| using | fcppt::record::map_elements |
| | Maps the elements of a vector using a metafunction.
|
| template<typename Record, typename Function> |
| using | fcppt::record::map_result |
| | The result of mapping a record.
|
|
| template<typename... Types1, typename... Types2> |
| bool | fcppt::record::operator== (fcppt::record::object< Types1... > const &_record1, fcppt::record::object< Types2... > const &_record2) |
| | Compares two records for equality.
|
| template<typename... Types1, typename... Types2> |
| bool | fcppt::record::operator!= (fcppt::record::object< Types1... > const &_record1, fcppt::record::object< Types2... > const &_record2) |
| | Compares two records for inequality.
|
| template<typename Label, typename... Elements> |
| fcppt::record::label_value_type< fcppt::record::object< Elements... >, Label > const & | fcppt::record::get (fcppt::record::object< Elements... > const &_arg) |
| | Gets an element from a record.
|
| template<typename Label, typename... Elements> |
| fcppt::record::label_value_type< fcppt::record::object< Elements... >, Label > & | fcppt::record::get (fcppt::record::object< Elements... > &_arg) |
| | Gets an element from a record.
|
| template<typename Result, typename Function> |
| Result | fcppt::record::init (Function const &_function) |
| | Initializes a record using a function.
|
template<typename Label>
requires (fcppt::record::is_label<Label>::value) |
| std::string | fcppt::record::label_name () |
| | Returns the name of a label as a string.
|
template<typename... Args>
requires ( fcppt::mpl::list::distinct<fcppt::mpl::list::object< fcppt::record::label<fcppt::record::element_init_tag<Args>>...>>::value && fcppt::record::is_vararg_ctor<Args...>::value) |
| fcppt::record::object< fcppt::record::element< fcppt::record::label< fcppt::record::element_init_tag< Args > >, fcppt::record::element_init_type< Args > >... > | fcppt::record::make (Args &&..._args) |
| | Creates a record from an initializer list.
|
| template<typename Record, typename Function> |
| fcppt::record::map_result< Record, Function > | fcppt::record::map (Record &&_record, Function const &_function) |
| | Maps a record using a function.
|
| template<typename Record1, typename Record2> |
| fcppt::record::disjoint_product< std::remove_cvref_t< Record1 >, std::remove_cvref_t< Record2 > > | fcppt::record::multiply_disjoint (Record1 &&_record1, Record2 &&_record2) |
| | Creates the disjoint product of two records.
|
| template<typename Ch, typename Traits, typename... Elements> |
| std::basic_ostream< Ch, Traits > & | fcppt::record::operator<< (std::basic_ostream< Ch, Traits > &_stream, fcppt::record::object< Elements... > const &_record) |
| | Outputs a record to a stream.
|
| template<typename Result, typename Arg> |
| Result | fcppt::record::permute (Arg &&_arg) |
| | Permutes one record into another.
|
| template<typename Label, typename... Elements> |
| void | fcppt::record::set (fcppt::record::object< Elements... > &_arg, fcppt::record::label_value_type< fcppt::record::object< Elements... >, Label > const &_value) |
| | Sets an element in a record by copy.
|
| template<typename Label, typename... Elements> |
| void | fcppt::record::set (fcppt::record::object< Elements... > &_arg, fcppt::record::label_value_type< fcppt::record::object< Elements... >, Label > &&_value) |
| | Sets an element in a record by move.
|