I'm trying to write a function that checks if the variable being cast can fit in the destination type, and assert() if not. Right now this is what I came up with. I didn't test it yet. I would like to make the template figure out the type of the varible being passed automatically, with something like typeid, although I don't really know what typeid really is. Is that possible? Also, I don't know much about templates.
template<typename from_T, typename to_T>
static inline to_T safe_cast(from_T variable)
{
assert(variable >= std::numeric_limits<to_T>::min());
assert(variable <= std::numeric_limits<to_T>::max());
return static_cast<to_T>(variable);
}
Well, if that is actually some function that already does this that I don't know of I will be glad to hear.
C++ Core Guidelines already has a gsl::narrow
// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
You can see the Microsoft implementation here
// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
template <class T, class U>
constexpr T narrow(U u) noexcept(false)
{
constexpr const bool is_different_signedness =
(std::is_signed<T>::value != std::is_signed<U>::value);
const T t = narrow_cast<T>(u);
if (static_cast<U>(t) != u || (is_different_signedness && ((t < T{}) != (u < U{}))))
{
throw narrowing_error{};
}
return t;
}
You can see the explanation of the implementation on this SO post (it's for an older version of the implementation, but nothing substantially changed, so the answer still applies).
TL;DR
Before you attempt to read this whole post, know that:
a solution to the presented issue has been found by myself, but I'm still eager to know if the analysis is correct;
I've packaged the solution into a fameta::counter class that solves a few remaining quirks. You can find it on github;
you can see it at work on godbolt.
How it all started
Since Filip Roséen discovered/invented, in 2015, the black magic that compile time counters via friend injection are in C++, I have been mildly obsessed with the device, so when the CWG decided that functionality had to go I was disappointed, but still hopeful that their mind could be changed by showing them a few compelling use cases.
Then, a couple years ago I decided to have a look at the thing again, so that uberswitches could be nested - an interesting use case, in my opinion - only to discover that it wouldn't work any longer with the new versions of the available compilers, even though issue 2118 was (and still is) in open state: the code would compile, but the counter would not increase.
The problem has been reported on Roséen's website and recently also on stackoverflow: Does C++ support compile-time counters?
A few days ago I decided to try and tackle the issues again
I wanted to understand what had changed in the compilers that made the, seemingly still valid C++, not work any longer. To that end, I've searched wide and far the interweb for somebody to have talked about it, but to no avail. So I've begun experimenting and came to some conclusions, that I'm presenting here hoping to get a feedback from the more-knowledged-than-myself around here.
Below I'm presenting Roséen's original code for sake of clarity. For an explanation of how it works, please refer to his website:
template<int N>
struct flag {
friend constexpr int adl_flag (flag<N>);
};
template<int N>
struct writer {
friend constexpr int adl_flag (flag<N>) {
return N;
}
static constexpr int value = N;
};
template<int N, int = adl_flag (flag<N> {})>
int constexpr reader (int, flag<N>) {
return N;
}
template<int N>
int constexpr reader (float, flag<N>, int R = reader (0, flag<N-1> {})) {
return R;
}
int constexpr reader (float, flag<0>) {
return 0;
}
template<int N = 1>
int constexpr next (int R = writer<reader (0, flag<32> {}) + N>::value) {
return R;
}
int main () {
constexpr int a = next ();
constexpr int b = next ();
constexpr int c = next ();
static_assert (a == 1 && b == a+1 && c == b+1, "try again");
}
With both g++ and clang++ recent-ish compilers, next() always returns 1. Having experimented a bit, the issue at least with g++ seems to be that once the compiler evaluates the functions templates default parameters the first time the functions are called, any subsequent call to those functions doesn't trigger a re-evaluation of the default parameters, thus never instantiating new functions but always referring to the previously instantiated ones.
First questions
Do you actually agree with this diagnosis of mine?
If yes, is this new behavior mandated by the standard? Was the previous one a bug?
If not, then what is the problem?
Keeping the above in mind, I came up with a work around: mark each next() invokation with a monotonically increasing unique id, to pass onto the callees, so that no call would be the same, therefore forcing the compiler to re-evaluate all the arguments each time.
It seems a burden to do that, but thinking of it one could just use the standard __LINE__ or __COUNTER__-like (wherever available) macros, hidden in a counter_next() function-like macro.
So I came up with the following, that I present in the most simplified form that shows the problem I will talk about later.
template <int N>
struct slot;
template <int N>
struct slot {
friend constexpr auto counter(slot<N>);
};
template <>
struct slot<0> {
friend constexpr auto counter(slot<0>) {
return 0;
}
};
template <int N, int I>
struct writer {
friend constexpr auto counter(slot<N>) {
return I;
}
static constexpr int value = I-1;
};
template <int N, typename = decltype(counter(slot<N>()))>
constexpr int reader(int, slot<N>, int R = counter(slot<N>())) {
return R;
};
template <int N>
constexpr int reader(float, slot<N>, int R = reader(0, slot<N-1>())) {
return R;
};
template <int N>
constexpr int next(int R = writer<N, reader(0, slot<N>())+1>::value) {
return R;
}
int a = next<11>();
int b = next<34>();
int c = next<57>();
int d = next<80>();
You can observe the results of the above on godbolt, which I've screenshotted for the lazies.
And as you can see, with trunk g++ and clang++ until 7.0.0 it works!, the counter increases from 0 to 3 as expected, but with clang++ version above 7.0.0 it doesn't.
To add insult to injury, I've actually managed to make clang++ up to version 7.0.0 crash, by simply adding a "context" parameter to the mix, such that the counter is actually bound to that context and, as such, can be restarted any time a new context is defined, which opens up for the possibility to use a potentially infinite amount of counters. With this variant, clang++ above version 7.0.0 doen't crash, but still doesn't produce the expected result. Live on godbolt.
At loss of any clue about what was going on, I've discovered the cppinsights.io website, that lets one see how and when templates get instantiated. Using that service what I think is happening is that clang++ does not actually define any of the friend constexpr auto counter(slot<N>) functions whenever writer<N, I> is instantiated.
Trying to explicitly call counter(slot<N>) for any given N that should already have been instantiated seems to give basis to this hypothesis.
However, if I try to explicitly instantiate writer<N, I> for any given N and I that should have already been instantiated, then clang++ complains about a redefined friend constexpr auto counter(slot<N>).
To test the above, I've added two more lines to the previous source code.
int test1 = counter(slot<11>());
int test2 = writer<11,0>::value;
You can see it all for yourself on godbolt. Screenshot below.
So, it appears that clang++ believes it has defined something that it believes it hasn't defined, which kind of makes your head spin, doesn't it?
Second batch of questions
Is my workaround legal C++ at all, or did I manage to just discover another g++ bug?
If it's legal, did I therefore discover some nasty clang++ bugs?
Or did I just delve into the dark underworld of Undefined Behavior, so I myself am the only one to blame?
In any event, I would warmly welcome anybody who wanted to help me get out of this rabbit hole, dispensing headaching explanations if need be. :D
After further investigation, it turns out there exists a minor modification that can be performed to the next() function, which makes the code work properly on clang++ versions above 7.0.0, but makes it stop working for all other clang++ versions.
Have a look at the following code, taken from my previous solution.
template <int N>
constexpr int next(int R = writer<N, reader(0, slot<N>())+1>::value) {
return R;
}
If you pay attention to it, what it literally does is to try to read the value associated with slot<N>, add 1 to it and then associate this new value to the very same slot<N>.
When slot<N> has no associated value, the value associated with slot<Y> is retrieved instead, with Y being the highest index less than N such that slot<Y> has an associated value.
The problem with the above code is that, even though it works on g++, clang++ (rightfully, I would say?) makes reader(0, slot<N>()) permanently return whatever it returned when slot<N> had no associated value. In turn, this means that all slots get effectively associated with the base value 0.
The solution is to transform the above code into this one:
template <int N>
constexpr int next(int R = writer<N, reader(0, slot<N-1>())+1>::value) {
return R;
}
Notice that slot<N>() has been modified into slot<N-1>(). It makes sense: if I want to associate a value to slot<N>, it means no value is associated yet, therefore it makes no sense to attempt to retrieve it. Also, we want to increase a counter, and value of the counter associated with slot<N> has to be one plus the value associated with slot<N-1>.
Eureka!
This breaks clang++ versions <= 7.0.0, though.
Conclusions
It seems to me that the original solution I posted has a conceptual bug, such that:
g++ has quirk/bug/relaxation that cancels out with my solution's bug and eventually makes the code work nonetheless.
clang++ versions > 7.0.0 are stricter and don't like the bug in the original code.
clang++ versions <= 7.0.0 have a bug that makes the corrected solution not work.
Summing all that up, the following code works on all versions of g++ and clang++.
#if !defined(__clang_major__) || __clang_major__ > 7
template <int N>
constexpr int next(int R = writer<N, reader(0, slot<N-1>())+1>::value) {
return R;
}
#else
template <int N>
constexpr int next(int R = writer<N, reader(0, slot<N>())+1>::value) {
return R;
}
#endif
The code as-is also works with msvc.
The icc compiler doesn't trigger SFINAE when using decltype(counter(slot<N>())), preferring to complain about not being able to deduce the return type of function "counter(slot<N>)" because it has not been defined. I believe this is a bug, that can be worked around by doing SFINAE on the direct result of counter(slot<N>). This works on all other compilers too, but g++ decides to spit out a copious amount of very annoying warnings that cannot be turned off. So, also in this case, #ifdef could come to the rescue.
The proof is on godbolt, screnshotted below.
I have some very heavily templated code, where I need to pass configuration flags encoded as unsigned via a template.
template<unsigned T>
struct MemoryTraits
{
// Do something with T...
};
There are some predefined flags, that I'm supposed to bit-or, and pass to the struct above, then it will be passed further to other templated functions and classes.
SomeTemplatedContainer</*other parameters */, MemoryTraits<Trait1 | Trait2>> myContainer{};
I was trying to create a variadic template function, that would take any number of flags, and alias the MemoryTraits, depending on template parameters.
template<unsigned ...Traits>
void foo() {
using MyMemoryTraits = MemoryTraits<(Traits | ...)>;
SomeTemplatedContainer</*other parameters */, MyMemoryTraits> myContainer{}
}
I tried to implement it using c++17 fold expressions, and everything works when I invoke foo with one or more arguments, but when calling it without any, I get the following compile time error.
error: expected expression
foo(); -> fold expression has empty expansion
From what I understand, it's because an empty fold expression with operator | doesn't have a default value, is there any way I can provide a default value to it? So far I came up with the following helper lambda.
constexpr auto TraitsHelper = [](){
if constexpr (sizeof...(Traits) == 0){
return 0;
} else if constexpr(sizeof...(Traits) == 1){
constexpr std::array TRAITS{Traits...};
return TRAITS[0];
} else {
return (Traits | ...);
}
};
It works, at least for now, but is there some better way to fix this issue?
std::experimental::source_location will probably be added to the C++ standard at some point. I'm wondering if it possible to get the location information into the compile-time realm. Essentially, I want a function that returns different types when called from different source locations. Something like this, although it doesn't compile because the location object isn't constexpr as it's a function argument:
#include <experimental/source_location>
using namespace std::experimental;
constexpr auto line (const source_location& location = source_location::current())
{
return std::integral_constant<int, location.line()>{};
}
int main()
{
constexpr auto ll = line();
std::cout << ll.value << '\n';
}
This doesn't compile, with a message about
expansion of [...] is not a constant expression
regarding the return std::integral_constant<int, location.line()>{} line. What good it is to have the methods of source_location be constexpr if I can't use them?
As Justin pointed the issue with your code is that function argument are not constexpr but the problem of using source_location in a constexpr function in a more useful way is mentioned in the constexpr! functions proposal which says:
The "Library Fundamentals v. 2" TS contains a "magic" source_location
class get to information similar to the FILE and LINE macros
and the func variable (see N4529 for the current draft, and N4129
for some design notes). Unfortunately, because the "value" of a
source_location is frozen at the point source_location::current() is
invoked, composing code making use of this magic class is tricky:
Typically, a function wanting to track its point of invocation has to
add a defaulted parameter as follows:
void my_log_function(char const *msg,
source_location src_loc
= source_location::current()) {
// ...
}
This idiom ensure that the value of the source_location::current()
invocation is sampled where my_log_function is called instead of where
it is defined.
Immediate (i.e., constexpr!) functions, however, create a clean
separation between the compilation process and the constexpr
evaluation process (see also P0992). Thus, we can make
source_location::current() an immediate function, and wrap it as
needed in other immediate functions: The value produced will
correspond to the source location of the "root" immediate function
call. For example:
constexpr! src_line() {
return source_location::current().line();
}
void some_code() {
std::cout << src_line() << '\n'; // This line number is output.
}
So this is currently an open problem.
Given a type T, I'd like to know whether that type satisfies Boost.Hana's Struct concept. In other words, I would like to construct a metafunction is_struct_v such that for
struct foo_t { int x; }
BOOST_HANA_ADAPT_STRUCT(foo_t, x);
the following code compiles:
static_assert(is_struct_v<foo_t>);
whereas is_struct_v<std::vector<int>> should return false.
My guess was that hana's is_valid "function" is appropriate for this, however, I'm unable to bend the compiler to my will. Here is my attempt:
#include <boost/hana.hpp>
namespace hana = boost::hana;
template <typename TypeTag>
constexpr auto is_struct(TypeTag t) {
auto has_members = hana::is_valid([](auto v)
-> decltype((void)(boost::hana::members(hana::traits::declval(v))))
{});
return has_members(t);
}
template <typename T>
constexpr bool is_struct_v = is_struct(boost::hana::type_c<T>);
This does return true for the foo_t case, but when I attempt to apply is_struct_v to std::vector<int>, I get the following error in clang (trunk):
static_assert failed due to requirement 'hana::Struct<S>::value'
UPDATE: I've solved my own problem; see answer below. That said, I would still appreciate an explanation of why the version which uses hana::members doesn't work. I find it odd that the static_assert triggers a compile time error because I thought is_valid is supposed to SFINAE that away. What am I missing?
Well, that was silly. It was only after posting that I realized I could use the error message to solve my problem! So hana::Struct<foo_t>::value is true, while hana::Struct<std::vector<int>> is false.