boost::any_range<gsl::string_span<>> crash in Release mode - c++

I'm observing a rather weird behaviour of the following piece of code:
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/any_range.hpp>
#include <vector>
#include <string>
#include <iostream>
#include "gsl.h"
template <typename T>
using ImmutableValueRange = boost::any_range<T, boost::bidirectional_traversal_tag, /*const*/ T>;
template <typename T, typename C>
ImmutableValueRange<T> make_transforming_immutable_range(const C& container)
{
return container | boost::adaptors::transformed([](const typename C::value_type& v) -> T
{
//std::cout << "trans : " << T{ v }.data() << "\n";
return T{ v };
});
}
void f(ImmutableValueRange<gsl::cstring_span<>> r)
{
for (const auto& c : r) {
std::cout << c.data() << "\n";
}
}
int main()
{
std::vector<std::string> v({ "x", "y", "z" });
f(make_transforming_immutable_range<gsl::cstring_span<>>(v));
}
The idea here is to isolate the actual representation of a sequence of strings that is received as a parameter by the function f behind an any_range and gsl::string_span (note, the commit changing string_view to string_span has been made a couple of hours ago to GSL).
My original code did not have a const T as Reference template parameter to any_range (it was a simple T) and it crashed during execution. However, that happened only in Release mode an worked fine in Debug or RelWithDebInfo (generated by CMake). I used VS2013/2015 x64. Furthermore, trying to debug the full Release version, adding debug output to the conversion lambda eliminated the crash (my guess is it prevented some inlining). My final working solution is to specify const T as Reference.
However, I'm still wondering why did the crash happen in the first place? Is it the VS compiler bug? Bug in the current implementation of string_span? Or am I simply misusing the boost::any_range?
Edit
Just built the version with clang 3.7.0 and the behaviour is similar (works fine in debug and doesn't crash, but outputs garbage without const T with -O2). So it doesn't seem like a compiler problem.

As it turns out, the any_range's dereference method will return a reference to T unless the Reference type is specified as const T, thus creating a dangling reference to a temporary. This happens due to use of any_incrementable_iterator_interface::mutable_reference_type_generator defined in any_iterator_interface.hpp.
Therefore, the correct solution to the problem is indeed to specify const T as the Reference type in case the iterator dereferencing returns a temporary.

This is a bug in boost::range and a fix was only merged in Feb of 2020, but didn't make it into 1.73. The fix is available as of 1.74
https://github.com/boostorg/range/pull/94

After a quick look, I suspect the problem lies in your lambda. If I understood correctly, you end up taking a std::string by const reference with the following parameter declaration:
const typename C::value_type& v
However, you are then using v to construct a cstring_span. Here's the rub: cstring_span only has a constructor that goes from a non-const reference to a container type (like std::string). Conceptually, the constructor looks like this:
template <class Cont>
cstring_span(Cont& c)
So I am guessing that when you return from your lambda, a temporary is being created from v, and then passed to the cstring_span constructor in order to provide a non-const reference argument. Of course, once that temporary gets cleaned up, your cstring_span is left dangling.

Related

What to expect when experimenting with C++23 features in MSVC on Compiler Explorer

I was watching a C++Con video on YouTube found here.
I became interested in these new concepts. I tried to implement the code snippets from slides 27 and 29 from the time stamps #23:00 - #26:30. There is a subtle difference in my code where I added the operator()() to my_function class in order to use it within the auto range loop within main().
Also, I had to modify the less_than within the sort_by() function call by using its operator()() in order for Compiler Explorer to compile the code.
Here is my version of the code that does compile:
#include <iostream>
#include <vector>
#include <algorithm>
struct less_than {
template<typename T, typename U>
bool operator()(this less_than, const T& lhs, const U& rhs) {
return lhs < rhs;
}
};
struct my_vector : std::vector<int> {
using std::vector<int>::vector;
auto sorted_by(this my_vector self, auto comp) -> my_vector {
std::sort(self.begin(), self.end(), comp);
return self;
}
my_vector& operator()() {
return *this;
}
};
int main() {
my_vector{3,1,4,1,5,9,2,6,5}.sorted_by(less_than());
for (auto v : my_vector()) {
std::cout << v << " ";
}
return 0;
}
Here is my link to Compiler Explorer to see the actual compiled code, assembly as well as the executed output.
The code does compile and executes. The vector within main() does contain the values from its initializer list constructor that can be seen within the assembly. However it appears that nothing is being printed to the standard output, or it is being constructed, sorted and then destroyed from the same line of c++ execution and its going out of scope before referencing it within the auto range loop.
The topic at this point in the video is about automatically deducing the this pointer to simplify build patterns to reduce the complexity of CRTP and the concept here is to introduce By-Value this: Move Chains.
Yes this is experimental and may change before the entire C++23 language standard is implemented and released.
I'm just looking for both insight and clarity to make sure that I'm understanding what's happening within my code according to Compiler Explorer, the topic of this talk, and what the future may bring for newer language features.
Is my assumption of why I'm not getting an output correct? Does it pertain to object lifetime, visibility and or going out of scope?
I can't say I know a lot about C++23, but I don't think your problem is to do with that per-se. This:
for (auto v : my_vector()) ...
default-constructs an (empty, temporary) vector and then runs a range for loop on it, and MSVC is evidently smart enough to see that this is effectively a no-op and throws the whole thing away.
But if you do this:
for (auto v : my_vector{3,1,4,1,5,9,2,6,5}.sorted_by(less_than())) ...
then what looks to me to be reasonable code is generated. Pity we can't run it. Widen the panes on the right-hand side a little bit to see the program output!
Godbolt link

Inserting non-const pair into `std::unordered_map` is slower than const pair

I have some code like these (from cppcon), when inserting a non-const pair into a unordered_map, the performance is very different to inserting with a const one.
#include <algorithm>
#include <chrono>
#include <iostream>
#include <iterator>
#include <unordered_map>
#include <vector>
using namespace std;
struct StopWatch {
StopWatch() : clk{std::chrono::system_clock::now()} {}
~StopWatch() {
auto now = std::chrono::system_clock::now();
auto diff = now - clk;
cout << chrono::duration_cast<chrono::microseconds>(diff).count() << "ms"
<< endl;
}
decltype(std::chrono::system_clock::now()) clk;
};
void Benchmark_Slow(int iters) {
std::unordered_map<string, int> m;
std::pair<const string, int> p = {};
while (iters--)
m.insert(p);
}
void Benchmark_Fast(int iters) {
std::unordered_map<string, int> m;
const std::pair<const string, int> p = {};
while (iters--)
m.insert(p);
}
int main(void) {
{
StopWatch sw;
Benchmark_Fast(1000000);
}
{
StopWatch sw;
Benchmark_Slow(1000000);
}
return 0;
}
A online demo: Compiler Explorer
128247ms
392454ms
It seems that the const qualifier let the compiler to choose the unordered_map::insert(const value_type&) overload instead of the unordered_map::insert( P&& value ).
cppreference: unordered_map::insert
But I think that a forwarding templated universal reference insert(P&& value) would be the same as an insert with const lvalue reference, an identical copy operation.
But the emplace one(with non-const pair) runs much slower than insert one(with const pair).
Am I missing something here ? Or if this is something has a keyword to be searched on the google, I didn't find something answers that. Thank you in advance.
I think I do found a possible explanation.
from emplace it describe that if insertion fails, the constructed element would be destroyed immediately.
I follow the assembly code compiled with libstd++ of unordered_map::emplace (which accept templated argument and do std::forward) and unordered_map::insert link provided by #Jarod42, I the emplace one always allocate a new hash_node before it check if the key already in the map, because it's templated and it didn't know the argument type (maybe it only know it's is_convertible_to), so it do the construction before examine the key. The one in libc++ seems recognize the type is a const reference thus do the same as the insert one, copy construct occurs only if the key is not exsist.
When I modified the code with different key to be inserted, the difference gone away. quick C++ benchmark
I don't know did I miss something else. I' m sorry for this trivial problem was posted.

C++: Can't propagate polymorphic_allocator with scoped_allocator_adaptor

I have a vector<vector<int>> and want the entire memory (i.e., of both the outer and the inner vector) to be taken from a memory_resource. Here is a stripped down example, first the boring part:
#include <boost/container/pmr/memory_resource.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/container/pmr/polymorphic_allocator.hpp>
#include <iostream>
#include <string>
#include <vector>
// Sample memory resource that prints debug information
class MemoryResource : public boost::container::pmr::memory_resource {
void* do_allocate(std::size_t bytes, std::size_t alignment) {
std::cout << "Allocate " << bytes << " bytes" << std::endl;
return malloc(bytes);
}
void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) { free(p); }
bool do_is_equal(const memory_resource& other) const noexcept { return true; }
};
This is the part that I am interested in:
template <typename T>
using Alloc = boost::container::pmr::polymorphic_allocator<T>;
// using Alloc = std::allocator<T>;
template <typename T>
using PmrVector = std::vector<T, boost::container::scoped_allocator_adaptor<Alloc<T>>>;
using Inner = PmrVector<int>;
int main() {
MemoryResource resource{};
PmrVector<Inner> v(1000, Alloc<Inner>{&resource});
// PmrVector<Inner> v(1337, Alloc<Inner>{});
v[0].resize(100);
}
This gives me a lengthy compiler warning, essentially saying that it can't find a constructor for the inner vector.
If, instead of the polymorphic allocator, I use a regular allocator (e.g., std::allocator - see the lines that are commented out), everything seems to work.
The gcc error message is a bit better than that of clang:
/usr/local/include/boost/container/allocator_traits.hpp:415:10:
error: no matching function for call to '
std::vector<int, polymorphic_allocator<int> >::vector(
scoped_allocator_adaptor<...>&, polymorphic_allocator<...>&
)
'
Why would boost try to construct a vector by passing the allocator twice?
Also, here is a version that uses STL (experimental) instead of boost. That one gives an actual error message "construction with an allocator must be possible if uses_allocator is true", but that doesn't help me either.
Maybe I am understanding something conceptually wrong. Is this the way to do it or is there a better way to solve the original problem?
Argh. The explanation is hidden in std::experimental::pmr::polymorphic_allocator::construct:
This function is called (through std::allocator_traits) by any
allocator-aware object, such as std::vector, that was given a
std::polymorphic_allocator as the allocator to use. Since
memory_resource* implicitly converts to polymorphic_allocator, the
memory resource pointer will propagate to any allocator-aware
subobjects using polymorphic allocators.
So it turns out that polymorphic allocators automatically propagate. That also explains why the allocator is passed twice in the gcc error message.
Here is a working version:
template <typename T>
using Alloc = std::experimental::pmr::polymorphic_allocator<T>;
template <typename T>
using PmrVector = std::vector<T, Alloc<T>>;
using Inner = PmrVector<int>;
int main() {
MemoryResource resource{};
PmrVector<Inner> v(1000, Alloc<Inner>{&resource});
v[0].resize(100);
}
And here is the information that I would have need a couple of hours ago:
How do I use polymorphic_allocator and scoped_allocator_adaptor together?
You don't. Make sure that all inner containers also use polymorphic allocators, then the memory resource will be handed down automatically.

visual studio implementation of "move semantics" and "rvalue reference"

I came across a Youtube video on c++11 concurrency (part 3) and the following code, which compiles and generates correct result in the video.
However, I got a compile error of this code using Visual Studio 2012. The compiler complains about the argument type of toSin(list<double>&&). If I change the argument type to list<double>&, the code compiled.
My question is what is returned from move(list) in the _tmain(), is it a rvalue reference or just a reference?
#include "stdafx.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <list>
#include <algorithm>
using namespace std;
void toSin(list<double>&& list)
{
//this_thread::sleep_for(chrono::seconds(1));
for_each(list.begin(), list.end(), [](double & x)
{
x = sin(x);
});
for_each(list.begin(), list.end(), [](double & x)
{
int count = static_cast<int>(10*x+10.5);
for (int i=0; i<count; ++i)
{
cout.put('*');
}
cout << endl;
});
}
int _tmain(int argc, _TCHAR* argv[])
{
list<double> list;
const double pi = 3.1415926;
const double epsilon = 0.00000001;
for (double x = 0.0; x<2*pi+epsilon; x+=pi/16)
{
list.push_back(x);
}
thread th(&toSin, /*std::ref(list)*/std::move(list));
th.join();
return 0;
}
This appears to be a bug in MSVC2012. (and on quick inspection, MSVC2013 and MSVC2015)
thread does not use perfect forwarding directly, as storing a reference to data (temporary or not) in the originating thread and using it in the spawned thread would be extremely error prone and dangerous.
Instead, it copies each argument into decay_t<?>'s internal data.
The bug is that when it calls the worker function, it simply passes that internal copy to your procedure. Instead, it should move that internal data into the call.
This does not seem to be fixed in compiler version 19, which I think is MSVC2015 (did not double check), based off compiling your code over here
This is both due to the wording of the standard (it is supposed to invoke a decay_t<F> with decay_t<Ts>... -- which means rvalue binding, not lvalue binding), and because the local data stored in the thread will never be used again after the invocation of your procedure (so logically it should be treated as expiring data, not persistent data).
Here is a work around:
template<class F>
struct thread_rvalue_fix_wrapper {
F f;
template<class...Args>
auto operator()(Args&...args)
-> typename std::result_of<F(Args...)>::type
{
return std::move(f)( std::move(args)... );
}
};
template<class F>
thread_rvalue_fix_wrapper< typename std::decay<F>::type >
thread_rvalue_fix( F&& f ) { return {std::forward<F>(f)}; }
then
thread th(thread_rvalue_fix(&toSin), /*std::ref(list)*/std::move(list));
should work. (tested in MSVC2015 online compiler linked above) Based off personal experience, it should also work in MSVC2013. I don't know about MSVC2012.
What is returned from std::move is indeed an rvalue reference, but that doesn't matter because the thread constructor does not use perfect forwarding for its arguments. First it copies/moves them to storage owned by the new thread. Then, inside the new thread, the supplied function is called using the copies.
Since the copies are not temporary objects, this step won't bind to rvalue-reference parameters.
What the Standard says (30.3.1.2):
The new thread of execution executes
INVOKE( DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))... )
with the calls to
DECAY_COPY being evaluated in the constructing thread.
and
In several places in this Clause the operation DECAY_COPY(x) is used. All such uses mean call the function decay_copy(x) and use the result, where decay_copy is defined as follows:
template <class T> decay_t<T> decay_copy(T&& v)
{ return std::forward<T>(v); }
The value category is lost.

boost::transformed with tuple functor gives strange warning

I've written a convenient functor wrapper for tuple std::get. When using it with boost transformed and operator[], I get warning that I'm returning reference to local temporary object. My system: ubuntu 14.04, compilers: clang-3.5 and g++-4.8.2, boost version: 1.56.
#include <boost/range/adaptor/transformed.hpp>
#include <utility>
#include <vector>
template <std::size_t I>
struct tuple_get {
template <typename Tuple>
auto operator()(Tuple &&tuple) const ->
decltype(std::get<I>(std::forward<Tuple>(tuple))) {
return std::get<I>(std::forward<Tuple>(tuple));
}
};
int main() {
//removing const gets rid of warning
const std::vector<std::tuple<int,int>> v = {std::make_tuple(0, 0)};
//gives warning
(v | boost::adaptors::transformed(tuple_get<0>{})) [0];
}
Warning details:
include/boost/range/iterator_range_core.hpp:390:16: warning: returning reference to local temporary object [-Wreturn-stack-address]
return this->m_Begin[at];
note: in instantiation of member function 'boost::iterator_range_detail::iterator_range_base<boost::transform_iterator<tuple_get<0>,
std::__1::__wrap_iter<const std::__1::tuple<int, int> *>, boost::use_default, boost::use_default>, boost::random_access_traversal_tag>::operator[]' requested here
(v | boost::adaptors::transformed(tuple_get<0>{})) [0];
Adding flag -Wreturn-stack-address is not a solution since it's dangerous in bigger projects.
I noticed that deleting const keyword gets rid of warning but I don't know why and don't want to assume that functor gets only non-const ranges.
Questions: how to fix code to get rid of warning? Why deleting const gets rid of warning?
It's true.
//
// When storing transform iterators, operator[]()
// fails because it returns by reference. Therefore
// operator()() is provided for these cases.
//
So, you should be able to fix it with
(v | boost::adaptors::transformed(tuple_get<0>{})) (0);
which returns the abstract_value_type (which is the reference only if the elements are abstract, array or function, the value_type otherwise).