Skip to content

Latest commit

 

History

History
95 lines (67 loc) · 4.63 KB

Introduction.md

File metadata and controls

95 lines (67 loc) · 4.63 KB

Introduction to refl-cpp

Overview

refl-cpp allows you to do compile-time introspection, create statically-resolved proxy objects, implement type- or member-wise operations most efficiently and probably more.

Basic Usage

type_descriptor<T> is the type responsible for exposing a target type T's information.

type_descriptor<Point>::name; // -> const_string<5>{"Point"}

name is of type const_string<N> which is a refl-cpp-provided type which allows constexpr string equality, concat and slicing.

The type_descriptor<T> type also exposes a members data member and a member_types type alias where the former is a default instance of the latter.

type_descriptor<Point>::member_types; // -> type_list<member-descriptor-x, member-descriptor-y, func-descriptor-magnitude>

Since refl-cpp is a compile-time library, all of the metadata is preserved in a compile-time aware way. Each type. field or function_descriptor<T, N> is a template specialization. That is different from most classic reflection libraries which make use of runtime polymorphism and have a single non-template type responsible for all fields, functions, etc.

To allows for operations on these member descriptors refl-cpp uses the type_list<Ts...> type which does nothing by itself and is empty, but provides a means for passing a list of types through the type system.

The member_types alias above is an alias to a type_list where the template arguments are the different "anonymous" classes describing the members of the target class T.

As every other _descriptor type, type_descriptor has an attributes data member. attributes is a constexpr std::tuple<Ts...>, where Ts... is exposed by the attribute_types type alias. The attributes object is a core refl-cpp feature which allows any type or member to be decorated with user-defined objects.

type_descriptor<Point>::attributes; // -> std::tuple<Serializable, Debug>

These attributes can be used in a constexpr context.

Specializations of the field_descriptor<T, N> type represents member fields in refl-cpp. The field_descriptor type has a few key properties:

using x_field_descriptor = trait::get_t<0, member_list<Point>>;

// inherited from member_descriptor_base<T, N>
x_field_descriptor::name; // -> const_string<1>{"X"}
x_field_descriptor::attributes; // -> std::tuple<...>{attrs...}
// field_descriptor-specific
x_field_descriptor::is_static; // -> false
x_field_descriptor::is_writable; // (is non-const?) -> true
x_field_descriptor::value_type; // -> double
x_field_descriptor::pointer; // -> pointer of type double Point::*
x_field_descriptor::get(); // -> double&

Specializations of the function_descriptor<T, N> type represents member functions in refl-cpp. The function_descriptor type has a few key properties:

using magnitude_descriptor = trait::get_t<2, member_list<Point>>;

// inherited from member_descriptor_base<T, N>
magnitude_descriptor::name; // -> const_string<9>{"magnitude"}
magnitude_descriptor::attributes; // -> std::tuple<...>{attrs...}
// function_descriptor-specific
magnitude_descriptor::is_resolved; // -> true
magnitude_descriptor::pointer; // -> pointer of type double (Point::* const)(double, double)
magnitude_descriptor::resolve<Pointer>; // -> pointer of type Pointer on success, nullptr on fail.
magnitude_descriptor::invoke([Self&& self], Args&&... args); // -> the result of self.magnitude(args...)

function_descriptors can be tricky as they represent a "group" of functions with the same name. Overload resolution is done by the resolve or invoke functions of function_descriptor. Only when the function is not overloaded is pointer available (nullptr otherwise). A call to resolve is needed to get a pointer to a specific overload. A call to resolve is not needed to invoke the target function. The (*this) object must be passed as the first argument when a member function is invoked. When invoking a static function, simply provide the arguments as usual.


The examples above expect the following definition of Point.

class Point {
    double x;
    double y;
    double magnitude() const;
};

void print_point(std::ostream&, const Point&);

REFL_AUTO(
    type(Point, Serializable(), Debug(&print_point)),
    field(x),
    field(y),
    func(magnitude)
)