Equivalent ternary operator for constexpr if? - c++

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>)>);

Related

Unable to use std::apply on user-defined types

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

Checking for constexpr in a concept

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 :)

Emulating the special properties of sizeof with constexpr

In C++ sizeof is somewhat unique in that it's legal to write this:
int x;
sizeof(x); // a variable
As well as simply:
sizeof(int); // a type
(There's a third even weirder variant I'd rather ignore for now though, with no parenthesis needed, since I'm pretty certain that is impossible to emulate)
I'd like to be able to replicate this behaviour myself. To motivate it I've got an example bitsof operator.
#include <climits>
template <typename T>
struct bits_traits {
enum { value = sizeof(T) * CHAR_BIT };
};
struct int_12_bit {
enum { bits = 12 };
// Let's pretent this has a bunch of code for looking and feeling like a 12bit int in a helpful and portable way
};
template <>
struct bits_traits<int_12_bit> {
enum { value = int_12_bit::bits };
};
#define bitsof(x) bits_traits<x>::value
int main() {
using std::size_t;
size_t b = bitsof(int);
size_t a = bitsof(int_12_bit);
int_12_bit x;
size_t c = bitsof(x); // <-- Not cool
}
Clearly I could have written the whole thing in terms of a macro, using sizeof, e.g.
#define bitsof(x) (sizeof(x) * CHAR_BIT)
But then I lose the ability to "specialise" it.
And equally I could write size_t c = bitsof(decltype(x)). However what I'm asking here is for a way of emulating that behaviour in my own code without having to settle for a workaround. How can I write a bitsof that looks and feels like sizeof, but specialises like traits? Do I just have to accept that sizeof is a bit special and live with it?
I initially played with a few ideas:
Perhaps decltype works like sizeof, e.g. decltype(0) and decltype(int) are synonymous. No luck there though.
Maybe we could do something with pointer/reference template parameters. I couldn't see a way of getting deduction to work properly for that case though, and it would impose additional constraints on what variables we could use bitsof with.
Maybe some crazy SFINAE with a combination of templates and macros, but I can't see a way of making that happen, it's always just a syntax error.
Possibly something to workaround the limitations of one of the above using GCC's statement-expr extension.
As there's an easy workaround with decltype and more of a learning experiment I'm open to ideas using anything available in any C++ released compiler targeting any past, present or future standard.
You can do something like this:
#include <type_traits>
#define bitsof(k) decltype(bitsof_left+(k)+bitsof_right)
template <class K>
struct bits_traits { /* whatever you want here */ };
struct bitsof_left_t {
template <class T>
bits_traits<T> operator+(const T&);
} bitsof_left;
struct bitsof_right_t {
template <class T>
friend T operator+(const T&, bitsof_right_t);
bitsof_right_t operator+();
template <class T>
operator T() const;
} bitsof_right;
int main()
{
using foo = bitsof(42);
using bar = bitsof(int);
static_assert(std::is_same<foo, bits_traits<int>>::value);
static_assert(std::is_same<bar, bits_traits<int>>::value);
}
It works like this.
a + (42) + b is parsed as (a + (42)) + b), then overloaded binary operator+ at either side kicks in. In my example the operators are only declared, not defined, but since it's unevaluated context, it doesn't matter.
a + (int) + b is parsed as a + ((int) (+ b)). Here we employ the overloaded unary + at the right side, then overloaded cast operator, then overloaded binary + at the left side.
Its hard and probably impossible, mainly because you can only pass compile-time constants as template values to templates, hence your last example with the int_12_bit x; will never be able to be a template value (and types can't be passed as parameters, of course). I played around a bit with decltype, declval and different templates, but I simply could not get it to take in types and (non-constand expression) values with a single "call". It's really unfortunate decltype doesn't accept types, I wonder why the committee choose to only accept expressions.
Since you mentioned gcc-extensions, there is an extension which can make it work, __typeof__.
I personally have never used this extension, but it seems like it works similar to decltype but it also accepts types directly.
This snipped compiles under gcc x86-64 8.3 for me:
template<typename T>
struct bits_trait;
template<>
struct bits_trait<int>{};
void f() {
int x;
bits_trait<__typeof__(x)>();
bits_trait<__typeof__(int)>();
}
But this will only compile under gcc.
Edit: Clang seems to support it as well, no luck with MSVC though.
Not considering macros and without decltype, it is simply not possible because of the language syntax.
However you can get pretty damn close:
template <class T>
constexpr auto bitsof(T) { return sizeof(T) * CHAR_BIT; }
template <>
constexpr auto bitsof(int_12_bit) { return 12; }
template <class T>
constexpr auto bitsof() { return sizeof(T) * CHAR_BIT; }
template <>
constexpr auto bitsof<int_12_bit>() { return 12; }
auto test()
{
constexpr int a{};
constexpr int_12_bit x{};
static_assert(bitsof(a) == 32);
static_assert(bitsof(x) == 12);
static_assert(bitsof<int>() == 32);
static_assert(bitsof<int_12_bit>() == 12);
}
Aside from the slightly different syntax (but c'mon it's so close it shouldn't really matter) the biggest difference to the sizeof is that the arguments are not in an unevaluated context. So bitsof(foo()) will call foo(). And bitsof(a) is UB if a is uninitialized.
Building upon the quite magical answer from n.m., with just tiny bit of massage, it seems it is possible to have bitsof mimic sizeof.
#include <climits>
#include <iostream>
#include <type_traits>
template <typename T>
struct bits_traits {
enum { value = sizeof(T) * CHAR_BIT };
};
struct int_12_bit {
enum { bits = 12 };
};
template <>
struct bits_traits<int_12_bit> {
enum { value = int_12_bit::bits };
};
#define bits_traits_of(k) decltype(bits_traits_of_left+(k)+bits_traits_of_right)
struct bits_traits_of_left_t {
template <class T>
bits_traits<T> operator+(const T&);
} bits_traits_of_left;
struct bits_traits_of_right_t {
template <class T>
friend T operator+(const T&, bits_traits_of_right_t);
bits_traits_of_right_t operator+();
template <class T>
operator T() const;
} bits_traits_of_right;
#define bitsof(x) bits_traits_of(x)::value
int main() {
using std::size_t;
size_t a = bitsof(int);
size_t b = bitsof(int_12_bit);
std::cout <<"a="<< a <<", b="<< b << std::endl;
int_12_bit x;
size_t c = bitsof(x);
std::cout <<"c="<< c << std::endl;
}
The only thing that I changed, other than adding in definitions for bits_traits, is to redefine bitsof so that it returns the bits_traits::value rather than the bits_traits type.
$ ./a.out
a=32, b=12
c=12
I'm just writing this up to verify that it can work. All credits should go to n.m.'s answer.

constexpr operator overloading issues with using arguments

I am making a simple class inheriting from std::array. The point is that it should throw a compile time error if the subscript operator is used for an out of bounds index. However, I keep getting an error message. This is the code simplified.
#include <array>
using namespace std;
template<typename type, size_t size>
struct container : array<type,size>
{
constexpr inline type& operator[](int index) const
{
static_assert(index<size,"");
return ((static_cast<const array<type,size> >(*this))[index]);
}
template<class... bracelist>
constexpr container(bracelist&&... B)
:array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[2];
}
The error it gives me is:
main.cpp|80|error: non-constant condition for static assertion
main.cpp|80|error: 'index' is not a constant expression
However, I used "index" in the return statement, and commenting out the static_assert makes it work fine. If index was not a constant expression, wouldn't I not be able to use it in the subscript operator for std::array after the static_cast? I am new to using the constexpr functionality, so any help would be appreciated. Thank you.
Note: I am aware std::array's constexpr subscript operator already does this, I just want to know how to do this for future uses. Thanks.
There are 2 really useful features of constexpr functions, the interplay of which is not always fully appreciated.
In constexpr context they only evaluate code paths that are taken for the constexpr arguments.
In non-constexpr context they behave exactly like regular functions.
Which means that we can use exceptions to great effect.
Since while in constexpr context, if the exception path is taken, this is a compiler error (throw is not allowed in constexpr context). You get to see the "exception" in your compiler's error output.
example:
#include <array>
#include <stdexcept>
template<typename type, std::size_t size>
struct container : std::array<type,size>
{
constexpr auto operator[](std::size_t index) const
-> type const&
{
if (index < size)
return static_cast<const std::array<type,size>>(*this)[index];
else
throw std::out_of_range("index out of range" + std::to_string(index));
}
template<class... bracelist>
constexpr container(bracelist&&... B)
: std::array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[4];
}
Example output:
main.cpp: In function 'int main()':
main.cpp:28:37: in 'constexpr' expansion of 'myarray.container<int, 4>::operator[](4)'
main.cpp:13:81: error: expression '<throw-expression>' is not a constant expression
throw std::out_of_range("index out of range" + std::to_string(index));
This approach is actually more versatile than static_assert, since it works at both compile and runtime.
The thing you have to keep in mind is that constexpr functions can be called at runtime with non constexpr arguments. constexpr means for a function that the function is usable in a compile-time evaluated expression (e.g. another constexpr or a template argument) but not exclusively. A constexpr function can still be called in the classical way, i.e. at run-time with run-time variables. Which means that the parameters of a constexpr function cannot be and are not compile-time constants.
It doesn't apply to your case but in general if you know a parameter will always be called with a compile time constant than you can make it a template parameter.
constexpr void foo(int a)
{
static_assert(a != 0); // illegal code because the parameter
// cannot be a compile time constant
}
void test()
{
int x;
std::cin >> x;
foo(x); // this is perfectly legal
}
template <int N>
void foo()
{
static_assert(N != 0); // legal
}
void test()
{
int x;
std::cin >> x;
foo<x>(); // illegal, x is not a compile time constant
foo<24>(); // legal
constexpr int c = 11;
foo<c>();; // legal
}
This is the reason for std::get<N>(array) — it is the only way to assuredly pass a "compile-time value" in a manner that'll satisfy the rules of the language. Your attempt to create a compile-time op[] cannot work. You could of course make your own templated accessor like std::get, but one might ask why not just use std::array as it is already.

Creating a trait to detect closure types in C++

The standard defines closure types as follow:
[expr.prim.lambda.closure]
The type of a lambda-expression (which is also the type of the closure
object) is a unique, unnamed non-union class type, called the closure
type, whose properties are described below. [...]
Would there be a way to create a type trait to detect closure types:
template <class T>
struct is_closure {
static constexpr bool value = /* something */
};
template <class T>
inline constexpr bool is_closure_v = is_closure<T>::value;
If not doable in purely standard C++, intrinsics working on clang++-5.X and g++-7.X are welcome.
EDIT: Wouldn't it be doable to get the name of the lambda type using some compiler macros and then do some constexpr string processing?
I would love to see if there was actually a motivating use-case for needing to know, specifically, whether a type was a closure or not. I'm not sure if there is even a use-case for knowing if, in general a type can be invoked with some arguments - as opposed to specifically being invocable with a particular set of them. This kind of strikes me as intellectual masturbation.
That said, I love intellectual masturbation! The nice thing about closure types is that they're not nameable. But we still have hooks like __PRETTY_FUNCTION__ that give us the string form of names, so they have to be printable somehow -
and that somehow must be differentiable from normal names. Let's find out how:
#include <iostream>
template <typename T>
void print(T) {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
namespace N {
void foo() {
print([]{ return 1; });
}
}
int main() {
print([]{ return 1; });
print([](auto, auto&&...){ return 2; });
N::foo();
}
gcc prints:
void print(T) [with T = main()::<lambda()>]
void print(T) [with T = main()::<lambda(auto:1, auto:2&&, ...)>]
void print(T) [with T = N::foo()::<lambda()>]
clang prints:
void print(T) [T = (lambda at bar.cxx:15:11)]
void print(T) [T = (lambda at bar.cxx:16:11)]
void print(T) [T = (lambda at bar.cxx:10:11)]
In neither case are these strings producible from non-closure types. We cannot have types that start with < (gcc), and we cannot have types with spaces like lambda at (clang). So we can take advantage of that by building a type trait that just looks for the appropriate substring:
constexpr bool is_equal(const char* a, const char* b) {
for (; *a && *b; ++a, ++b) {
if (*a != *b) return false;
}
return *b == '\0';
}
constexpr bool has_substr(const char* haystack, const char* needle) {
for(; *haystack; ++haystack) {
if (is_equal(haystack, needle)) {
return true;
}
}
return false;
}
template <typename T>
constexpr bool closure_check() {
return has_substr(__PRETTY_FUNCTION__,
#ifdef __clang__
"(lambda at"
#else
"::<lambda"
#endif
);
}
template <typename T>
constexpr bool is_closure_v = closure_check<T>();
template <typename T>
struct is_closure
: std::integral_constant<bool, is_closure_v<T>>
{ };
I haven't tested this thoroughly, so I cannot say with confidence that it has neither false positives nor negatives, but it seems like the right approach here.
Edit: As Jarod42 points out, this is currently insufficient, as it fails to take into account, at the very least, closure types as template parameters: demo. There may be other false positives along this line.
Though, to reiterate, I am not clear on why such a thing might be useful.