Type pattern matching
To find and replace a type, create a struct that inherits publicly from
bronto::rewrite_type (either directly or indirectly) and apply the
BRONTO_BEFORE() and BRONTO_AFTER() annotations to type aliases identifying
the type to match and its replacement.
struct LegacyStringToStdString : bronto::rewrite_type {
using before BRONTO_BEFORE() = legacy::String;
using after BRONTO_AFTER() = std::string;
};The annotation is written between the name of the alias and the =. Every place
the matched type is written will be rewritten, including variable declarations,
function parameters, return types, base classes, data members, template
arguments, the right-hand side of other type aliases, and type-only contexts
such as sizeof.
BRONTO_BEFORE
Requirements
Each struct inheriting from bronto::rewrite_type must have at least one member
type alias marked BRONTO_BEFORE(). The annotation must be applied to a type
alias. Applying it to a function, struct, or any other member is an error.
Semantics
The aliased type defines the pattern to be matched. The pattern names entities
rather than spellings, so a pattern of old_ns::Old matches every use of that
type regardless of how it happens to be qualified at the use site. A different
entity that shares the name (::Old in the example below) does not match.
using Old = int;
namespace old_ns {
struct Old {};
} // namespace old_ns
struct R : bronto::rewrite_type {
using before BRONTO_BEFORE() = old_ns::Old;
using after BRONTO_AFTER() = new_ns::New;
}; namespace old_ns {
-static_assert(sizeof(Old) == 1);
+static_assert(sizeof(new_ns::New) == 1);
static_assert(sizeof(::Old) == sizeof(int));
} // namespace old_nsIf any qualifiers (const or volatile) are written in the pattern, the
matched type must also have them. If they are not written in the pattern, the
type may (but is not required to) be qualified, and any qualifiers it does
have are preserved in the replacement. With a pattern of Old and a replacement
of New const:
-void h(Old volatile);
+void h(New const volatile);Template parameters
Template parameters of an alias template act as "holes". A type parameter binds to a type argument and a non-type parameter binds to the argument expression. Bound arguments are used as written rather than evaluated, so the rule
struct Rule : bronto::rewrite_type {
template <typename T, int N>
using before BRONTO_BEFORE() = DepArray<T, N>;
template <typename T, int N>
using after BRONTO_AFTER() = T[N * 2];
};produces
- DepArray<int, 3 * 2> b;
+ int b[3 * 2 * 2];Because arguments are matched as written, a parameter appearing more than once
in a pattern requires the same spelling at each occurrence. A pattern of
S<T, T, T> matches S<int, int, int> and S<Int, Int, Int>, but does not
match S<Int, int, int>, even when Int is an alias for int.
Template arguments the pattern leaves unwritten are still matched against their
defaults. If S is declared as
template <typename T, typename U = void>
struct S {};then a pattern of S<T> matches both S<int> and S<int, void>, but does not
match S<int, int>.
BRONTO_AFTER
Requirements
Each struct inheriting from bronto::rewrite_type may have at most one member
type alias marked BRONTO_AFTER(). It must declare the same number of template
parameters as each alias marked BRONTO_BEFORE(). A rule with no BRONTO_AFTER
finds matches but rewrites nothing.
Semantics
The aliased type is the replacement for whatever the pattern matched. Each bound template argument is used in place of the template parameter of the same name.
The replacement is written with the qualification it needs at each site, so a
replacement of new_ns::New is written New at sites already inside new_ns.
The replacement may use any type constructors, including pointers, references, arrays, and function types. Bronto rebuilds the declarator at each use, adding parentheses wherever C++ requires them. For instance, the rule
template <typename T>
struct Pointer {};
struct Rule : bronto::rewrite_type {
template <typename T>
using before BRONTO_BEFORE() = Pointer<T>;
template <typename T>
using after BRONTO_AFTER() = T*;
};produces
- Pointer<int[3]> const* volatile d;
- Pointer<int[3]>* h[5];
- Pointer<int[3]>(*i)[5];
+ int (*const* volatile d)[3];
+ int (**h[5])[3];
+ int (*(*i)[5])[3];Limitations
- A type that is not written at the use site, such as one deduced through
auto, has nothing to rewrite. Class template argument deduction is not yet supported. - Uses of a type named through a using-declaration (
using old_ns::Old;) are not reliably rewritten. - Rewrite rules cannot be nested inside a
bronto::rewrite_typerule, and abronto::rewrite_typerule cannot be used as aBRONTO_USAGEconstraint.
Relationship to type inlining
A bronto::rewrite_type rule replaces one type with another. To replace an
alias with the type it already names, annotate that alias with BRONTO_INLINE()
instead, as described in type inlining.