Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,8 @@ $[rng(0, 10, pat::mod::open)]
scope. Use callables for domain logic that does not read naturally as `_` or a
short named-placeholder expression.

Note: block-scope names cannot be referenced inside `PTN_ON`, because its
caching lambda cannot capture them. Use plain `on(...)` there, or declare the
names at namespace scope.
Block-scope declarations use static storage duration, so they can also be
referenced inside `PTN_ON`'s captureless caching lambda.

### Migration from positional guard APIs

Expand Down
5 changes: 4 additions & 1 deletion include/ptn/patternia.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,11 @@ namespace ptn {
// - PTN_BIND_N is defined by chaining: it expands to
// PTN_BIND_{N-1} plus one more declaration, so adding a new
// arity only costs one short macro instead of a full rewrite.
// The static storage duration lets block-scope declarations appear
// inside PTN_ON's captureless caching lambda: only automatic
// variables require captures; statics do not.
#define PTN_BIND_DECL(Type, name) \
constexpr ::ptn::pat::mod::member_t<&Type::name> name{};
static constexpr ::ptn::pat::mod::member_t<&Type::name> name{};

#define PTN_BIND_1(Type, m0) PTN_BIND_DECL(Type, m0)

Expand Down
32 changes: 32 additions & 0 deletions tests/tests_named_placeholder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,38 @@ TEST(NamedPlaceholder, GuardOnTypePatternRejects) {
EXPECT_EQ(result, 0);
}

// =========================================================================
// PTN_ON: block-scope names have static storage duration, so the
// captureless caching lambda can reference them.
// =========================================================================

TEST(NamedPlaceholder, BlockScopeNamesWorkInsidePtnOn) {
PTN_BIND(Point, x, y);
auto on_circle = [](const Point &p) {
return ptn::match(p)
| PTN_ON(ptn::$(ptn::has<&Point::x,
&Point::y>)[x * x + y * y == 25]
>> 1,
ptn::_ >> 0);
};
EXPECT_EQ(on_circle(Point{3, 4}), 1);
EXPECT_EQ(on_circle(Point{1, 2}), 0);
}

TEST(NamedPlaceholder, RepeatedNamesInsidePtnOn) {
PTN_BIND(Point, x, y);
auto on_diagonal = [](const Point &p) {
return ptn::match(p)
| PTN_ON(
ptn::$(ptn::has<&Point::x, &Point::y>)[x * x == y * y]
>> 1,
ptn::_ >> 0);
};
EXPECT_EQ(on_diagonal(Point{3, 3}), 1);
EXPECT_EQ(on_diagonal(Point{3, -3}), 1);
EXPECT_EQ(on_diagonal(Point{3, 4}), 0);
}

// =========================================================================
// Arithmetic expressions in guards (not just comparisons).
// =========================================================================
Expand Down
Loading