Does view::join require copyable inner range? Why? - c++

Suppose that we have
cppcoro::generator<int> gen_impl(int in) {
const auto upper = in + 10;
for (; in < upper; ++in)
co_yield in;
}
cppcoro::generator<cppcoro::generator<int>> gen() {
for (int n = 1; n < 100; n += 10)
co_yield gen_impl(n);
}
So we can iterate inner range just fine
for (auto&& row : gen() ) {
for (auto n : row)
std::cout << n << ' ';
std::cout << '\n';
}
NOTE: range-for on ref is required because cppcoro::generator doesn't allow copying (deleted copy ctor)
Print
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100
But when we try to "flattern" with view::join
auto rng = gen();
for (auto n : rng | ranges::view::join) {
std::cout << n << '\n';
};
It seems view::join require Copyable inner range?
In file included from <source>:3:
In file included from /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view.hpp:38:
In file included from /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/for_each.hpp:23:
/opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/join.hpp:320:50: error: call to deleted constructor of 'cppcoro::generator<cppcoro::generator<int> >'
return join_view<all_t<Rng>>{all(static_cast<Rng&&>(rng))};
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/view.hpp:112:21: note: in instantiation of function template specialization 'ranges::v3::view::join_fn::operator()<cppcoro::generator<cppcoro::generator<int> > &, false, nullptr>' requested here
v.view_(static_cast<Rng&&>(rng))
^
/opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/utility/functional.hpp:731:42: note: in instantiation of function template specialization 'ranges::v3::view::view<ranges::v3::view::join_fn>::pipe<cppcoro::generator<cppcoro::generator<int> > &, ranges::v3::view::view<ranges::v3::view::join_fn> &, false, nullptr>' requested here
pipeable_access::impl<Pipe>::pipe(static_cast<Arg&&>(arg), pipe)
^
<source>:35:21: note: in instantiation of function template specialization 'ranges::v3::operator|<cppcoro::generator<cppcoro::generator<int> > &, ranges::v3::view::view<ranges::v3::view::join_fn>, false, nullptr>' requested here
for (auto n : rng | ranges::view::join) {
^
/opt/compiler-explorer/libs/cppcoro/include/cppcoro/generator.hpp:174:3: note: 'generator' has been explicitly marked deleted here
generator(const generator& other) = delete;
^
/opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/join.hpp:76:36: note: passing argument to parameter 'rng' here
explicit join_view(Rng rng)
^
What makes this not compiled?
Is there any bug in range-v3 or cppcoro?
Only incompatible design decisions?
godbolt (Full)

In range-v3, a move-only view is OK. That got implemented late and there may still be bugs, but that's not what is happening here.
The first problem is that you are trying to adapt an lvalue of type cppcoro::generator here:
auto rng = gen();
for (auto n : rng | ranges::view::join) {
Since a generator is a view, the join view will want to copy it. It can't because it is not copyable.
You can fix this problem by moving the generator in:
auto rng = gen();
for (auto n : std::move(rng) | ranges::view::join) {
Then you run into the next problem, which is that the reference type of generator<generator<int>> is const generator<int>&, and you have the same problem again: the join wants to hold a copy of the inner generator while it iterates over it, but it cannot make a copy.
The workaround is a bit ugly: change the generator to return a non-const lvalue reference:
cppcoro::generator<cppcoro::generator<int>&> gen() {
for (int n = 1; n < 100; n += 10) {
auto tmp = gen_impl(n);
co_yield tmp;
}
}
and then std::move each inner range with a move view:
auto rng = gen();
for (auto n : std::move(rng) | ranges::view::move | ranges::view::join) {
std::cout << n << '\n';
}
The result compiles. Whether it runs or not depends on how gracefully cppcoro handles the case where someone steals away the guts of the value that it safely tucked away in the coroutine's promise type.
https://godbolt.org/z/mszidX
A note about the future std::view::join:
The join view that will ship with C++20 is a little different. If the outer range's reference type is a real reference (as in this case), it will not try to make a copy of the view to which it refers. That means in C++20, you won't need the ugly view::move hack.
However, the C++20 View concept currently requires copyability so this solution still won't work. We have a TODO item to relax this before C++20 ships, but there's no telling how the Committee will like that idea.

Related

How to sort std::pair first only?

I have task to make method that:
get numbers from string
every number is equal to its summed sub-numbers
now sort these sums
display the sorted sums and their original values
I made that function:
void orderWeight(const std::string &strng)
{
std::vector<pair<int, int>> result;
int r{}, rr{}, rrr{};
std::stringstream ss(strng);
while(ss>>r){
rrr = r;
while(r!=0){
rr += r%10;
r /= 10;
}
result.push_back( make_pair(rr, rrr));
rr = 0;
r = 0;
}
std::sort( result.begin(), result.end());
for( int i=0; i<result.size();i++){
cout<<result[i].first<<" "<<result[i].second<<endl;
}
}
This function for arguments - 56 65 74 100 99 68 86 180 90 returns :
1 100
9 90
9 180
11 56
11 65
11 74
14 68
14 86
18 99
But I don't want to sort it by first value THEN by second as You can see in
9 90
9 180
If two numbers are the same, don't sort them by second value, just how are they in a string.
How can I make it happen?
You can make it happen by providing a predicate to sort that tells it how to sort. For example, a simple lambda:
std::sort( result.begin(), result.end(),
[](const auto& lhs, const auto& rhs) {
return lhs.first < rhs.first;
});
The above will sort only by the first value.
See also: https://en.cppreference.com/w/cpp/algorithm/sort

How do I call the value in array and change the value in array?

class DataStorage{
// 0 1 2 3 4 5 6 7 8
string Data[20][4]={{"Wee","50","1","First"},{"Wee","22","2","First"},
// 9 10 11 12 13 14 15 16
{"Jason","26","3","First"},{"Krappa","12","4","First"},
// 17 18 19 20 21 22 23 24
{" "," ","5","First"},{" "," ","6","Economy"},
//25 26 27 28 29 30 31 32
{"Kappa","15","7","Economy"},{"Eraser","17","8","Economy"},
//33 34 35 36 37 38 39 40
{" "," ","9","Economy"},{"Morty"," ","10","Economy"},
//41 42 43 44 45 46 47 48
{"Rick"," ","11","Economy"},{"Amanda","10","12","Economy"},
//49 50 51 52 53 54 55 56
{"Lee","","13","Economy"},{"MingLee"," ","14","Economy"},
//57 58 59 60 61 62 63 64
{"Beauty"," ","15","Economy"},{"S4head"," ","16","Economy"},
//65 66 67 68 69 70 71 72
{"Ivan"," ","17","Economy"},{"Dex"," ","18","Economy"},
//73 74 75 76 77 78 79 80
{"Chua"," ","19","Economy"},{"Haha"," ","20","Economy"},};
};
int main(){
}
How do I call the value in array and change the value in array? Do I need to make some function to get value from the input and pass it into a variable in class and set it into my array?
I'm not sure what you're asking when you say How do I call the value in array and change the value in array? but I think you're asking how do you change the value of an array element.
To modify an array element you assign the array's index to what you're changing the array's element to; however, remember that C++ arrays are 0-index arrays meaning when you start counting their elements at 0. For example the following code modifies the element at index 5. Live preview
#include <iostream>
int array[10] = {1, 5, 33, 7, -23, 2, 8, 54, 19, 2};
int main() {
std::cout << array[5] << std::endl;
array[5] = 100; // Set the value of the element at index 5 to 100
std::cout << array[5] << std::endl;
return 0;
}
If you want to have Data as a class member of DataStorage you have to initialize it in the member initialization list. I also highly recommend to use an abstraction for the bare array, like std::array. This allows to use bounds-checked access with the at() function. You can then access Data and change it's contents.
#include <array>
#include <iostream>
#include <string>
class DataStorage
{
public:
std::array<std::array<std::string,4>,20> Data;
DataStorage() : Data({{
{{"Wee","50","1","First"}},
{{"Wee","22","2","First"}},
{{"Jason","26","3","First"}},
{{"Krappa","12","4","First"}},
{{" "," ","5","First"}},
{{" "," ","6","Economy"}},
{{"Kappa","15","7","Economy"}},
{{"Eraser","17","8","Economy"}},
{{" "," ","9","Economy"}},
{{"Morty"," ","10","Economy"}},
{{"Rick"," ","11","Economy"}},
{{"Amanda","10","12","Economy"}},
{{"Lee","","13","Economy"}},
{{"MingLee"," ","14","Economy"}},
{{"Beauty"," ","15","Economy"}},
{{"S4head"," ","16","Economy"}},
{{"Ivan"," ","17","Economy"}},
{{"Dex"," ","18","Economy"}},
{{"Chua"," ","19","Economy"}},
{{"Haha"," ","20","Economy"}}
}}) {}
};
int main()
{
DataStorage d;
std::cout << d.Data.at(10).at(2) << '\n'; // prints 11
d.Data.at(10).at(2) = "1729";
std::cout << d.Data.at(10).at(2) << '\n'; // prints 1729
}

shared_ptr vs. new operator: which one to use

In the function below I have made use of http_client from cpprestsdk (https://github.com/Microsoft/cpprestsdk) to make http requests to a network camera. The function below is probably a callback called by the lcm library (http://lcm-proj.github.io/) when a certain request is made.
I had problems with line 11. I was previously using the new operator:
auto init_session_response = new init_session_response_t;
to create the pointer and manually delete it just before exiting the function.
But I got a access violation exception when trying to modify the init_session_response object in the pplx task continuation at line 49.
init_session_response->status_code =
ptz_camera::status_codes_t::OK;
This problem went away when I started using std::shared_ptr. Can someone explain to me why using shared_ptr solved the problem? Should the http_client* also be created using std::shared_ptr?
1 void lcm_handler::on_init_session_req(const lcm::ReceiveBuffer* rbuff,
2 const std::string& channel,
3 const ptz_camera::init_session_request_t* req)
4 {
5 std::cout << "Received init session req on channel: " << channel <<
6 "; Camera: " << req->ip_address << std::endl;
7
8 auto ip_address = req->ip_address;
9
10 // Note use of std::shared_ptr
11 auto init_session_response = make_shared<ptz_camera::init_session_response_t>();
12
13 auto key_position = this->ip_client_map.find(ip_address);
14 if (key_position == ip_client_map.end())
15 {
16 std::cout << "Creating a new client for the ip: "
17 << req->ip_address << endl;
18
19 wstring username = this->convert_to_wstring(req->username);
20 wstring password = this->convert_to_wstring(req->password);
21
22 wstring main_uri = L"http://" + convert_to_wstring(ip_address);
23 auto config = http_client_config();
24 auto login = credentials(username, password);
25 config.set_credentials(login);
26 config.set_timeout(std::chrono::milliseconds(500));
27
28 http_client* client = new http_client(main_uri, config);
29 std::cout << "Client created...\n";
30
31 uri_builder uri = uri_builder(U("/") + uri_constants::stw_cgi).
32 append_path(uri_constants::attributes_cgi).append_path(uri_constants::attributes);
33
34 auto request = uri.to_string();
35
36 client->request(methods::GET, request)
37 .then([this, ip_address, client, init_session_response]
38 (pplx::task<http_response> request_task) -> pplx::task<wstring>
39 {
40 try
41 {
42 auto response = request_task.get();
43 if (response.status_code() == status_codes::OK)
44 {
45 std::cout << "Saving client...";
46 this->ip_client_map[ip_address] = client;
47 std::cout << "success.\n";
48
49 init_session_response->status_code =
50 ptz_camera::status_codes_t::OK;
51 }
52
53 else
54 {
55 cout << "GET request to client failed! HTTP Error: "
56 << response.status_code() << std::endl;
57
58 init_session_response->status_code =
59 ptz_camera::status_codes_t::ERR;
60 }
61
62 return response.extract_string();
63 }
64
65 catch (const exception& e)
66 {
67 cout << "Caught exception: " << e.what() << endl;
68 return create_task([e, this]() -> wstring
69 {
70 return convert_to_wstring(e.what());
71 });
72 }
73
74 })
75 .then([init_session_response, this](wstring response)
76 {
77 string n = this->convert_to_string(response);
78 init_session_response->response_message = n;
79 });
80 }
81
82
83 else
84 {
85 string message = "Client for ip: " + req->ip_address + " already exists\n";
86 cout << message << endl;
87 init_session_response->response_message = message;
88 init_session_response->status_code = ptz_camera::status_codes_t::OK;
89 }
90
91 this->lcm->publish(ptz_camera_channels::init_session_res_channel,
92 init_session_response.get());
93}
When you get violation access error, you must have deleted the pointer in some place in your code (e.g. in some then lambdas), but you did not post your code using raw pointer so I can not say which line.
By using std::shared_ptr, when it's passed to the lambda, it's captured by value, so it increase the use_count and ensures that init_session_response is valid and not destructed in the labmda, which solves the issue.

Decimate vector in eigen

I have a float array Eigen::ArrayXf which I need to decimate (i.e. pick 1 out of f.i. 8 samples).
Eigen::ArrayXf decimatedSignal = Eigen::Map<Eigen::ArrayXf, 0, Eigen::InnerStride<8> >(signal.data(), length, 1).eval();
which works, with a caveat: I need to know how long length is, and it can be specified too long, leading to runtime errors.
Q: is there a way to decimate all that is possible, so that resultant length is == signal.size() / 8 ?
Two things. You are using the c'tor for mapping a matrix:
Map (
PointerArgType dataPtr,
Index nbRows,
Index nbCols,
const StrideType & a_stride = StrideType()
)
Constructor in the dynamic-size matrix case.
Parameters
dataPtr pointer to the array to map
nbRows the number of rows of the matrix expression
nbCols the number of columns of the matrix expression
a_stride optional Stride object, passing the strides.
I think you want the c'tor for a vector:
Map ( PointerArgType dataPtr,
Index a_size,
const StrideType & a_stride = StrideType()
)
Constructor in the dynamic-size vector case.
Parameters
dataPtr pointer to the array to map
a_size the size of the vector expression
a_stride optional Stride object, passing the strides.
The second thing is that you want length == signal.size())/8. Is that always a whole integer, or are you rounding up? If the data is 16 in length and you want the positions [0] and [8], then use 1+(signal.size()-1)/8 as the length parameter:
Eigen::ArrayXf decimatedSignal = Eigen::Map<Eigen::ArrayXf, 0, Eigen::InnerStride<8> >(signal.data(), 1+((signal.size()-1)/8) ).eval();
For example:
#include <Eigen/Core>
#include <iostream>
using std::cout;
using std::endl;
int main(int argc, char *argv[])
{
Eigen::VectorXf signal;
signal.setLinSpaced(64, 0.0, 63.);
cout << "Original signal:" << endl << signal.transpose() << endl;
Eigen::ArrayXf decimatedSignal = Eigen::Map<Eigen::ArrayXf, 0,
Eigen::InnerStride<8> >(signal.data(), 1+((signal.size()-1)/8)).eval();
cout << endl << "Decimated:" << endl << decimatedSignal.transpose() << endl;
return 0;
}
outputs
Original signal:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
Decimated:
0 8 16 24 32 40 48 56
which I think is exactly what you want.

conversion from ‘std::vector<AdjacencyData> (*)()’ to non-scalar type ‘std::vector<AdjacencyData>’ requested

I have defined a Lsdb class with a function and parameters as below.
47 AdjacencyData
48 getAdjacentNode(const std::string routerName, const std::string adjacentNodeName);
49
50 private:
51 std::vector<AdjacencyData> m_AdjacencyList;
52 std::map<std::string, std::vector<AdjacencyData>()> m_Lsdb;
In the implementation of the method, I have below code.
43 Lsdb::getAdjacentNode(const std::string routerName, const std::string nodeName)
44 {
45
46 AdjacencyData nodeData;
47
48 // Get the adjacency list for given router.
49 std::map<std::string, std::vector<AdjacencyData>()>::iterator itr = m_Lsdb.find(routerName);
50 if (itr != m_Lsdb.end())
51 {
52 // Get the specific node data from list.
53 std::vector<AdjacencyData> nodeList = itr->second;
54
55 std::vector<AdjacencyData>::iterator listItr = nodeList.begin();
56 for(; listItr != nodeList.end(); listItr++)
57 {
58 nodeData = *listItr;
59 if (nodeData.getRouterName().compare(nodeName))
60 {
61 return nodeData;
62 }
63 }
64 }
65
66 return nodeData;
67 }
On compiling, I am getting the following error. Can someone please help understand what is the problem with this code?
In file included from /usr/include/c++/4.6/bits/stl_algobase.h:65:0,
from /usr/include/c++/4.6/bits/char_traits.h:41,
from /usr/include/c++/4.6/ios:41,
from /usr/include/c++/4.6/ostream:40,
from /usr/include/c++/4.6/iostream:40,
from ./ns3/assert.h:48,
from ../src/lsdb.cc:22:
/usr/include/c++/4.6/bits/stl_pair.h: In instantiation of ‘std::pair<const std::string, std::vector<AdjacencyData>()>’:
../src/lsdb.cc:50:25: instantiated from here
/usr/include/c++/4.6/bits/stl_pair.h:93:11: error: field ‘std::pair<const std::string, std::vector<AdjacencyData>()>::second’ invalidly declared function type
../src/lsdb.cc: In member function ‘AdjacencyData Lsdb::getAdjacentNode(std::string, std::string)’:
../src/lsdb.cc:53:48: error: conversion from ‘std::vector<AdjacencyData> (*)()’ to non-scalar type ‘std::vector<AdjacencyData>’ requested
you error is in this line:
std::map<std::string, std::vector<AdjacencyData>()>::iterator itr = m_Lsdb.find(routerName);
you are putting std::vector<AdjacencyData>(), which is a function, as the 2nd type of std::map, you should use std::vector<AdjacencyData>.