Why is the type of boost::hana::tuple_c implementation-defined? - c++

The Boost.Hana documentation for tuple_c states:
Also note that the type of the objects returned by tuple_c and an
equivalent call to make<tuple_tag> may differ.
followed by the following snippet:
BOOST_HANA_CONSTANT_CHECK(
hana::to_tuple(hana::tuple_c<int, 0, 1, 2>)
==
hana::make_tuple(hana::int_c<0>, hana::int_c<1>, hana::int_c<2>)
);
However, the actual implementation for tuple_c simply has:
#ifdef BOOST_HANA_DOXYGEN_INVOKED
template <typename T, T ...v>
constexpr implementation_defined tuple_c{};
#else
template <typename T, T ...v>
constexpr hana::tuple<hana::integral_constant<T, v>...> tuple_c{};
#endif
and, indeed, the code snippet works just fine without the to_tuple wrapper:
BOOST_HANA_CONSTANT_CHECK(
hana::tuple_c<int, 0, 1, 2>
==
hana::make_tuple(hana::int_c<0>, hana::int_c<1>, hana::int_c<2>)
);
Question: why is the actual type of tuple_c implementation defined? Isn't the to_tuple wrapper superfluous?

The phrase "implementation defined" does not describe an implementation. It explicitly states that an implementation choice is left undocumented on purpose, for one reason or another. Of course it is implemented somehow. The users should not rely on any particular implementation but use only documented APIs.
Leaving an implementation choice undocumented is a sensible default unless there's a specific reason to document it. This is true even if there's only one obvious choice today, because tomorrow things may change.

Actually, the documentation has this covered in a FAQ:
Why leave some container's representation implementation-defined?
First, it gives much more wiggle room for the implementation to
perform compile-time and runtime optimizations by using clever
representations for specific containers. For example, a tuple
containing homogeneous objects of type T could be implemented as an
array of type T instead, which is more efficient at compile-time.
Secondly, and most importantly, it turns out that knowing the type of
a heterogeneous container is not as useful as you would think. Indeed,
in the context of heterogeneous programming, the type of the object
returned by a computation is usually part of the computation too. In
other words, there is no way to know the type of the object returned
by an algorithm without actually performing the algorithm.

Not speaking with authority, but I would say that wrapping the tuple_c with to_tuple is in fact superfluous. The documentation states that the result is functionally equivalent to make_tuple except that the type is not guaranteed to be the same.
One possible optimization would be returning something like this:
template <auto ...i>
struct tuple_c_t { };
To be sure I made a pull request to see if we can get the superfluous conversion removed from the example.
https://github.com/boostorg/hana/pull/394
UPDATE: It was confirmed by the author of Boost.Hana that the conversion is unnecessary and the example was updated to reflect that.

Related

Why is std::aligned_storage to be deprecated in C++23 and what to use instead?

I just saw that C++23 plans to deprecate both std::aligned_storage and std::aligned_storage_t as well as std::aligned_union and std::aligned_union_t.
Placement new'd objects in aligned storage are not particularly constexpr friendly as far as I understand, but that doesn't appear to be a good reason to throw out the type completely. This leads me to assume that there is some other fundamental problem with using std::aligned_storage and friends that I am not aware of. What would that be?
And is there a proposed alternative to these types?
Here are three excerpts from P1413R3:
Background
aligned_* are harmful to codebases and should not be used. At a high level:
Using aligned_* invokes undefined behavior (The types cannot provide storage.)
The guarantees are incorrect (The standard only requires that the type be at least as large as requested but does not put an upper bound on the size.)
The API is wrong for a plethora of reasons (See "On the API".)
Because the API is wrong, almost all usage involves the same repeated pre-work (See "Existing usage".)
On the API
std::aligned_* suffer from many poor API design decisions. Some of these are shared, and some are specific to each. As for what is shared, there are three main problems [only one is included here for brevity]:
Using reinterpret_cast is required to access the value
There is no .data() or even .data on std::aligned_* instances.
Instead, the API requires you to take the address of the object, call reinterpret_cast<T*>(...) with it, and then
finally indirect the resulting pointer giving you a T&. Not only does this mean that it cannot be used in constexpr, but
at runtime it's much easier to accidentally invoke undefined behavior. reinterpret_cast being a requirement for use
of an API is unacceptable.
Suggested replacement
The easiest replacement for aligned_* is actually not a library feature. Instead, users should use a properly-aligned
array of std::byte, potentially with a call to std::max(std::initializer_list<T>) . These can be found in the
<cstddef> and <algorithm> headers, respectively (with examples at the end of this section).
Unfortunately, this replacement is not ideal. To access the value of aligned_*, users must call reinterpret_cast on
the address to read the bytes as T instances. Using a byte array as a replacement does not avoid this problem. That said, it's important to recognize that continuing to use reinterpret_cast where it already exists is not nearly as bad as newly introducing it where it was previously not present. ...
The above section from the accepted proposal to retire aligned_* is then followed with a number of examples, like these two replacement suggestions:
// To replace std::aligned_storage
template <typename T>
class MyContainer {
private:
//std::aligned_storage_t<sizeof(T), alignof(T)> t_buff;
alignas(T) std::byte t_buff[sizeof(T)];
};
// To replace std::aligned_union
template <typename... Ts>
class MyContainer {
private:
//std::aligned_union_t<0, Ts...> t_buff;
alignas(Ts...) std::byte t_buff[std::max({sizeof(Ts)...})];
};

What are the rules on using `void` to specialize a template?

Here's a stripped down illustration of what I just now wrote in the wild. I wasn't really expecting it to work, but it did.
#include <array>
template <typename T> using Foo = std::array<T,123>;
const int FOO_SIZE = std::tuple_size<Foo<void>>::value;
I wasn't sure using void to specialize Foo would compile, but it did, at least in this case.
I'm not confident that this solution is portable, or if it's just a fluke because the implementation of std::array happens to be compatible with the concept of an array-of-voids, which sounds like nonsense to me.
When can I, and when can I not, use void to specialize a template?
I can't find a really convincing specific set of standard wording without reproducing half the standard (:D) but I believe that this is well-defined.
The array constructor requires that T be MoveConstructible or MoveAssignable, and you're not going to be able to instantiate a std::array<void, N>.
But that's fine, because std::tuple_size doesn't need to do that, and it isn't specified to need to do that, and neither thing makes any other specific requirements that would render your code problematic.
However, this does not seem like useful code, and there is no general rule for when void can be used as a template argument. You have to look at the requirements for the specific thing you're using, in the specific context in which you're using it.
It seems on the face of it surprising that this compiles at all, since array of type void are explicitly excluded by the standard (11.3.4: 2 in the N4659 working draft linked) :
An array can be constructed from one of the fundamental types (except
void), from a pointer, from a pointer to member, from a class, from an
enumeration type, or from another array.
And std::array is typically implemented directly in terms of an array, so one would expect any attempt to use it to fail.
However C++ has generous rules regarding errors in template instantiation that are not directly required in the compilation of the actual usage. In this case, I believe the code is working - and portable - since the calculation of std::tuple_size<Foo<void>>::value does not actually instance Foo<void>, but I would view such as usage as perverse and something you should probably be avoiding since Foo<void> has no validity outside of such exceptions.

Deprecated std::is_literal_type in C++17

According to cppreference, the trait std::is_literal_type is deprecated in C++17. The question is why and what is the preferred replacement for the future to check whether a type is a literal type.
As stated in P0174:
The is_literal type trait offers negligible value to generic code, as what is really needed is the ability to know that a specific construction would produce constant initialization. The core term of a literal type having at least one constexpr constructor is too weak to be used meaningfully.
Basically, what it's saying is that there's no code you can guard with is_literal_type_v and have that be sufficient to ensure that your code actually is constexpr. This isn't good enough:
template<typename T>
std::enable_if_t<std::is_literal_type_v<T>, void> SomeFunc()
{
constexpr T t{};
}
There's no guarantee that this is legal. Even if you guard it with is_default_constructible<T> that doesn't mean that it's constexpr default constructible.
What you would need is an is_constexpr_constructible trait. Which does not as of yet exist.
However, the (already implemented) trait does no harm, and allows compile-time introspection for which core-language type-categories a given template parameter might satisfy. Until the Core Working Group retire the notion of a literal type, the corresponding library trait should be preserved.
The next step towards removal (after deprecation) would be to write a paper proposing to remove the term from the core language while deprecating/removing the type trait.
So the plan is to eventually get rid of the whole definition of "literal types", replacing it with something more fine-grained.

Modifying scoped enum by reference

I am increasingly finding scoped enums unwieldy to use. I am trying to write a set of function overloads including a template for scoped enums that sets/initializes a value by reference--something like this:
void set_value(int& val);
void set_value(double& val);
template <typename ENUM> set_value(ENUM& val);
However, I don't quite see how to write the templated version of set_value without introducing multiple temporary values:
template <typename ENUM>
set_value(ENUM& val)
{
std::underlying_type_t<ENUM> raw_val;
set_value(raw_val); // Calls the appropriate "primitive" overload
val = static_cast<ENUM>(raw_val);
}
I believe the static_cast introduces a second temporary value in addition to raw_val. I suppose it's possible that one or both of these could be optimized away by the compiler, and in any case it shouldn't really make much difference in terms of performance since the set_value call will also generate temporary values (assuming it's not inlined), but this still seems inelegant. What I would like to do would be something like this:
template <typename ENUM>
set_value(ENUM& val)
{
set_value(static_cast<std::underlying_type_t<ENUM>&>(val));
}
... but this isn't valid (nor is the corresponding code using pointers directly instead of references) because scoped enums aren't related to their underlying primitives via inheritance.
I could use reinterpret_cast, which, from some preliminary testing, appears to work (and I can't think of any reason why it wouldn't work), but that seems to be frowned upon in C++.
Is there a "standard" way to do this?
I could use reinterpret_cast, which, from some preliminary testing, appears to work (and I can't think of any reason why it wouldn't work), but that seems to be frowned upon in C++.
Indeed, that reinterpret_cast is undefined behavior by violation of the strict aliasing rule.
Eliminating a single mov instruction (or otherwise, more or less, copying a register's worth of data) is premature micro-optimization. The compiler is likely to be able to take care of it.
If performance is really important, then follow the optimization process: profile, disassemble, understand the compiler's interpretation, and work together with it within the defined rules.
At a glance, you (and the compiler) might have an easier time with functions like T get_value() instead of void set_value(T). The flow of data and initialization make more sense, although type deduction is lost. You can regain the deduction through tag types, if that's really important.

Specializing std::optional

Will it be possible to specialize std::optional for user-defined types? If not, is it too late to propose this to the standard?
My use case for this is an integer-like class that represents a value within a range. For instance, you could have an integer that lies somewhere in the range [0, 10]. Many of my applications are sensitive to even a single byte of overhead, so I would be unable to use a non-specialized std::optional due to the extra bool. However, a specialization for std::optional would be trivial for an integer that has a range smaller than its underlying type. We could simply store the value 11 in my example. This should provide no space or time overhead over a non-optional value.
Am I allowed to create this specialization in namespace std?
The general rule in 17.6.4.2.1 [namespace.std]/1 applies:
A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly
prohibited.
So I would say it's allowed.
N.B. optional will not be part of the C++14 standard, it will be included in a separate Technical Specification on library fundamentals, so there is time to change the rule if my interpretation is wrong.
If you are after a library that efficiently packs the value and the "no-value" flag into one memory location, I recommend looking at compact_optional. It does exactly this.
It does not specialize boost::optional or std::experimental::optional but it can wrap them inside, giving you a uniform interface, with optimizations where possible and a fallback to 'classical' optional where needed.
I've asked about the same thing, regarding specializing optional<bool> and optional<tribool> among other examples, to only use one byte. While the "legality" of doing such things was not under discussion, I do think that one should not, in theory, be allowed to specialize optional<T> in contrast to eg.: hash (which is explicitly allowed).
I don't have the logs with me but part of the rationale is that the interface treats access to the data as access to a pointer or reference, meaning that if you use a different data structure in the internals, some of the invariants of access might change; not to mention providing the interface with access to the data might require something like reinterpret_cast<(some_reference_type)>. Using a uint8_t to store a optional-bool, for example, would impose several extra requirements on the interface of optional<bool> that are different to the ones of optional<T>. What should the return type of operator* be, for example?
Basically, I'm guessing the idea is to avoid the whole vector<bool> fiasco again.
In your example, it might not be too bad, as the access type is still your_integer_type& (or pointer). But in that case, simply designing your integer type to allow for a "zombie" or "undetermined" value instead of relying on optional<> to do the job for you, with its extra overhead and requirements, might be the safest choice.
Make it easy to opt-in to space savings
I have decided that this is a useful thing to do, but a full specialization is a little more work than necessary (for instance, getting operator= correct).
I have posted on the Boost mailing list a way to simplify the task of specializing, especially when you only want to specialize some instantiations of a class template.
http://boost.2283326.n4.nabble.com/optional-Specializing-optional-to-save-space-td4680362.html
My current interface involves a special tag type used to 'unlock' access to particular functions. I have creatively named this type optional_tag. Only optional can construct an optional_tag. For a type to opt-in to a space-efficient representation, it needs the following member functions:
T(optional_tag) constructs an uninitialized value
initialize(optional_tag, Args && ...) constructs an object when there may be one in existence already
uninitialize(optional_tag) destroys the contained object
is_initialized(optional_tag) checks whether the object is currently in an initialized state
By always requiring the optional_tag parameter, we do not limit any function signatures. This is why, for instance, we cannot use operator bool() as the test, because the type may want that operator for other reasons.
An advantage of this over some other possible methods of implementing it is that you can make it work with any type that can naturally support such a state. It does not add any requirements such as having a move constructor.
You can see a full code implementation of the idea at
https://bitbucket.org/davidstone/bounded_integer/src/8c5e7567f0d8b3a04cc98142060a020b58b2a00f/bounded_integer/detail/optional/optional.hpp?at=default&fileviewer=file-view-default
and for a class using the specialization:
https://bitbucket.org/davidstone/bounded_integer/src/8c5e7567f0d8b3a04cc98142060a020b58b2a00f/bounded_integer/detail/class.hpp?at=default&fileviewer=file-view-default
(lines 220 through 242)
An alternative approach
This is in contrast to my previous implementation, which required users to specialize a class template. You can see the old version here:
https://bitbucket.org/davidstone/bounded_integer/src/2defec41add2079ba023c2c6d118ed8a274423c8/bounded_integer/detail/optional/optional.hpp
and
https://bitbucket.org/davidstone/bounded_integer/src/2defec41add2079ba023c2c6d118ed8a274423c8/bounded_integer/detail/optional/specialization.hpp
The problem with this approach is that it is simply more work for the user. Rather than adding four member functions, the user must go into a new namespace and specialize a template.
In practice, all specializations would have an in_place_t constructor that forwards all arguments to the underlying type. The optional_tag approach, on the other hand, can just use the underlying type's constructors directly.
In the specialize optional_storage approach, the user also has the responsibility of adding proper reference-qualified overloads of a value function. In the optional_tag approach, we already have the value so we do not have to pull it out.
optional_storage also required standardizing as part of the interface of optional two helper classes, only one of which the user is supposed to specialize (and sometimes delegate their specialization to the other).
The difference between this and compact_optional
compact_optional is a way of saying "Treat this special sentinel value as the type being not present, almost like a NaN". It requires the user to know that the type they are working with has some special sentinel. An easily specializable optional is a way of saying "My type does not need extra space to store the not present state, but that state is not a normal value." It does not require anyone to know about the optimization to take advantage of it; everyone who uses the type gets it for free.
The future
My goal is to get this first into boost::optional, and then part of the std::optional proposal. Until then, you can always use bounded::optional, although it has a few other (intentional) interface differences.
I don't see how allowing or not allowing some particular bit pattern to represent the unengaged state falls under anything the standard covers.
If you were trying to convince a library vendor to do this, it would require an implementation, exhaustive tests to show you haven't inadvertently blown any of the requirements of optional (or accidentally invoked undefined behavior) and extensive benchmarking to show this makes a notable difference in real world (and not just contrived) situations.
Of course, you can do whatever you want to your own code.