Skip to content

Commit

Permalink
* Fixed comparison of Extend types with other types. Requires the o…
Browse files Browse the repository at this point in the history
…ther type can be turned into a tuple.

* Fixed internal consistencies.
* Updated `mope` to allow comments by setting a section to nothing: `{{#section=}}...{{/section}}`.
* Added concept `IsTuple` which determines whether a type is a `std::tuple`.
  • Loading branch information
helly25 committed Jun 9, 2024
1 parent 394d4be commit 7a84a13
Show file tree
Hide file tree
Showing 14 changed files with 164 additions and 442 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 0.2.27

* Fixed comparison of `Extend` types with other types. Requires the other type can be turned into a tuple.
* Fixed internal consistencies.
* Updated `mope` to allow comments by setting a section to nothing: `{{#section=}}...{{/section}}`.
* Added concept `IsTuple` which determines whether a type is a `std::tuple`.

# 0.2.26

* Added `mbo::hash::simple::GetHash(std::string_view)` which is constexpr safe.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ The C++ library is organized in functional groups each residing in their own dir
* concept `IsBracesConstructibleV` determines whether a type can be constructe from given argument types.
* concept `IsPair` determines whether a type is a `std::pair`.
* concept `IsSameAsAnyOfRaw` / `NotSameAsAnyOfRaw` which determine whether type is one of a list of types.
* concept `IsTuple` determines whether a type is a `std::tuple`.
* concept `IsVariant` determines whether a type is a `std::variant` type.
* mbo/types:tstring_cc, mbo/types/tstring.h
* struct `tstring`: Implements type `tstring` a compile time string-literal type.
Expand Down
6 changes: 5 additions & 1 deletion mbo/mope/mope.cc
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ absl::Status Template::ExpandSectionTag(const TagInfo& tag, Context& ctx, std::s
if (RE2::FullMatch(*tag.config, *kReFor, &range.start, &range.end, &range.step, &range.join)) {
return ExpandRangeData(tag, range, ctx, output);
}
if (*tag.config == "") {
output = "";
return absl::OkStatus();
}
if (!tag.config->starts_with('[')) {
return absl::UnimplementedError(
absl::StrCat("Tag '", tag.name, "' has unknown config format '", *tag.config, "'."));
Expand Down Expand Up @@ -393,7 +397,7 @@ absl::StatusOr<bool> Template::ExpandValueTag(const TagInfo& tag, Context& ctx,
std::optional<const Template::TagInfo> Template::FindAndConsumeTag(std::string_view& pos) {
// Grab parts as string_view. Done in a separate block so these cannot escape.
// They will be invalid upon the first change of `output`.
static constexpr LazyRE2 kTagRegex = {"({{(#?)([_a-zA-Z]\\w*)(?:([=:])((?:[^}]|[}][^}])+))?}})"};
static constexpr LazyRE2 kTagRegex = {"({{(#?)([_a-zA-Z]\\w*)(?:([=:])((?:[^}]|[}][^}])*))?}})"};
std::string_view start;
std::string_view type;
std::string_view name;
Expand Down
28 changes: 18 additions & 10 deletions mbo/mope/mope_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ that are made up of sections and values.
calling `SetValue`.
```c++
mbo::mope::Templaye mope;
mbo::mope::Template mope;
mope.SetValue("foo", "bar");
std::string output("My {{foo}}.");
mope.Expand(output);
Expand All @@ -120,7 +120,7 @@ times for the same `name` which becomes the section name. The section starts
with '{{#' <name> (':' <join>)? '}}' and ends with '{{/' <name> '}};.
```c++
mbo::mope::Templaye mope;
mbo::mope::Template mope;
mope.AddSectionDictinary("section")->SetValue("foo", "bar");
mope.AddSectionDictinary("section")->SetValue("foo", "-");
mope.AddSectionDictinary("section")->SetValue("foo", "baz");
Expand All @@ -134,7 +134,15 @@ The optional <join> value can be a quoted string or a reference. It is used to
join the section values and won't be used if the section has fewer then 2
elements.
3) The template supports for-loops:
3) Comments
'{{#'<name>'=}}'...'{{/'<name>'}}'
The section tags can have additional configurations as explained below. However,
there is a special configuration, the empty one, which is otherwise illegal. It
functions as a comment becasue it replaces the whole section with nothing.
4) The template supports for-loops:
'{{#'<name>'='<start>';'<end>(';'<step>(';'<join> )? )? '}}'...'{{/'<name>'}}'
Expand All @@ -151,32 +159,32 @@ This creates an automatic 'section' with a dynamic value under <name> which
can be accessed by '{{' <name> '}}'.
```c++
mbo::mope::Templaye mope;
mbo::mope::Template mope;
mope.SetValue("max", "5");
std::string output("My {{#foo=1;max;2;"."}}{{foo}}{{/foor}}");
std::string output("My {{#foo=1;max;2;"."}}{{foo}}{{/foo}}");
mope.Expand(output);
CHECK_EQ(output, "My 1.3.5.");
```
4) The template allows to set tags from within the template. This allows to
5) The template allows to set tags from within the template. This allows to
provide centralized configuration values for instance to for-loops. The values
are global but can be overwritten at any point. However, they cannot override
template tags.
These are value tags with a configuration: '{{' <<name> '=' <value> '}}'
```c++
mbo::mope::Templaye mope;
mbo::mope::Template mope;
std::string output(R"(
{{foo_start=1}}
{{foo_end=8}}
{{foo_step=2}}
My {{#foo=foo_start;foo_end;foo_step;'.'}}{{foo}}{{/foor}})"R);
My {{#foo=foo_start;foo_end;foo_step;'.'}}{{foo}}{{/foo}})"R);
mope.Expand(output);
CHECK_EQ(output, "My 1.3.5.7.");
```
5) The template supports lists:
6) The template supports lists:
'{{#' <name> '=[' <values> '] (';' <join>)? }}'...'{{/'<name>'}}'
Expand All @@ -188,7 +196,7 @@ CHECK_EQ(output, "My 1.3.5.7.");
reference or a string in single (') or double quotes (").
```c++
mbo::mope::Templaye mope;
mbo::mope::Template mope;
std::string output(R"({{#foo=['1,2',3\,4]}}[{{foo}}]{{/foo}})"R);
mope.Expand(output);
CHECK_EQ(output, "[1,2][3,4]");
Expand Down
2 changes: 1 addition & 1 deletion mbo/types/extend.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ namespace std {
// }
// ```
template<typename Extended>
requires mbo::types_internal::HasExtender<Extended, mbo::types::extender::AbslHashable>
requires mbo::types::types_internal::HasExtender<Extended, mbo::types::extender::AbslHashable>
struct hash<Extended> { // NOLINT(cert-dcl58-cpp)

std::size_t operator()(const Extended& obj) const noexcept { return absl::HashOf(obj); }
Expand Down
71 changes: 60 additions & 11 deletions mbo/types/extend_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ using ::testing::WhenSorted;
static_assert(std::same_as<::mbo::types::extender::AbslStringify, ::mbo::extender::AbslStringify>);
static_assert(std::same_as<::mbo::types::extender::Default, ::mbo::extender::Default>);

static_assert(::mbo::types_internal::ExtenderListValid<::mbo::extender::Default>);
static_assert(::mbo::types_internal::ExtenderListValid<AbslHashable, AbslStringify, Comparable, Printable, Streamable>);
static_assert(::mbo::types::types_internal::ExtenderListValid<::mbo::extender::Default>);
static_assert(::mbo::types::types_internal::ExtenderListValid<AbslHashable, AbslStringify, Comparable, Printable, Streamable>);

struct Empty {};

Expand Down Expand Up @@ -412,19 +412,44 @@ TEST_F(ExtendTest, Comparable) {
int a = 0;
int b = 0;
};
struct Flat {
int a = 0;
int b = 0;
};

constexpr TestComparable kTest1{.a = 25, .b = 42};
constexpr TestComparable kTest2{.a = 25, .b = 43};
constexpr TestComparable kTest3{.a = 26, .b = 42};
constexpr TestComparable kTest4{.a = 25, .b = 42};

constexpr Flat kFlat1{.a = 25, .b = 42};
constexpr Flat kFlat2{.a = 25, .b = 43};
constexpr Flat kFlat3{.a = 26, .b = 42};
constexpr Flat kFlat4{.a = 25, .b = 42};

{
EXPECT_THAT(kTest1 == kTest1, true);
EXPECT_THAT(kTest1 != kTest1, false);
EXPECT_THAT(kTest1 < kTest1, false);
EXPECT_THAT(kTest1 <= kTest1, true);
EXPECT_THAT(kTest1 > kTest1, false);
EXPECT_THAT(kTest1 >= kTest1, true);
EXPECT_THAT(kTest1, kTest1); // sortcut for Eq, see below
// Same kind of comparison, just with similar type.
EXPECT_THAT(kTest1 == kFlat1, true);
EXPECT_THAT(kTest1 != kFlat1, false);
EXPECT_THAT(kTest1 < kFlat1, false);
EXPECT_THAT(kTest1 <= kFlat1, true);
EXPECT_THAT(kTest1 > kFlat1, false);
EXPECT_THAT(kTest1 >= kFlat1, true);
// Same kind of comparison, just with similar type, otherway round.
EXPECT_THAT(kFlat1 == kTest1, true);
EXPECT_THAT(kFlat1 != kTest1, false);
EXPECT_THAT(kFlat1 < kTest1, false);
EXPECT_THAT(kFlat1 <= kTest1, true);
EXPECT_THAT(kFlat1 > kTest1, false);
EXPECT_THAT(kFlat1 >= kTest1, true);
// Gmock
EXPECT_THAT(kTest1, kTest1); // shortcut for Eq, see below
EXPECT_THAT(kTest1, Eq(kTest1));
EXPECT_THAT(kTest1, Not(Lt(kTest1)));
EXPECT_THAT(kTest1, Le(kTest1));
Expand All @@ -438,6 +463,14 @@ TEST_F(ExtendTest, Comparable) {
EXPECT_THAT(kTest1 <= kTest2, true);
EXPECT_THAT(kTest1 > kTest2, false);
EXPECT_THAT(kTest1 >= kTest2, false);
// Same kind of comparison, just with similar type.
EXPECT_THAT(kTest1 == kFlat2, false);
EXPECT_THAT(kTest1 != kFlat2, true);
EXPECT_THAT(kTest1 < kFlat2, true);
EXPECT_THAT(kTest1 <= kFlat2, true);
EXPECT_THAT(kTest1 > kFlat2, false);
EXPECT_THAT(kTest1 >= kFlat2, false);
// GMock
EXPECT_THAT(kTest1, Not(kTest2));
EXPECT_THAT(kTest1, Not(Eq(kTest2)));
EXPECT_THAT(kTest1, Lt(kTest2));
Expand All @@ -452,6 +485,14 @@ TEST_F(ExtendTest, Comparable) {
EXPECT_THAT(kTest1 <= kTest3, true);
EXPECT_THAT(kTest1 > kTest3, false);
EXPECT_THAT(kTest1 >= kTest3, false);
// Same kind of comparison, just with similar type.
EXPECT_THAT(kTest1 == kFlat3, false);
EXPECT_THAT(kTest1 != kFlat3, true);
EXPECT_THAT(kTest1 < kFlat3, true);
EXPECT_THAT(kTest1 <= kFlat3, true);
EXPECT_THAT(kTest1 > kFlat3, false);
EXPECT_THAT(kTest1 >= kFlat3, false);
// GMock
EXPECT_THAT(kTest1, Not(kTest3));
EXPECT_THAT(kTest1, Not(Eq(kTest3)));
EXPECT_THAT(kTest1, Lt(kTest3));
Expand All @@ -466,7 +507,15 @@ TEST_F(ExtendTest, Comparable) {
EXPECT_THAT(kTest1 <= kTest4, true);
EXPECT_THAT(kTest1 > kTest4, false);
EXPECT_THAT(kTest1 >= kTest4, true);
EXPECT_THAT(kTest1, kTest4); // sortcut for Eq, see below
// Same kind of comparison, just with similar type.
EXPECT_THAT(kTest1 == kFlat4, true);
EXPECT_THAT(kTest1 != kFlat4, false);
EXPECT_THAT(kTest1 < kFlat4, false);
EXPECT_THAT(kTest1 <= kFlat4, true);
EXPECT_THAT(kTest1 > kFlat4, false);
EXPECT_THAT(kTest1 >= kFlat4, true);
// GMock
EXPECT_THAT(kTest1, kTest4); // shortcut for Eq, see below
EXPECT_THAT(kTest1, Eq(kTest4));
EXPECT_THAT(kTest1, Not(Lt(kTest4)));
EXPECT_THAT(kTest1, Le(kTest4));
Expand Down Expand Up @@ -529,13 +578,13 @@ TEST_F(ExtendTest, Hashable) {

EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({person, Person{}}));

ASSERT_THAT((::mbo::types_internal::IsExtended<Name>), true);
ASSERT_THAT((::mbo::types_internal::IsExtended<Person>), true);
ASSERT_THAT((::mbo::types_internal::IsExtended<PlainName>), false);
ASSERT_THAT((::mbo::types_internal::IsExtended<PlainPerson>), false);
ASSERT_THAT((::mbo::types::types_internal::IsExtended<Name>), true);
ASSERT_THAT((::mbo::types::types_internal::IsExtended<Person>), true);
ASSERT_THAT((::mbo::types::types_internal::IsExtended<PlainName>), false);
ASSERT_THAT((::mbo::types::types_internal::IsExtended<PlainPerson>), false);
ASSERT_THAT(Person::RegisteredExtenderNames(), Contains("AbslHashable"));
ASSERT_THAT(Person::RegisteredExtenderNames().size(), std::tuple_size_v<Person::RegisteredExtenders>);
ASSERT_THAT((::mbo::types_internal::HasExtender<Person, AbslHashable>), true);
ASSERT_THAT((::mbo::types::types_internal::HasExtender<Person, AbslHashable>), true);

EXPECT_THAT(absl::HashOf(person), Ne(0));
EXPECT_THAT(absl::HashOf(person), absl::HashOf(plain_person));
Expand All @@ -546,8 +595,8 @@ TEST_F(ExtendTest, Hashable) {
std::string last;
};

ASSERT_THAT((::mbo::types_internal::IsExtended<NameNoDefault>), true);
ASSERT_THAT((::mbo::types_internal::HasExtender<NameNoDefault, AbslHashable>), false);
ASSERT_THAT((::mbo::types::types_internal::IsExtended<NameNoDefault>), true);
ASSERT_THAT((::mbo::types::types_internal::HasExtender<NameNoDefault, AbslHashable>), false);
}

TEST_F(ExtendTest, ExtenderNames) {
Expand Down
25 changes: 17 additions & 8 deletions mbo/types/extender.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include "mbo/types/internal/struct_names.h" // IWYU pragma: keep
#include "mbo/types/traits.h" // IWYU pragma: keep
#include "mbo/types/tstring.h"
#include "mbo/types/tuple.h"

namespace mbo::types {

Expand Down Expand Up @@ -234,15 +235,23 @@ struct Comparable_ : ExtenderBase { // NOLINT(readability-identifier-naming)
friend bool operator<(const T& lhs, const T& rhs) { return lhs.ToTuple() < rhs.ToTuple(); }

// Define operator `<=>` on `const T&` and another type.
template<typename Lhs>
friend auto operator<=>(const Lhs& lhs, const T& rhs) {
return lhs <=> rhs.ToTupl();
}
template<typename Other, typename = decltype(StructToTuple(std::declval<const Other&>()))>
auto operator<=>(const Other& other) const { return this->ToTuple() <=> StructToTuple(other); }

template<typename Rhs>
friend auto operator<=>(const T& lhs, const Rhs& rhs) {
return lhs.ToTuple() <=> rhs;
}
template<IsTuple Other>
auto operator<=>(const Other& other) const { return this->ToTuple() <=> other; }

template<typename Other, typename = decltype(StructToTuple(std::declval<const Other&>()))>
bool operator==(const Other& other) const { return this->ToTuple() == StructToTuple(other); }

template<IsTuple Other>
bool operator==(const Other& other) const { return this->ToTuple() == other; }

template<typename Other, typename = decltype(StructToTuple(std::declval<const Other&>()))>
bool operator<(const Other& other) const { return this->ToTuple() < StructToTuple(other); }

template<IsTuple Other>
bool operator<(const Other& other) const { return this->ToTuple() < other; }
};

template<typename ExtenderBase>
Expand Down
3 changes: 3 additions & 0 deletions mbo/types/internal/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,8 @@ mope_test(
name = "decompose_count_test",
srcs = ["decompose_count.h.mope"],
outs = ["decompose_count.h"],
args = [
#"--set=clang_pre_17=x",
],
clang_format = True,
)
Loading

0 comments on commit 7a84a13

Please sign in to comment.