c++20 concept check for function being declared const - c++

I want to test out the new concepts feature in c++20 and I was wondering if I can create a concept that checks for the existence of a function that is declared const.
I want the check to fail if the function exists with the right type but isn't const. I couldn't find anything relevant here: https://en.cppreference.com/w/cpp/concepts
I have this
template <typename T>
concept hasToString = requires (T val) {
{ val.toString() } /* const here gives error */ -> std::same_as<std::string>;
};
void f(hasToString auto bar)
{
std::cout << bar.toString();
}

You can make the parameter const:
template <typename T>
concept hasToString = requires (T const val) {
{ val.toString() } -> std::same_as<std::string>;
};
Concepts check usage patterns, and so if what you want to check is calling a member function on a const object, you need to construct that scenario.
Note that this depends on what you want to happen if T happens to be a reference type. If you want this to work:
void f(hasToString auto&& bar)
Then T might be a reference type, and if you still want it really be const, then you need to turn a type like T& into T const. The long-way of writing that is:
template <typename T>
concept hasToString = requires (std::remove_reference_t<T> const val) {
{ val.toString() } -> std::same_as<std::string>;
};
But if you do this enough times, you could consider adding an alias template to handle that.

You can always check the expression is well-formed when applied to a const object argument.
template <typename T>
concept hasToString = requires (T val) {
{ std::as_const(val).toString() } -> std::same_as<std::string>;
};
or
template <typename T>
concept hasToString = requires (T val, T const cval) {
{ cval.toString() } -> std::same_as<std::string>;
};
Adding an extra object parameter is probably more inline with how abstract concept requirements are defined (well, if your requires-expression checks for more than one requirement). You can have as many parameters as you want in the require-expression's parameter list.

Related

Concepts: require a function on a type without default constructing that type

I need to require of some type A that there exists a function f(A, A::B).
I'm testing that by calling f with instances of A and A::B. Is there a less ostentatious way to test against an instance of the dependent type without requiring default constructible?
template <class Container>
concept CanPutData = requires (Container a)
{
//put(typename Container::data_type{}, a); // overconstrained
put(*reinterpret_cast<typename Container::data_type*>(0), a); // oof
};
void test(CanPutData auto container) {}
template<class Container>
void put(typename Container::data_type const& data, Container& into) {}
template<class Data>
struct data_container { using data_type = Data; };
struct not_default_constructible_data { int& v; };
int main()
{
test(data_container<not_default_constructible_data>{});
return 0;
}
Same way you're already getting a Container without requiring that one to be default constructible: by just sticking it in the parameter list of the requires expression:
template <class Container>
concept CanPutData = requires (Container container, typename Container::data_type data)
{
put(data, container);
};
You can also put Container::data_type in the template parameter list
template <class Container, class DataType = Container::data_type>
concept CanPutData = requires (DataType d, Container a)
{
put(d, a);
};
Demo
The advantage of this form is that when the requires-clause is relatively large, if the Container does not have a data_type member, the compiler will only report that the template parameter list does not satisfy the constraints, rather than output the entire requires clause.

How to best solve "void foo( const T& t = T() )" when T==void

I have a function which has an option parameter of type T.
template<typename T>
void foo( const T& t = T() )
{ t.stuff; }
This was all fine but I now have a scenario where T becomes void. In this case I expected a no-operation empty function. The only workable solution I have requires three separate declarations and I have many of these sort of methods:
template<typename T>
void foo( const T& t)
{ t.stuff; }
template<typename T>
inline void foo()
{ foo(T()); }
template<>
inline void foo<void>() {}
Ideally I hope there should be a more elegant solution to overload the 'Void' function without resorting to a 3rd declaration? Especially with new C++17 solving so many things these days! A more concise syntax that was shorter coudl be nice...
A simpler solution (in that there are only 2 overloads) would be something like this:
template<typename T>
void foo( const T& t = T() ) {
t.stuff;
}
template<typename T>
std::enable_if_t<std::is_void_v<T>>
foo() {}
// void foo(const T&); is the only viable overload for non-void T,
// since std::enable_if_t SFINAEs
// void foo(); is the only viable overload for void T,
// since const T& forms a reference to void
And this can be slightly shortened with alias templates since you use this pattern a lot:
template<typename T, typename TypeIfVoid = void>
using if_void = std::enable_if_t<std::is_void_v<T>, TypeIfVoid>;
template<typename T>
void foo(const T& t = T()) {
t.stuff;
}
template<typename T>
if_void<T> foo() {}
Two default template parameters will do it:
template<class T> using FallbackT = std::conditional_t<std::is_void_v<T>, int, T>;
template<class T = int&, class U = FallbackT<T>>
void foo(U const& t = U()) {
if constexpr (!std::is_void_v<T>)
t.stuff();
}
Example.
int& is the default for T so that compilation fails (default-constructing a reference at U()) if someone attempts to call foo() without either a template argument or an actual argument (try uncommenting it in the example).
I'm using int within FallbackT alias template since U just needs to be something that is default-constructible - this isn't visible to the user.
If you want to be fancy (and prevent misuse) you could add a variadic guard and use closure types:
template<
class T = decltype([]{})&,
class...,
class U = std::conditional_t<std::is_void_v<T>, decltype([]{}), T>>
void foo(U const& t = U()) {
if constexpr (!std::is_void_v<T>)
t.stuff();
}
Here, the variadic guard prevents specifying U explicitly, as e.g. foo<int, long>(); the closure types make it impossible for someone to call foo with those types by any other means - this is probably unnecessary.
Ideally I hope there should be a more elegant solution to overload the 'Void' function without resorting to a 3rd declaration? Especially with new C++17 solving so many things these days! A more concise syntax that was shorter coudl be nice...
Well... without a 3rd declaration, yes (you can use only one).
More elegant... it's question of tastes, I suppose.
More coincise syntax... well... almost the same, I suppose.
Anyway, I propose the following version, if constexpr and std::conditional_t based.
template <typename T,
typename U = std::conditional_t<std::is_same_v<T, void>,
int,
T>>
void foo (U const & u = U{})
{
if constexpr ( std::is_same_v<T, void> )
{ /* do nothing */ }
else
{ /* do u.stuff; */ }
}

Type requirement in C++ concepts (C++20)

I am learning the newly implemented concepts of C++20 standard using g++ 10.
I am stuck with a simple type requirement. Namely I want to implement a requirement for a template argument T to have T::inner member name.
Here is my code with error. What is wrong with this simple code and how to fix it?
#include<concepts>
template<typename T>
concept ContainsInner = requires
{
typename T::inner;
};
template<ContainsInner T>
struct S{};
struct Q
{
int inner;
};
int main()
{
S<Q> s; // instantiate S with Q for template type,
// which must satisfy the ContainsInner concept.
// Q indeed contains the inner name but still the compilation fails
}
This:
template<typename T>
concept ContainsInner = requires
{
typename T::inner;
};
is requiring that T has a type named inner. Which gcc tells you in its error:
source>:6:12: note: the required type 'typename T::inner' is invalid
6 | typename T::inner;
| ~~~~~~~~~^~~~~~~~~
Q doesn't have a type named inner. If what you want is to have a member variable named inner, then you want:
template<typename T>
concept ContainsInner = requires(T t) {
t.inner;
};
Note that this doesn't even check what type it is, just that it exists. Which isn't very useful. Maybe you want to require that it's an int:
template<typename T>
concept ContainsInner = requires(T t) {
{ t.inner } -> std::same_as<int&>;
};

How to decltype template method C++?

I write interfaces through concepts for implementation validation.
There are no problems with conventional methods:
// Interface realization
struct Realization
{
int* TestMethod(const std::string& aStr)
{
return (int *) aStr.c_str();
}
};
// Concept
template <typename T>
concept IRealization = std::is_same_v<decltype(&T::TestMethod), int* (T::*)(const std::string&)>;
// and then, for example
void Check()
{
static_assert(IRealization<Realization>)
}
but when I try to write a similar check for a template method:
// Interface realization
struct Realization
{
template <typename T>
int* TemplateMethod(const T& aStr)
{
return (int *) aStr.c_str();
}
};
, I run into a problem of dectype a template method, because I cant write
decltype(&RealizationImpl::TemplateMethod)
(at the time of checking the interface, I do not know the type that will be substituted)
Please tell me, can I somehow get the signature of the template function without type, or otherwise solve my problem? Thanks!
You should not write concepts like this. A concept should never check for something as specific as a member function with an exact signature. A concept should instead say that, given an instance of the type in question, I should be able to do i.memberFunc(...), where ... is the list of parameters.
For example, your "IRealization" concept (please don't prefix concepts with I. Concepts are not interfaces) ought to say "T must have a member function which can be called given a std::string argument and results in something which is convertible to an int." That would look like:
template <typename T>
concept IRealization = requires(T t, std::string str)
{
{ t.TestMethod(str) } -> convertible_to<int>;
};
This allows the user to provide a TestMethod that takes, for example, std::string_view instead of std::string. There's no point in being so incredibly restrictive on the type.
A concept that checks for T having a member function which is callable with some type U would have to be templated on both T and U:
template <typename T, typename U>
concept IRealization = requires(T t, U u)
{
{ t.TestMethod(u) } -> convertible_to<int>;
};
What is the problem with adding another type to the concept?
// Concept
template <typename T, typename U>
concept IRealization = std::is_same_v<decltype(&T::template TestMethod<U>), int* (T::*)(const U&)>;
For instance.
You could even make it prettier by creating a typedef -
template<typename T, typename U>
using FuncT = decltype(&T::template TestMethod<U>);

Concept definition requiring a constrained template member function

Note: everything that follows uses the Concepts TS implementation in GCC 6.1
Let's say I have a concept Surface, like the following:
template <typename T>
concept bool Surface() {
return requires(T& t, point2f p, float radius) {
{ t.move_to(p) };
{ t.line_to(p) };
{ t.arc(p, radius) };
// etc...
};
}
Now I want to define another concept, Drawable, which matches any type with a member function:
template <typename S>
requires Surface<S>()
void draw(S& surface) const;
i.e.
struct triangle {
void draw(Surface& surface) const;
};
static_assert(Drawable<triangle>(), ""); // Should pass
That is, a Drawable is something which has a templated const member function draw() taking an lvalue reference to something which satisfies the Surface requirements. This is reasonably easy to specify in words, but I can't quite work out how to do it in C++ with the Concepts TS. The "obvious" syntax doesn't work:
template <typename T>
concept bool Drawable() {
return requires(const T& t, Surface& surface) {
{ t.draw(surface) } -> void;
};
}
error: 'auto' parameter not permitted in this context
Adding a second template parameter allows the concept definition to compile, but:
template <typename T, Surface S>
concept bool Drawable() {
return requires(const T& t, S& s) {
{ t.draw(s) };
};
}
static_assert(Drawable<triangle>(), "");
template argument deduction/substitution failed:
couldn't deduce template parameter 'S'
now we can only check whether a particular <Drawable, Surface> pair matches the Drawable concept, which isn't quite right. (A type D either has the required member function or it does not: that doesn't depend on which particular Surface we check.)
I'm sure it's possible to do what I'm after, but I can't work out the syntax and there aren't too many examples online yet. Does anybody know how to write a concept definition which requires type to have a constrained template member function?
What you're looking for is for a way for the compiler to synthesize an archetype of Surface. That is, some private, anonymous type that minimally satisfies the Surface concept. As minimally as possible. Concepts TS doesn't currently allow for a mechanism for automatically synthesizing archetypes, so we're left with doing it manually. It's quite a complicated process, since it's very easy to come up with archetype candidates that have way more functionality that the concept specifies.
In this case, we can come up with something like:
namespace archetypes {
// don't use this in real code!
struct SurfaceModel {
// none of the special members
SurfaceModel() = delete;
SurfaceModel(SurfaceModel const& ) = delete;
SurfaceModel(SurfaceModel&& ) = delete;
~SurfaceModel() = delete;
void operator=(SurfaceModel const& ) = delete;
void operator=(SurfaceModel&& ) = delete;
// here's the actual concept
void move_to(point2f );
void line_to(point2f );
void arc(point2f, float);
// etc.
};
static_assert(Surface<SurfaceModel>());
}
And then:
template <typename T>
concept bool Drawable() {
return requires(const T& t, archetypes::SurfaceModel& surface) {
{ t.draw(surface) } -> void;
};
}
These are valid concepts, that probably work. Note that there's a lot of room for even more refinement on the SurfaceModel archetype. I have a specific function void move_to(point2f ), but the concept just requires that it's callable with an lvalue of type point2f. There's no requirement that move_to() and line_to() both take an argument of type point2f, they could both take complete different things:
struct SurfaceModel {
// ...
struct X { X(point2f ); };
struct Y { Y(point2f ); };
void move_to(X );
void line_to(Y );
// ...
};
This kind of paranoia makes for a better archetype, and serves to illustrate how complex this problem could be.