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 :)
Related
While implementing a compressed_tuple class for some project I'm working on, I ran into the following issue: I can't seem to pass instances of this type to std::apply, even though this should be possible according to: https://en.cppreference.com/w/cpp/utility/apply.
I managed to reproduce the issue quite easily, using the following fragment (godbolt):
#include <tuple>
struct Foo {
public:
explicit Foo(int a) : a{ a } {}
auto &get_a() const { return a; }
auto &get_a() { return a; }
private:
int a;
};
namespace std {
template<>
struct tuple_size<Foo> {
constexpr static auto value = 1;
};
template<>
struct tuple_element<0, Foo> {
using type = int;
};
template<size_t I>
constexpr auto get(Foo &t) -> int & {
return t.get_a();
}
template<size_t I>
constexpr auto get(const Foo &t) -> const int & {
return t.get_a();
}
template<size_t I>
constexpr auto get(Foo &&t) -> int && {
return std::move(t.get_a());
}
template<size_t I>
constexpr auto get(const Foo &&t) -> const int && {
return move(t.get_a());
}
} // namespace std
auto foo = Foo{ 1 };
auto f = [](int) { return 2; };
auto result = std::apply(f, foo);
When I try to compile this piece of code, it seems that it cannot find the std::get overloads that I have defined, even though they should perfectly match. Instead, it tries to match all of the other overloads (std::get(pair<T, U>), std::get(array<...>), etc.), while not even mentioning my overloads. I get consistent errors in all three major compilers (MSVC, Clang, GCC).
So my question is whether this is expected behavior and it's simply not possible to use std::apply with user-defined types? And is there a work-around?
So my question is whether this is expected behavior and it's simply
not possible to use std::apply with user-defined types?
No, there is currently no way.
In libstdc++, libc++, and MSVC-STL implementations, std::apply uses std::get internally instead of unqualified get, since users are prohibited from defining get under namespace std, it is impossible to apply std::apply to user-defined types.
You may ask, in [tuple.creation], the standard describes tuple_cat as follows:
[Note 1: An implementation can support additional types in the
template parameter pack Tuples that support the tuple-like protocol,
such as pair and array. — end note]
Does this indicate that other tuple utility functions such as std::apply should support user-defined tuple-like types?
Note that in particular, the term "tuple-like" has no concrete definition at
this point of time. This was intentionally left C++ committee to make this gap
being filled by a future proposal. There exists a proposal that is
going to start improving this matter, see P2165R3.
And is there a work-around?
Before P2165 is adopted, unfortunately, you may have to implement your own apply and use non-qualified get.
The question has throughly been answered by #康桓瑋. I'm going to post some more details on how to provide a workaround.
First, here is a generic C++20 apply function:
#include<tuple>
#include<functional>
namespace my
{
constexpr decltype(auto) apply(auto&& function, auto&& tuple)
{
return []<size_t ... I>(auto && function, auto && tuple, std::index_sequence<I...>)
{
using std::get;
return std::invoke(std::forward<decltype(function)>(function)
, get<I>(std::forward<decltype(tuple)>(tuple)) ...);
}(std::forward<decltype(function)>(function)
, std::forward<decltype(tuple)>(tuple)
, std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<decltype(tuple)> > >{});
}
} //namespace my
The own namespace is useful so that the custom apply does not interfere with the std-version. The unqualified call to get means (quoting #Quuxplusone from his blog, which gives the best explanation I encountered so far):
An unqualified call using the two-step, like using my::xyzzy;
xyzzy(t), indicates, “I know one way to xyzzy whatever this thing may
be, but T itself might know a better way. If T has an opinion, you
should trust T over me.”
You can then roll out your own tuple-like class,
struct my_tuple
{
std::tuple<int,int> t;
};
template<size_t I>
auto get(my_tuple t)
{
return std::get<I>(t.t);
}
namespace std
{
template<>
struct tuple_size<my_tuple>
{
static constexpr size_t value = 2;
};
}
With the overload of get() and the specialization of std::tuple_size, the apply function then works as expected. Moreover, you can plug in any compliant std-type:
int main()
{
auto test = [](auto ... x) { return 1; };
my::apply(test, my_tuple{});
my::apply(test, std::tuple<int,double>{});
my::apply(test, std::pair<int,double>{});
my::apply(test, std::array<std::string,10>{});
}
DEMO
I have a simple code snippet below, which compiles using:
g++-9 -std=c++2a -fconcepts
This is trying to define a concept that requires the presence of a function. I would expect the output to be "yes" but it's not... Any idea why? Thanks.
#include <iostream>
template <typename T>
concept bool HasFunc1 =
requires(T) {
{ T::func1() } -> int;
};
struct Test
{
int func1()
{
return 5;
}
};
int main()
{
if constexpr (HasFunc1<Test>)
std::cout << "yes\n";
}
You are testing for presence of a static member function. What you want is
template <typename T>
concept bool HasFunc1 =
requires(T t) {
{ t.func1() } -> int;
};
Try calling it yourself:
Test::func1();
prog.cc: In function 'int main()':
prog.cc:19:14: error: cannot call member function 'int Test::func1()' without object
19 | Test::func1();
| ^
Oh, right. func1 should either be a static member function, or you should call it on an instance inside your concept:
template <typename T>
concept bool HasFunc1 =
requires(T t) {
{ t.func1() } -> int;
};
#makogan asked (buried 19 deep in comments): what if func has arguments?
The answer is: for simple cases, manufacture parameters using constructors, or new expression. (Not particularly readable, but way more readable than the probably correct way given below).
template <typename T>
concept HasFunc1 =
requires(T t) {
{ t.func1( int() ) } -> std::same_as<int>;
};
For more complex examples, you can declare test parameters in the requires clause argument list:
concept IsCoServiceImplementation = requires(
T t,
CoServiceReply<typename T::return_type> *completionCallback)
{
{ T::return_type };
{t.OnSuspend(completionCallback) };
{t.OnCancel(completionCallback) };
};
This concept does have the desired intent (for me): it mostly converts nested-10-deep error messages about failures to meet a template contract into almost readable first-level error messages.
There's still a strange disconnect between the required code and the concept. It would be really nice to have tighter constraints on parameter types. And testing const-ness is grimly difficult. Far from the feature I had hope for. :-(
I'm still struggling with c++20 features. I'm open to suggestions as to how to do this better.
(A CoService, in case you're wondering, is an experimental attempt I'm working on to make it easier to marshal coroutine code back onto non-coroutine code with minimum fuss and bother).
One can run the concept check at the compile time.
(The OP's check evaluates only at the run time.)
First, prepare a checker function (technically a templated variable):
template <HasFunc1 h>
constexpr bool HasFunc1_satisfied = true;
Then do the check somewhere.
// The class to be tested
struct Test
{
int func1()
{
return 5;
}
};
// Do the test at the compile time
static_assert(HasFunc1_satisfied< Test >);
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);
}
While compiling the following (reduced) code:
#include <tuple>
#include <stdlib.h>
template<size_t N> struct tying;
template<> struct tying<1> {static auto function(auto& o) -> decltype(auto) {auto& [p1] = o;return std::tie(p1);}};
template<> struct tying<2> {static auto function(auto& o) -> decltype(auto) {auto& [p1,p2] = o;return std::tie(p1,p2);}};
template<typename T, size_t N> concept bool n_components =
requires(T& object) {
{ tying<N>::function(object) };
};
typedef struct
{
int a;
float b;
} test_t;
int main(int argc, char* argv[])
{
constexpr size_t n = 1;
constexpr bool t = n_components<test_t, n>;
printf("n_components<test_t, %d>: %s\n", n, t ? "yes" : "nope");
return 0;
}
with gcc specific options -std=c++1z -fconcepts the gcc (version 7.3.0, and x86-64 gcc-trunk at godbolt also) fails with an error:
error: only 1 name provided for structured bindingnote: while 'test_t' decomposes into 2 elements
I was very surprised by this, as the error "raises" inside the requires-expression, and to my understanding, this should result in n_components constraint to get evaluated to false, as the requirement is not satisfied.
Note: replacing constexpr size_t n = 1; with other values "fixes" the error, so I presume the n_components constraint is not to blame:
replacing with n = 2 the n_components constraint evaluates to "true" as expected.
replacing with n = 3 the n_components constraint evaluates to "false" as expected - due to referring to unspecialized tying<3> struct.
It seems there are no other compilers supporting the c++ concepts and structured bindings available yet to compare with.
PS. I was playing around with "poor man's reflection" a.k.a. magic_get and hoped to replace the unreliable is_braces_constructible trait with something more robust...
Here's a partial solution for clang since 5.0.
It is distilled from a recent reddit post
Find the number of structured bindings for any struct
The method is non-standard, using gnu extension statement expressions to effectively SFINAE on structured binding declarations. It is also non-portable as gcc fails to parse structured binding in statement expression in a lambda trailing return type.
template <typename... Ts> struct overloads : Ts... { using Ts::operator()...; };
template <typename... Ts> overloads(Ts...) -> overloads<Ts...>;
template <typename T>
auto num_bindings_impl() noexcept {
return overloads{
[](auto&& u, int) -> decltype(({auto&& [x0] = u; char{};}))(*)[1] {return {};},
[](auto&& u, int) -> decltype(({auto&& [x0,x1] = u; char{};}))(*)[2] {return {};},
[](auto&& u, int) -> decltype(({auto&& [x0,x1,x2] = u; char{};}))(*)[3] {return {};},
[](auto&& u, int) -> decltype(({auto&& [x0,x1,x2,x3] = u; char{};}))(*)[4] {return {};},
[](auto&& u, unsigned) -> void {}
}(declval<T>(), int{});
};
This implementation to be used only in unevaluated context and expanding out
for as many bindings as you wish, up to compiler implementation limits.
Then, you can define traits that work up to that limit:
template <typename T>
inline constexpr bool has_bindings = [] {
if constexpr ( ! is_empty_v<T> )
return ! is_void_v< decltype(num_bindings_impl<T>()) >;
else return false;
}();
template <typename T>
inline constexpr unsigned num_members = [] {
if constexpr ( ! is_empty_v<T> )
return sizeof *num_bindings_impl<T>();
else return 0;
}();
https://godbolt.org/z/EVnbqj
The immediate situation
I was very surprised by this, as the error "raises" inside the requires-expression
To be precise, the error happens in the body of the functions you wrote. We can boil down your code to:
void function(auto& arg);
void function_with_body(auto& arg)
{
arg.inexistent_member;
}
template<typename Arg>
concept bool test = requires(Arg arg) { function(arg); };
// never fires
static_assert( test<int> );
template<typename Arg>
concept bool test_with_body = requires(Arg arg) { function_with_body(arg); };
// never fires
static_assert( test_with_body<int> );
(on Coliru)
As far as the requires expression are concerned, the function calls are valid C++ expressions: there's nothing tricky about their return and parameter types, and the supplied argument can be passed just fine. No check is performed for the function bodies, and with good reason: the function or function template may have been declared but not defined yet (i.e. as is the case for function). Instead, it's normally up to the function template writer to make sure that the function body will be error-free for all (sensible) specializations.
Your code uses return type deduction, but that won't make too much of a difference: you can declare a function with a placeholder type (e.g. decltype(auto)) without defining it, too. (Although a call to such a function is in fact an invalid expression since the type cannot be deduced and that's visible to a requires expression, but that's not what you're doing.) In pre-concepts parlance, return type deduction is not SFINAE-friendly.
Structured bindings
As to your wider problem of writing a constraints around structured bindings, you are out of luck. As best as I know these are all the obstacles:
There is no interface for querying the decomposition size of a type, even though the implementation blatantly has access to the information in order to type-check structured binding declarations. (std::tuple_size is for tuple-like types only.). This could be worked around by blindingly trying sizes in increasing order though.
You can only ever write constraints around types and expressions, but structured bindings can only appear in normal variable declarations. (If function parameters could be structured bindings we could work around that.) No trickery with lambda expressions allowed because these can't appear in an unevaluated operand, such as in the body of a requires expression.
The closest you can go is emulate what the language is doing during an actual structured binding. That can get you support for tuple-like types (including arrays), but not 'dumb' aggregates.
(range-for is another case of a statement-level constraint that you can't fit into an expression constraint, and where emulating the language can only get you so far.)
Maybe I missed something, but I can't find any hints: is there a constexpr ternary operator in C++17 equivalent to constexpr-if?
template<typename Mode>
class BusAddress {
public:
explicit constexpr BusAddress(Address device) :
mAddress(Mode::write ? (device.mDevice << 1) : (device.mDevice << 1) | 0x01) {}
private:
uint8_t mAddress = 0;
};
No, there is no constexepr conditional operator. But you could wrap the whole thing in a lambda and immediately evaluate it (an IIFE):
template<typename Mode>
class BusAddress {
public:
explicit constexpr BusAddress(Address device)
: mAddress([&]{
if constexpr (Mode::write) {
return device.mDevice << 1;
}
else {
return (device.mDevice << 1) | 0x01;
}
}())
{ }
private:
uint8_t mAddress = 0;
};
It may not be the sexiest code ever, but it gets the job done. Note that lambdas are constexpr by default where possible as of N4487 and P0170.
You seem to be acting under the belief that if constexpr is a performance optimization. It isn't. If you put a constant expression in a ?: clause, any compiler worth using will figure out what it resolves to and remove the condition. So the code as you have written it will almost certainly compile down to a single option, for a particular Mode.
The principle purpose of if constexpr is to eliminate the other branch entirely. That is, the compiler doesn't even check to see if it is syntactically valid. This would be for something where you if constexpr(is_default_constructible_v<T>), and if it is true, you do T(). With a regular if statement, if T isn't default constructible, T() will still have to be syntactically valid code even if the surrounding if clause is a constant expression. if constexpr removes that requirement; the compiler will discard statements that are not in the other condition.
This becomes even more complicated for ?:, because the expression's type is based on the types of the two values. As such, both expressions need to be legal expressions, even if one of them is never evaluated. A constexpr form of ?: would presumably discard the alternative that is not taken at compile time. And therefore the expression's type should really only be based on one of them.
That a very different kind of thing.
Accepted answer can also be translated into a template function for convenience:
#include <type_traits>
#include <utility>
template <bool cond_v, typename Then, typename OrElse>
decltype(auto) constexpr_if(Then&& then, OrElse&& or_else) {
if constexpr (cond_v) {
return std::forward<Then>(then);
} else {
return std::forward<OrElse>(or_else);
}
}
// examples
struct ModeFalse { static constexpr bool write = false; };
struct ModeTrue { static constexpr bool write = true; };
struct A {};
struct B {};
template <typename Mode>
auto&& test = constexpr_if<Mode::write>(A{}, B{});
static_assert(std::is_same_v<A&&, decltype(test<ModeTrue>)>);
static_assert(std::is_same_v<B&&, decltype(test<ModeFalse>)>);
const A a;
B b;
template <typename Mode>
auto&& test2 = constexpr_if<Mode::write>(a, b);
static_assert(std::is_same_v<const A&, decltype(test2<ModeTrue>)>);
static_assert(std::is_same_v<B&, decltype(test2<ModeFalse>)>);