mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-22 21:36:11 +01:00
struct -> class normalization for matchers
This commit is contained in:
parent
9abe49ec53
commit
1a56ba851b
@ -37,14 +37,18 @@ namespace Matchers {
|
|||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct MatcherBase : MatcherUntypedBase {
|
class MatcherBase : public MatcherUntypedBase {
|
||||||
|
public:
|
||||||
virtual bool match( T const& arg ) const = 0;
|
virtual bool match( T const& arg ) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Detail {
|
namespace Detail {
|
||||||
|
|
||||||
template<typename ArgT>
|
template<typename ArgT>
|
||||||
struct MatchAllOf final : MatcherBase<ArgT> {
|
class MatchAllOf final : public MatcherBase<ArgT> {
|
||||||
|
std::vector<MatcherBase<ArgT> const*> m_matchers;
|
||||||
|
|
||||||
|
public:
|
||||||
MatchAllOf() = default;
|
MatchAllOf() = default;
|
||||||
MatchAllOf(MatchAllOf const&) = delete;
|
MatchAllOf(MatchAllOf const&) = delete;
|
||||||
MatchAllOf& operator=(MatchAllOf const&) = delete;
|
MatchAllOf& operator=(MatchAllOf const&) = delete;
|
||||||
@ -83,9 +87,6 @@ namespace Matchers {
|
|||||||
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
||||||
return CATCH_MOVE(rhs);
|
return CATCH_MOVE(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<MatcherBase<ArgT> const*> m_matchers;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! lvalue overload is intentionally deleted, users should
|
//! lvalue overload is intentionally deleted, users should
|
||||||
@ -98,7 +99,9 @@ namespace Matchers {
|
|||||||
MatchAllOf<ArgT> operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf<ArgT> const& rhs) = delete;
|
MatchAllOf<ArgT> operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf<ArgT> const& rhs) = delete;
|
||||||
|
|
||||||
template<typename ArgT>
|
template<typename ArgT>
|
||||||
struct MatchAnyOf final : MatcherBase<ArgT> {
|
class MatchAnyOf final : public MatcherBase<ArgT> {
|
||||||
|
std::vector<MatcherBase<ArgT> const*> m_matchers;
|
||||||
|
public:
|
||||||
MatchAnyOf() = default;
|
MatchAnyOf() = default;
|
||||||
MatchAnyOf(MatchAnyOf const&) = delete;
|
MatchAnyOf(MatchAnyOf const&) = delete;
|
||||||
MatchAnyOf& operator=(MatchAnyOf const&) = delete;
|
MatchAnyOf& operator=(MatchAnyOf const&) = delete;
|
||||||
@ -136,9 +139,6 @@ namespace Matchers {
|
|||||||
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
||||||
return CATCH_MOVE(rhs);
|
return CATCH_MOVE(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<MatcherBase<ArgT> const*> m_matchers;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! lvalue overload is intentionally deleted, users should
|
//! lvalue overload is intentionally deleted, users should
|
||||||
@ -151,8 +151,10 @@ namespace Matchers {
|
|||||||
MatchAnyOf<ArgT> operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf<ArgT> const& rhs) = delete;
|
MatchAnyOf<ArgT> operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf<ArgT> const& rhs) = delete;
|
||||||
|
|
||||||
template<typename ArgT>
|
template<typename ArgT>
|
||||||
struct MatchNotOf final : MatcherBase<ArgT> {
|
class MatchNotOf final : public MatcherBase<ArgT> {
|
||||||
|
MatcherBase<ArgT> const& m_underlyingMatcher;
|
||||||
|
|
||||||
|
public:
|
||||||
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ):
|
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ):
|
||||||
m_underlyingMatcher( underlyingMatcher )
|
m_underlyingMatcher( underlyingMatcher )
|
||||||
{}
|
{}
|
||||||
@ -164,9 +166,6 @@ namespace Matchers {
|
|||||||
std::string describe() const override {
|
std::string describe() const override {
|
||||||
return "not " + m_underlyingMatcher.toString();
|
return "not " + m_underlyingMatcher.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
MatcherBase<ArgT> const& m_underlyingMatcher;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Detail
|
} // namespace Detail
|
||||||
|
@ -17,7 +17,8 @@ namespace Matchers {
|
|||||||
enum class FloatingPointKind : uint8_t;
|
enum class FloatingPointKind : uint8_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WithinAbsMatcher final : MatcherBase<double> {
|
class WithinAbsMatcher final : public MatcherBase<double> {
|
||||||
|
public:
|
||||||
WithinAbsMatcher(double target, double margin);
|
WithinAbsMatcher(double target, double margin);
|
||||||
bool match(double const& matchee) const override;
|
bool match(double const& matchee) const override;
|
||||||
std::string describe() const override;
|
std::string describe() const override;
|
||||||
@ -26,8 +27,11 @@ namespace Matchers {
|
|||||||
double m_margin;
|
double m_margin;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WithinUlpsMatcher final : MatcherBase<double> {
|
class WithinUlpsMatcher final : public MatcherBase<double> {
|
||||||
WithinUlpsMatcher(double target, uint64_t ulps, Detail::FloatingPointKind baseType);
|
public:
|
||||||
|
WithinUlpsMatcher( double target,
|
||||||
|
uint64_t ulps,
|
||||||
|
Detail::FloatingPointKind baseType );
|
||||||
bool match(double const& matchee) const override;
|
bool match(double const& matchee) const override;
|
||||||
std::string describe() const override;
|
std::string describe() const override;
|
||||||
private:
|
private:
|
||||||
@ -42,7 +46,8 @@ namespace Matchers {
|
|||||||
// |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
|
// |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
|
||||||
// the same result if we do this for floats, as if we do this for
|
// the same result if we do this for floats, as if we do this for
|
||||||
// doubles that were promoted from floats.
|
// doubles that were promoted from floats.
|
||||||
struct WithinRelMatcher final : MatcherBase<double> {
|
class WithinRelMatcher final : public MatcherBase<double> {
|
||||||
|
public:
|
||||||
WithinRelMatcher( double target, double epsilon );
|
WithinRelMatcher( double target, double epsilon );
|
||||||
bool match(double const& matchee) const override;
|
bool match(double const& matchee) const override;
|
||||||
std::string describe() const override;
|
std::string describe() const override;
|
||||||
|
@ -26,39 +26,46 @@ namespace Matchers {
|
|||||||
std::string m_str;
|
std::string m_str;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StringMatcherBase : MatcherBase<std::string> {
|
class StringMatcherBase : public MatcherBase<std::string> {
|
||||||
StringMatcherBase( StringRef operation, CasedString const& comparator );
|
protected:
|
||||||
std::string describe() const override;
|
|
||||||
|
|
||||||
CasedString m_comparator;
|
CasedString m_comparator;
|
||||||
StringRef m_operation;
|
StringRef m_operation;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StringMatcherBase( StringRef operation,
|
||||||
|
CasedString const& comparator );
|
||||||
|
std::string describe() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StringEqualsMatcher final : StringMatcherBase {
|
class StringEqualsMatcher final : public StringMatcherBase {
|
||||||
|
public:
|
||||||
StringEqualsMatcher( CasedString const& comparator );
|
StringEqualsMatcher( CasedString const& comparator );
|
||||||
bool match( std::string const& source ) const override;
|
bool match( std::string const& source ) const override;
|
||||||
};
|
};
|
||||||
struct StringContainsMatcher final : StringMatcherBase {
|
class StringContainsMatcher final : public StringMatcherBase {
|
||||||
|
public:
|
||||||
StringContainsMatcher( CasedString const& comparator );
|
StringContainsMatcher( CasedString const& comparator );
|
||||||
bool match( std::string const& source ) const override;
|
bool match( std::string const& source ) const override;
|
||||||
};
|
};
|
||||||
struct StartsWithMatcher final : StringMatcherBase {
|
class StartsWithMatcher final : public StringMatcherBase {
|
||||||
|
public:
|
||||||
StartsWithMatcher( CasedString const& comparator );
|
StartsWithMatcher( CasedString const& comparator );
|
||||||
bool match( std::string const& source ) const override;
|
bool match( std::string const& source ) const override;
|
||||||
};
|
};
|
||||||
struct EndsWithMatcher final : StringMatcherBase {
|
class EndsWithMatcher final : public StringMatcherBase {
|
||||||
|
public:
|
||||||
EndsWithMatcher( CasedString const& comparator );
|
EndsWithMatcher( CasedString const& comparator );
|
||||||
bool match( std::string const& source ) const override;
|
bool match( std::string const& source ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RegexMatcher final : MatcherBase<std::string> {
|
class RegexMatcher final : public MatcherBase<std::string> {
|
||||||
|
std::string m_regex;
|
||||||
|
CaseSensitive m_caseSensitivity;
|
||||||
|
|
||||||
|
public:
|
||||||
RegexMatcher( std::string regex, CaseSensitive caseSensitivity );
|
RegexMatcher( std::string regex, CaseSensitive caseSensitivity );
|
||||||
bool match( std::string const& matchee ) const override;
|
bool match( std::string const& matchee ) const override;
|
||||||
std::string describe() const override;
|
std::string describe() const override;
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_regex;
|
|
||||||
CaseSensitive m_caseSensitivity;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Creates matcher that accepts strings that are exactly equal to `str`
|
//! Creates matcher that accepts strings that are exactly equal to `str`
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
namespace Matchers {
|
namespace Matchers {
|
||||||
struct MatcherGenericBase : MatcherUntypedBase {
|
class MatcherGenericBase : public MatcherUntypedBase {
|
||||||
|
public:
|
||||||
MatcherGenericBase() = default;
|
MatcherGenericBase() = default;
|
||||||
virtual ~MatcherGenericBase(); // = default;
|
virtual ~MatcherGenericBase(); // = default;
|
||||||
|
|
||||||
@ -119,7 +120,8 @@ namespace Matchers {
|
|||||||
|
|
||||||
|
|
||||||
template<typename... MatcherTs>
|
template<typename... MatcherTs>
|
||||||
struct MatchAllOfGeneric final : MatcherGenericBase {
|
class MatchAllOfGeneric final : public MatcherGenericBase {
|
||||||
|
public:
|
||||||
MatchAllOfGeneric(MatchAllOfGeneric const&) = delete;
|
MatchAllOfGeneric(MatchAllOfGeneric const&) = delete;
|
||||||
MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete;
|
MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete;
|
||||||
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
|
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
|
||||||
@ -137,8 +139,12 @@ namespace Matchers {
|
|||||||
return describe_multi_matcher<MatcherTs...>(" and "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
|
return describe_multi_matcher<MatcherTs...>(" and "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Has to be public to enable the concatenating operators
|
||||||
|
// below, because they are not friend of the RHS, only LHS,
|
||||||
|
// and thus cannot access private fields of RHS
|
||||||
std::array<void const*, sizeof...( MatcherTs )> m_matchers;
|
std::array<void const*, sizeof...( MatcherTs )> m_matchers;
|
||||||
|
|
||||||
|
|
||||||
//! Avoids type nesting for `GenericAllOf && GenericAllOf` case
|
//! Avoids type nesting for `GenericAllOf && GenericAllOf` case
|
||||||
template<typename... MatchersRHS>
|
template<typename... MatchersRHS>
|
||||||
friend
|
friend
|
||||||
@ -169,7 +175,8 @@ namespace Matchers {
|
|||||||
|
|
||||||
|
|
||||||
template<typename... MatcherTs>
|
template<typename... MatcherTs>
|
||||||
struct MatchAnyOfGeneric final : MatcherGenericBase {
|
class MatchAnyOfGeneric final : public MatcherGenericBase {
|
||||||
|
public:
|
||||||
MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete;
|
MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete;
|
||||||
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete;
|
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete;
|
||||||
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
|
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
|
||||||
@ -187,6 +194,10 @@ namespace Matchers {
|
|||||||
return describe_multi_matcher<MatcherTs...>(" or "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
|
return describe_multi_matcher<MatcherTs...>(" or "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Has to be public to enable the concatenating operators
|
||||||
|
// below, because they are not friend of the RHS, only LHS,
|
||||||
|
// and thus cannot access private fields of RHS
|
||||||
std::array<void const*, sizeof...( MatcherTs )> m_matchers;
|
std::array<void const*, sizeof...( MatcherTs )> m_matchers;
|
||||||
|
|
||||||
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
|
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
|
||||||
@ -218,7 +229,10 @@ namespace Matchers {
|
|||||||
|
|
||||||
|
|
||||||
template<typename MatcherT>
|
template<typename MatcherT>
|
||||||
struct MatchNotOfGeneric final : MatcherGenericBase {
|
class MatchNotOfGeneric final : public MatcherGenericBase {
|
||||||
|
MatcherT const& m_matcher;
|
||||||
|
|
||||||
|
public:
|
||||||
MatchNotOfGeneric(MatchNotOfGeneric const&) = delete;
|
MatchNotOfGeneric(MatchNotOfGeneric const&) = delete;
|
||||||
MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete;
|
MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete;
|
||||||
MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
|
MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
|
||||||
@ -239,8 +253,6 @@ namespace Matchers {
|
|||||||
friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
|
friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
|
||||||
return matcher.m_matcher;
|
return matcher.m_matcher;
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
MatcherT const& m_matcher;
|
|
||||||
};
|
};
|
||||||
} // namespace Detail
|
} // namespace Detail
|
||||||
|
|
||||||
|
@ -17,8 +17,10 @@ namespace Catch {
|
|||||||
namespace Matchers {
|
namespace Matchers {
|
||||||
|
|
||||||
template<typename T, typename Alloc>
|
template<typename T, typename Alloc>
|
||||||
struct VectorContainsElementMatcher final : MatcherBase<std::vector<T, Alloc>> {
|
class VectorContainsElementMatcher final : public MatcherBase<std::vector<T, Alloc>> {
|
||||||
|
T const& m_comparator;
|
||||||
|
|
||||||
|
public:
|
||||||
VectorContainsElementMatcher(T const& comparator):
|
VectorContainsElementMatcher(T const& comparator):
|
||||||
m_comparator(comparator)
|
m_comparator(comparator)
|
||||||
{}
|
{}
|
||||||
@ -35,13 +37,13 @@ namespace Matchers {
|
|||||||
std::string describe() const override {
|
std::string describe() const override {
|
||||||
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
|
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
|
||||||
}
|
}
|
||||||
|
|
||||||
T const& m_comparator;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename AllocComp, typename AllocMatch>
|
template<typename T, typename AllocComp, typename AllocMatch>
|
||||||
struct ContainsMatcher final : MatcherBase<std::vector<T, AllocMatch>> {
|
class ContainsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
|
||||||
|
std::vector<T, AllocComp> const& m_comparator;
|
||||||
|
|
||||||
|
public:
|
||||||
ContainsMatcher(std::vector<T, AllocComp> const& comparator):
|
ContainsMatcher(std::vector<T, AllocComp> const& comparator):
|
||||||
m_comparator( comparator )
|
m_comparator( comparator )
|
||||||
{}
|
{}
|
||||||
@ -67,13 +69,13 @@ namespace Matchers {
|
|||||||
std::string describe() const override {
|
std::string describe() const override {
|
||||||
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
|
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<T, AllocComp> const& m_comparator;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename AllocComp, typename AllocMatch>
|
template<typename T, typename AllocComp, typename AllocMatch>
|
||||||
struct EqualsMatcher final : MatcherBase<std::vector<T, AllocMatch>> {
|
class EqualsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
|
||||||
|
std::vector<T, AllocComp> const& m_comparator;
|
||||||
|
|
||||||
|
public:
|
||||||
EqualsMatcher(std::vector<T, AllocComp> const& comparator):
|
EqualsMatcher(std::vector<T, AllocComp> const& comparator):
|
||||||
m_comparator( comparator )
|
m_comparator( comparator )
|
||||||
{}
|
{}
|
||||||
@ -93,12 +95,14 @@ namespace Matchers {
|
|||||||
std::string describe() const override {
|
std::string describe() const override {
|
||||||
return "Equals: " + ::Catch::Detail::stringify( m_comparator );
|
return "Equals: " + ::Catch::Detail::stringify( m_comparator );
|
||||||
}
|
}
|
||||||
std::vector<T, AllocComp> const& m_comparator;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename AllocComp, typename AllocMatch>
|
template<typename T, typename AllocComp, typename AllocMatch>
|
||||||
struct ApproxMatcher final : MatcherBase<std::vector<T, AllocMatch>> {
|
class ApproxMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
|
||||||
|
std::vector<T, AllocComp> const& m_comparator;
|
||||||
|
mutable Catch::Approx approx = Catch::Approx::custom();
|
||||||
|
|
||||||
|
public:
|
||||||
ApproxMatcher(std::vector<T, AllocComp> const& comparator):
|
ApproxMatcher(std::vector<T, AllocComp> const& comparator):
|
||||||
m_comparator( comparator )
|
m_comparator( comparator )
|
||||||
{}
|
{}
|
||||||
@ -129,13 +133,13 @@ namespace Matchers {
|
|||||||
approx.scale(static_cast<double>(newScale));
|
approx.scale(static_cast<double>(newScale));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<T, AllocComp> const& m_comparator;
|
|
||||||
mutable Catch::Approx approx = Catch::Approx::custom();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename AllocComp, typename AllocMatch>
|
template<typename T, typename AllocComp, typename AllocMatch>
|
||||||
struct UnorderedEqualsMatcher final : MatcherBase<std::vector<T, AllocMatch>> {
|
class UnorderedEqualsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
|
||||||
|
std::vector<T, AllocComp> const& m_target;
|
||||||
|
|
||||||
|
public:
|
||||||
UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target):
|
UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target):
|
||||||
m_target(target)
|
m_target(target)
|
||||||
{}
|
{}
|
||||||
@ -149,15 +153,12 @@ namespace Matchers {
|
|||||||
std::string describe() const override {
|
std::string describe() const override {
|
||||||
return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
|
return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
std::vector<T, AllocComp> const& m_target;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// The following functions create the actual matcher objects.
|
// The following functions create the actual matcher objects.
|
||||||
// This allows the types to be inferred
|
// This allows the types to be inferred
|
||||||
|
|
||||||
|
|
||||||
//! Creates a matcher that matches vectors that contain all elements in `comparator`
|
//! Creates a matcher that matches vectors that contain all elements in `comparator`
|
||||||
template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
|
template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
|
||||||
ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
|
ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
|
||||||
|
@ -36,7 +36,7 @@ namespace Catch {
|
|||||||
|
|
||||||
namespace Matchers {
|
namespace Matchers {
|
||||||
template <typename ArgT>
|
template <typename ArgT>
|
||||||
struct MatcherBase;
|
class MatcherBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
using StringMatcher = Matchers::MatcherBase<std::string>;
|
using StringMatcher = Matchers::MatcherBase<std::string>;
|
||||||
|
Loading…
Reference in New Issue
Block a user