What is the simple, idiomatic way, to check that a specific struct member validates a given concept ?
I tried the following and it does not work because { T::f } yields type float&:
#include <concepts>
struct foo {
float f;
};
// ok
static_assert(std::floating_point<decltype(foo::f)>);
template<typename T>
concept has_fp_member = requires (T t) {
{ T::f } -> std::floating_point;
};
// fails
static_assert(has_fp_member<foo>);
Where can I "remove" that useless reference being added on { T::f } ? Without making the code super ugly, adding new concepts, etc... my main requirement is that things stay readable !
e.g.
template<typename T>
concept has_fp_member = std::floating_point<decltype(T::f)>;
is very subpar, because my actual concept would check a large set of attributes, and I do not want a mess of std::foo<decltype(T::a)> && std::bar<decltype(T::b)> && ...
Note that I use float as an example but my question is about a general solution for any type / concept.
You might want to use macro:
#include <concepts>
#include <type_traits>
template <class T>
std::decay_t<T> decay_copy(T&&);
#define CHECK_MEMBER(name, type) \
{ decay_copy(t.name) } -> type
template<typename T>
concept has_member_variables = requires (T t) {
CHECK_MEMBER(f, std::floating_point);
CHECK_MEMBER(i, std::integral);
};
Demo.
Here's a partial solution but I would like to find something better (inspired from an answer to this question which has been removed)
auto prvalue(auto&& arg) { return arg; }
template<typename T>
concept has_fp_member = requires (T t) {
{ prvalue(T::f) } -> std::floating_point;
};
static_assert(has_fp_member<foo>);
It only supports cases where the member is copy-constructible though.
Related
I have the following case
template<typename Class>
concept has_member = requires (Class t)
{
// How can I write only either of the following conditions be satisfied?
{t.isInterface() }->std::same_as<bool>;
// or
{t.canInterface() }->std::same_as<bool>;
// or
// ... more conditions!
};
struct A {
bool isInterface() const { return true; }
};
struct B {
bool canInterface() const { return true; }
};
void foo(const has_member auto& A_or_B)
{
// do something
}
int main() {
foo(A{}); // should work
foo(B{}); // should work
}
Like I mentioned in the comments, I would like to logically or the requirements (in a single concepts), so that the class A and B can be passed to the doSomething().
As per my knowledge, the the current concept is checking all the requirements, that means a logical and.
If I take it apart to different concepts everything works, but I would need more concepts to be written tosatify the intention.
Is it possoble to combne into one? something like pseudocode
template<typename Class>
concept has_member = requires (Class t)
{
{t.isInterface() }->std::same_as<bool> || {t.canInterface() }->std::same_as<bool>;
// ...
};
Anytime you want to start building concepts, you should start with the point-of-use. That is, the place where you are going to use some template parameters for some purpose.
That means, you start with foo. How does foo "do something" with this interface? It can't be as simple as:
void foo(const has_member auto& A_or_B)
{
A_or_B.isInterface();
}
That will be a compile error for some of the has_member objects. So you will need to do this:
void foo(const has_member auto& A_or_B)
{
if constexpr(<stuff>)
A_or_B.isInterface();
else
A_or_B.hasInterface();
}
Where <stuff> is some compile-time conditional check to see which actual interface the object has. You could spell out the requires clause, but that would be unnecessarily wordy. So just stick them in a concept:
template<typename T>
concept has_is_interface = requires (Class t)
{
{t.isInterface() }->std::same_as<bool>;
};
template<typename T>
concept has_can_interface = requires (Class t)
{
{t.canInterface() }->std::same_as<bool>;
};
And now, your question answers itself:
template<typename Class>
concept has_member =
(has_is_interface || has_can_interface) &&
requires (Class t)
{
// ... more conditions!
};
That having been said, this design is generally wrongheaded. The reason is that it puts a lot of verbosity at the point of use.
What you probably want is to have a few free functions which can call the appropriate member interface:
bool do_is_interface(has_is_interface auto const& is_if)
{
return is_if.is_interface();
}
bool do_is_interface(has_can_interface auto const& is_if)
{
return is_if.can_interface();
}
And then your concept looks like:
template<typename Class>
concept has_member =
requires (Class t)
{
{ do_is_interface(t) } -> std::same_as<bool>
// ... more conditions!
};
Is it possoble to combine into one?
Yes, it is possible. You can write the conjunction of two requires as follows:
template<typename Class>
concept has_member =
requires (Class t) { {t.isInterface() }->std::same_as<bool>; }
|| //---> like this
requires (Class t) { {t.canInterface() }->std::same_as<bool>;};
// ... more conditions!
Demo
You can use the following concept form stolen from the standard
#include <concepts>
template<typename Class>
concept has_member = requires (Class t) {
requires (requires { { t.isInterface() } -> std::same_as<bool>; } ||
requires { { t.canInterface() } -> std::same_as<bool>; }
// add more functions
);
};
which allows us to add different member functions directly in the requires-clause.
I am trying to wrap my mind around C++ 20 concept and constraint by porting some of my old code.
struct Status
{
std::string status;
std::time_t statusDate;
};
struct CurrentStatusStack
{
std::vector<Status> statusVec;
std::vector<std::filesystem::path> ticketPathVec;
};
void setBar(CurrentStatusStack s)
{
ui->setLatestStatusMessage(s.statusVec.back().status);
ui->setLatestStatusMessagePath(s.ticketPathVec.back());
}
To translate the above code block to a similar but generic function setFoo(const T& t) and to make sure T type implements function that setBar requires I wrote a few concepts:
A ContainerOf concept
/*
`ContainerOf` requires container (could be a vector or a set
or other STL-like containers) to implement at least:
1. `cbegin()` that returns a const iterator pointer to the first element.
2. `empty()` an emptiness check fn.
*/
template <class Container, typename T>
concept ContainerOf = requires(Container a, T)
{
requires std::regular<Container>;
requires std::same_as<typename Container::value_type, T>;
{
a.cbegin()
} -> std::same_as<typename Container::const_iterator>;
{
a.empty()
} -> std::same_as<bool>;
};
A HasTicketPaths concept
/*
`HasTicketPaths` ensures that that structure given implements a `getTicketPaths`
function that returns a container that satisfies `ContainerOf` with
`std::filesystem::path` elements in it.
*/
template <typename T>
concept HasTicketPaths = requires(T t)
{
{
t.getTicketPaths()
} -> ContainerOf<fs::path>;
};
A IsStatus concept
/*
To set status the obj needs at least two elements:
1. Status string and
2. the date the status was posted,
`IsStatus` ensure those constrients by requiring `status` and
`statusDate` functions that return a `std::string` and `std::time_t`
respectively.
*/
template <typename T>
concept IsStatus = requires(T t)
{
{
t.status()
} -> std::same_as<std::string>;
{
t.statusDate()
} -> std::same_as<std::time_t>;
};
Now I think all have to do is somehow combine those two concepts in HasStatus concept and change the function prototype to
template <typename T>
requires HasStatus<T> && requires HasTicketPaths<T>
void setFoo(const T& t);
but I am not sure how to do that.
I imagined it'd look something like this
/*
`HasStatus` ensures that the container has a `getStatus` function that return
a container with each element type ensuring `IsStatus`'s requirement.
*/
template <typename T>
concept HasStatus = requires(T t)
{
{
t.getStatus()
} -> ContainerOf<IsStatus>;
};
but it generates the following error
invalid use of ‘decltype(auto) [requires ::IsStatus<<placeholder>, >]’ in template argument
70 | } -> ContainerOf<IsStatus>;
I think I misunderstood how concepts actually work but I am not sure where/what to look for.
A concept is not a type, so it can’t appear as a container element type—neither in the type of an object (this is why you have to use std::vector<std::any> to approximate std::vector<std::copyable>) nor in the type for your concept ContainerOf. Moreover, you can’t use a concept as a template argument, so you can’t have a higher-order ContainerLike concept.
What you can do is make a Container concept that checks only for empty, add the constraint
{ *c.cbegin() } -> IsStatus;
and apply it to t.getStatus() in another concept.
To help others that may stumble upon this question (or future me), following is a minimal complete/reproducible example for the way I ended up implementing that
#include <concepts>
#include <ctime>
#include <iostream>
#include <string>
#include <vector>
template <typename T>
concept IsStatus = requires(T t) {
{ t.getStatus() } -> std::same_as<const std::string&>;
{ t.getStatusDate() } -> std::same_as<const std::time_t&>;
};
template <class Container>
concept ContainerOfStatus = requires(Container c) {
{ c.empty() } -> std::same_as<bool>;
{ *c.cbegin() } -> IsStatus;
};
template <typename T>
concept HasStatus = requires(T t) {
{ t.getAllStatus() } -> ContainerOfStatus;
};
struct Status {
std::string status;
std::time_t statusDate;
const std::string& getStatus() const { return status; };
const std::time_t& getStatusDate() const { return statusDate; };
};
struct CurrentStatusStack {
std::vector<Status> statusVec;
const std::vector<Status>& getAllStatus() const { return statusVec; };
};
template <typename T>
requires HasStatus<T>
void printStatus(T t) {
std::cout << t.getAllStatus().cbegin()->getStatus() << std::endl;
}
int main() {
Status s{"some status", std::time_t(nullptr)};
CurrentStatusStack st;
st.statusVec.push_back(s);
std::cout << st.getAllStatus().cbegin()->getStatus() << std::endl;
printStatus(st);
}
on godbolt compiler explorer
Here's another very similar question
I just started doing concepts. The syntax for checking expressions is really useful and removes me a lot of boilerplate I had with sfinae. However I wondered how can I check if an expression can be used in a constexpr context. Sometimes those expression results in void. The way I can imagine would look like this, with the constexpr keyword uncommented:
template<typename T>
concept foo = requires(T t) {
/* constexpr */ { t.bar(); } -> std::same_as<void>;
/* constepxr */ { T::foo; };
}
However, I highly doubt this is the right syntax. Is there an easy way to check for the constexpr-ness of the expression in a concept?
I don't want to check if the evaluation of a constexpr function won't fail for all possible values of t, I want to know if I can put that expression at a place where the compiler expect something to be evaluable at compile time.
I think the expected concept can be created using std::bool_constant, which has the property that the failure of substitution in its argument of not-constant expression is not a compilation error, but just makes the concept false.
The proposed solution is
#include <concepts>
#include <type_traits>
template<typename T>
concept HasConstexprVoidBar =
requires(T t) {
{ t.bar() } -> std::same_as<void>;
{ std::bool_constant<(T{}.bar(), true)>() } -> std::same_as<std::true_type>;
};
struct A {
constexpr void bar() {}
};
struct B {
void bar() {}
};
int main() {
// concept check passes for constexpt A::bar()
static_assert( HasConstexprVoidBar<A> );
// concept check fails for B::bar()
static_assert( !HasConstexprVoidBar<B> );
}
Here the concept HasConstexprVoidBar is verified successfully for struct A having constexpr void method bar and evaluates to false for struct B having not-constexpr method.
Demo: https://gcc.godbolt.org/z/nsx9z99G4
How about this one:
template <typename F, auto Test=std::bool_constant<(F{}(), true)>()>
consteval auto is_constexpr (F) { return Test; }
EDIT: the one I wrote before doesn't work in clang > 13.0 for some reason, but this does
and then
requires (T t) {
is_constexpr([]{ T{}.cols(); });
}
Here we use the C++20's consteval func to force constexpr check inside and the fact that since C++20 simple lambdas can be default-constructed, hence we construct it here from F{} and here we go :)
The following (academically built, non working) code has two "problems" that I know how to solve in an ugly way. I would like a pretty one.
#include <type_traits>
template<class T> struct Integer {
Integer(T t) { static_assert(std::is_integral_v<T>, "Must be int"); }
};
template<class T> struct Floating {
Floating(T t) { static_assert(std::is_floating_point_v<T>, "Must be flating point"); }
};
template<class T> void brol(T t)
{
Integer i(t); //these two cannot work together
Floating f(t);
template<class U> auto stuff = [] (U& u) -> void //invalid syntax : no template on bloc scope
{ u *= 2; }
if(std::is_integral_v<T>)
stuff(i);
else
stuff(f);
}
int main()
{
brol(2);
brol(2.0);
}
Obviously, I cannot build an Integer and a Floating inside if the function brol(), because of the static_asserts. This problem can be easily solved with SFINAE tests, either nicely with std::enable_if_v or with some ... -> decltype( ... ) trickery. In the same way, I can also improve the end of the function body and avoid the if/else block
The lambda stuff() cannot be template (because the compiler states it cannot). I * could * make it independent and hide it in a subnamespace, but it's not so great.
HOWEVER, I cannot simply make an SFINAE test with std::enable_if, as the prototypes will only differ with their default template arguments and this is illegal (cf. documentation, section Notes in std::enable_if).
On the other hand, I don't know how to solve ussie 2 nicely...
It seems to me that std::conditional should solve all your problems
template <typename T>
void brol (T t)
{
using U = std::conditional_t<std::is_integral_v<T>, Integer<T>, Floating<T>>;
U u{t};
auto stuff = [] (U & u) -> void { u *= 2; };
stuff(u);
}
Anyway, the problem with the lambda could be solved also with a generic lambda (as pointed by rustyx)
auto stuff = [] (auto & u) -> void { u *= 2; };
Regarding the final if, in C++17 (you're using std::is_integral_v, so you're using C++17) you can also use if constexpr in similar circustances
if constexpr (std::is_integral_v<T>)
stuff(i);
else
stuff(f);
but remain the problem that you have to define i or f.
It's unclear what the purpose of your Integer and Floating is...
But regarding the "template<class U> auto stuff = [](U& u)" part, C++14 does make the lambda a template automatically if you simply use auto as the parameter type:
template<class T> void brol(T t) {
auto stuff = [](auto& u)
{ u *= 2; };
stuff(t);
}
int main() {
brol(2);
brol(2.0);
}
Below is excerpted from cppref:
#include <experimental/type_traits>
template<class T>
using copy_assign_t = decltype(std::declval<T&>() = std::declval<const T&>());
struct Meow { };
using namespace std::experimental;
int main()
{
static_assert(is_detected_v<copy_assign_t, Meow>,
"Meow should be copy assignable!"); // version 1
static_assert(is_copy_assignable_v<Meow>,
"Meow should be copy assignable!"); // version 2
}
Is there any difference between version 1 and version 2?
Is there a typical use case that must use is_detected_v?
std::is_detected can be used as a building block for std::is_copy_assignable. If you want to check for copy-assignability, you should use std::is_copy_assignable. If you need to check for the existence of a custom operation/member function, std::is_detected gives you an easy way of doing that.
template<class T>
using foo_detector = decltype(std::declval<T&>().foo());
static_assert(!is_detected_v<foo_detector, Meow>, "Meow shouldn't have `.foo()`!");
A realistic example use case is unification of different APIs:
template<class T>
using clean_detector = decltype(std::declval<T&>().clean());
template<class T>
using clear_detector = decltype(std::declval<T&>().clear());
template <typename T>
auto clear(const T& x) -> std::enable_if_t<is_detected_v<has_clean, T>>
{
x.clean();
}
template <typename T>
auto clear(const T& x) -> std::enable_if_t<is_detected_v<has_clear, T>>
{
x.clear();
}
Usage example:
struct Foo { void clean(); };
struct Bar { void clear(); };
int main()
{
Foo f; Bar b;
clear(f);
clear(b);
}
Is detected lets you determine if a substitution would cause failure.
This lets you write a trait (ad hoc or not) that expresses a SFINAE check.
Such traits can be passed around, used as tags, and inverted.
So I can write can_foo easily with it. Then I can have two overloads, one if can_foo the other if !can_foo.
With traditional SFINAE, writing an overload that is valid if can_foo is easy; but !can_foo is hard.
We can also tag dispatch or use it for constexpr if calculation.
auto distance( Iterator A, Sentinal B ){
if constexpr (can_subtract_v<Sentinal&, Iterator&>){
return B-A;
} else {
// count manually
}
}