C++ range-v3: trying to chain together transforms - c++

I'm completely new to the range library, so I shouldn't be surprised that this code isn't compiling and I cannot figure out why:
#include <iostream>
#include <algorithm>
#include <fstream>
#include <iterator>
#include <vector>
#include <range/v3/all.hpp>
#include <range/v3/view/all.hpp>
using namespace ranges::v3;
std::ifstream open_file(const std::string &filename) {
return std::ifstream{filename};
}
int count_lines(std::ifstream &in) {
return std::count(std::istreambuf_iterator<char>{in},
std::istreambuf_iterator<char>{}, '\n');
}
std::vector<int>
count_lines_in_files(const std::vector<std::string> &filenames) {
auto a1 = filenames | view::transform(open_file) | view::transform(count_lines);
return a1;
}
int main() {
const std::vector<std::string> files{"listing1_1.cpp",
"listing1_2.cpp",
"listing1_4.cpp",
"listing1_5.cpp"};
const auto result = count_lines_in_files(files);
std::cout << ranges::view::all(result) << '\n';
}
It appears that the complaint is about a1, which the compiler tells me "error: variable has incomplete type 'void'."
Can someone see what I'm doing wrong, or tell me how to properly chain these together if possible?
Thanks in advance!

As noted by Porsche9II, "std::ifstream doesn't have a copy constructor". You can find more on this topic here:
Why are iostreams not copyable?
C++11 introduced a move constructor (6) for std::basic_ifstream, so you could write
auto open_file(const std::string &filename) {
return std::ifstream{filename};
}
auto count_lines(std::ifstream &&in) {
return std::count(std::istreambuf_iterator<char>{in},
std::istreambuf_iterator<char>{}, '\n');
}
Testable HERE.

std::ifstream doesn't have a copy constructor - returning std::ifstream by a function is not a good idea. One possible solution: opening and counting should take place in one function.

Related

std::variant cout in C++

I am relatively new to CPP and have recently stumbled upon std::variant for C++17.
However, I am unable to use the << operator on such type of data.
Considering
#include <iostream>
#include <variant>
#include <string>
using namespace std;
int main() {
variant<int, string> a = "Hello";
cout<<a;
}
I am unable to print the output. Is there any short way of doing this? Thank you so much in advance.
You can use std::visit if you don't want to use std::get.
#include <iostream>
#include <variant>
struct make_string_functor {
std::string operator()(const std::string &x) const { return x; }
std::string operator()(int x) const { return std::to_string(x); }
};
int main() {
const std::variant<int, std::string> v = "hello";
// option 1
std::cout << std::visit(make_string_functor(), v) << "\n";
// option 2
std::visit([](const auto &x) { std::cout << x; }, v);
std::cout << "\n";
}
use std::get
#include <iostream>
#include <variant>
#include <string>
using namespace std;
int main() {
variant<int, string> a = "Hello";
cout << std::get<string>(a);
}
If you want to get automatically, it can't be done without knowing its type. Maybe you can try this.
string s = "Hello";
variant<int, string> a = s;
cout << std::get<decltype(s)>(a);
#include <iostream>
#include <variant>
#include <string>
int main( )
{
std::variant<int, std::string> variant = "Hello";
std::string string_1 = std::get<std::string>( variant ); // get value by type
std::string string_2 = std::get<1>( variant ); // get value by index
std::cout << string_1 << std::endl;
std::cout << string_2 << std::endl;
//may throw exception if index is specified wrong or type
//Throws std::bad_variant_access on errors
//there is also one way to take value std::visit
}
Here is the description link: https://en.cppreference.com/w/cpp/utility/variant

string has no member named 'reverse'

I am trying to reverse a string (c++, compiling with g++).
Isn't string considered a container for the algorithm functions?
This is the code:
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string str = "hello";
str.reverse(str.begin(), str.rbegin());
return 0;
}
Thanks
The std::string class template does not have a member function called reverse. There is a std::reverse function located in the <algorithm> header. You probably want to use it in a following manner:
#include <string>
#include <algorithm>
int main() {
std::string str = "hello";
std::reverse(str.begin(), str.end());
}
Note the use of str.end() in place of your str.rbegin(). You can also define a new string and use the string constructor overload that accepts reverse iterators:
#include <string>
int main() {
std::string str = "hello";
std::string reversestr(str.rbegin(), str.rend());
}
std::string has no method reverse. But std::reverse exists:
#include <string>
#include <algorithm>
#include <iostream>
int main()
{
std::string str = "hello";
std::reverse(str.begin(), str.end());
std::cout << str << "\n"; // prints "olleh"
}

.txt to vector <string>, non-class type error

Hi I just started to learn C++ this week and I require some assistance.
Basically what I am trying to do is read from a .txt file and "convert" it into a vector string and then display it.
my error is at this line: text.readFile("scenario.txt"), it says: "request for member 'readFile' in 'text', which is of non-class type 'Conversion()'"
what does that mean?
and also my method getLines() could not be resolved.
main.cpp
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include "Conversion.h"
using namespace std;
int main()
{
vector<string> lines;
Conversion text();
if(text.readFile("scenario.txt") == true)
lines = text.getLines();
for(int i = 0; i < lines.size(); ++i)
cout << lines[i] << endl;
return 0;
}
Conversion.cpp
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include "Conversion.h"
using namespace std;
vector<string> lines;
Conversion::Conversion(std::vector<std::string> lines) {
lines.clear();
}
Conversion::Conversion() {
}
Conversion::~Conversion() {
}
bool Conversion::readFile(string filename) {
ifstream file;
string line;
file.open(filename.c_str());
if(!file.is_open())
return false;
while(getline(file, line))
lines.push_back(line);
return true;
}
vector<string> Conversion::getLines(){
return lines;
}
Conversion.h
#ifndef CONVERSION_H_
#define CONVERSION_H_
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
class Conversion {
public:
Conversion();
Conversion(std::vector <std::string>);
~Conversion();
std::vector<std::string> getLines();
bool readFile(std::string);
private:
std::vector<std::string> lines;
std::string line;
std::ifstream file;
};
#endif
Once again, Conversion text(); is a function declaration, not a class instantiation. To call the default constructor, change it to Conversion text;
You probably wanted to copy the passed lines in your constructor:
Conversion::Conversion(std::vector<std::string> const& lines) : lines(lines) { }
Your code should work now, but there can be done some improvements. To avoid copy, getLines should return by reference-to-const:
std::vector<std::string> const& getLines();
// you don't have to create lines in main, you can print like this:
for(auto const& x : text.getLines())
cout << x << endl;
and I'd use it even here:
bool readFile(std::string const&);
I hope this is the last thing - std::ifstream constructor and open function also take std::string:
file.open(filename);
You need to remove the parentheses when instantiating the Conversion object:
Conversion text;
See this question for detailed answers: Is no parentheses on a constructor with no arguments a language standard?

how to bind a template function

Hi to all you boost gurus out there!
I want to find a certain element in a vector of strings, ignoring case:
#include <iostream>
#include <string>
#include <vector>
#include "boost/algorithm/string.hpp"
#include "boost/bind.hpp"
using std::string;
using std::vector;
bool icmp(const string& str1, const string& str2)
{
return boost::iequals(str1, str2);
}
int main(int argc, char* argv[])
{
vector<string> vec;
vec.push_back("test");
// if (std::find_if(vec.begin(), vec.end(), boost::bind(&boost::iequals<string,string>, "TEST", _1)) != vec.end()) <-- does not compile
if (std::find_if(vec.begin(), vec.end(), boost::bind(&icmp, "TEST", _1)) != vec.end())
std::cout << "found" << std::endl;
return 0;
}
This is working fine so far, but what I would like to know is, if it is possible to get rid of the extra function (icmp()) and invoke iequals (template function) directly (like in the commented line).
Thanks in advance!
Adding the template params and the default locale parameter works on my machine.
if (std::find_if(vec.begin(), vec.end(), boost::bind(&boost::iequals<string,string>, "TEST", _1, std::locale())) != vec.end())
std::cout << "found" << std::endl;
Compiler is VS2010.
I'm sure this is not what you're hoping for, but this appears to be the only fix I can work out (with g++ c++03 mode):
typedef bool (*fptr)(const std::string&, const std::string&);
if (std::find_if(vec.begin(), vec.end(),
boost::bind((fptr) &boost::iequals<string,string>, "TEST", _1)
) != vec.end())
Using boost lambda :)
#include <iostream>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <algorithm>
#include <vector>
using namespace std;
using namespace boost::lambda;
int main()
{
vector<string> vec;
vec.push_back("TEST");
vec.push_back("test");
vector<string>::iterator it;
it = find_if(vec.begin(),vec.end(),_1 == "test");
cout << *it << endl;
return 0;
}

BOOST.IOstreams: trouble to write to bzip2

Hello I am would like to store my data in to bzip2 file using Boost.IOstreams.
void test_bzip()
{
namespace BI = boost::iostreams;
{
string fname="test.bz2";
{
BI::filtering_stream<BI::bidirectional> my_filter;
my_filter.push(BI::combine(BI::bzip2_decompressor(), BI::bzip2_compressor())) ;
my_filter.push(std::fstream(fname.c_str(), std::ios::binary|std::ios::out)) ;
my_filter << "test" ;
}//when my_filter is destroyed it is trowing an assertion.
}
};
What I am doing wrong?
I am using boost 1.42.0.
kind regards
Arman.
EDIT
The code is working if I remove the bidirectional option:
#include <fstream>
#include <iostream>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <string>
void test_bzip()
{
namespace BI = boost::iostreams;
{
std::string fname="test.bz2";
{
std::fstream myfile(fname.c_str(), std::ios::binary|std::ios::out);
BI::filtering_stream<BI::output> my_filter;
my_filter.push(BI::bzip2_compressor()) ;
//my_filter.push(std::fstream(fname.c_str(), std::ios::binary|std::ios::out)) ; //this line will work on VC++ 2008 V9 but not in G++ 4.4.4
my_filter.push(myfile);
my_filter << "test";
}
}
};
maybe some one can explain why?
An fstream can not be copied, so you must use the reference version of push
template<typename StreamOrStreambuf>
void push( StreamOrStreambuf& t,
std::streamsize buffer_size = default value,
std::streamsize pback_size = default value );
So your function should look something like
std::fstream theFile(fname.c_str(), std::ios::binary | std::ios::out);
// [...]
my_filter.push(theFile) ;
I'm suprised you compiler allows your code, I'd think it complain about a reference to temporary... which compiler are you using?