How do I use an std::fstream from within a block? - c++

I'm having trouble trying to use an ifstream from within a block. (This is part of a larger, complex project, so I whipped up a quick little source file with just the relevant parts.)
// foo.cpp, in its entirety:
#include <iostream>
#include <fstream>
#include <Block.h>
int main() {
__block std::ifstream file("/tmp/bar") ;
// ^ tried this with and without the __block
void (^block)() = ^{
file.rdbuf() ;
file.close() ;
file.open("/tmp/bar") ;
} ;
block() ;
}
If I declare the ifstream with __block, I get:
foo.cpp:6:24: error: call to implicitly-deleted copy constructor of
'std::ifstream' (aka 'basic_ifstream<char>')
__block std::ifstream file("/tmp/bar") ;
^~~~
If I declare it without __block, I get:
foo.cpp:8:3: error: call to implicitly-deleted copy constructor of
'const std::ifstream' (aka 'const basic_ifstream<char>')
file.rdbuf() ;
^~~~
// rdbuf() and (presumably) other const functions
foo.cpp:9:3: error: member function 'close' not viable: 'this' argument has
type 'const std::ifstream' (aka 'const basic_ifstream<char>'), but
function is not marked const
file.close() ;
^~~~
// open(), close(), and (presumably) other non-const functions
What's the proper way to use fstreams inside of blocks?

From Block Implementation Specification
It is an error if a stack based C++ object is used within a block if it does not have a copy constructor.
Which is the first error - ifstream blocks copy. __block requires copy.
As the quote says, one option is to declare ifstream on heap(new/delete).. but that is messy.
The rest of the errors are simple const correctness errors. Not declaring __block imports the objects as a const copy, which is the first of the two errors, and it cannot be used to call non const functions like close.
Try to switch to lamda expressions from C++11 and see if they alleviate these issues.

Related

Troubles using fstream in a class

I get the following error, when compiling:
1>c:\users\ra\source\repos\sandbox\game\gamesetup_1\gamesetup_1\main.cpp(15): error C2280: 'DebugLib::DebugLib(const DebugLib &)': attempting to reference a deleted function
1>c:\users\ra\source\commonincludes\tannic\debuglib\debuglib.h(41): note: compiler has generated 'DebugLib::DebugLib' here
1>c:\users\ra\source\commonincludes\tannic\debuglib\debuglib.h(41): note: 'DebugLib::DebugLib(const DebugLib &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::basic_fstream<char,std::char_traits<char>>::basic_fstream(const std::basic_fstream<char,std::char_traits<char>> &)'
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.16.27023\include\fstream(1421): note: 'std::basic_fstream<char,std::char_traits<char>>::basic_fstream(const std::basic_fstream<char,std::char_traits<char>> &)': function was explicitly deleted
1>Done building project "GameSetup_1.vcxproj" -- FAILED.
The code looks like this:
DebugLib.h:
#include <string>
#include <fstream>
class DebugLib
{
public:
DebugLib(); // Reset timestamp etc.
~DebugLib(); // Flush output buffer
void Init(uint8_t output, std::string fileName = "debug.log"); // Initializes Log
void Log(int category, std::string msg); // Add a line to the log
void Flush(); // Output the remains of the Debug buffer
void Shutdown(); // Shut it down
private:
unsigned int m_initTime;
unsigned int m_bufferPos;
std::string m_outputBuffer[DEBUG_MAXSIZE];
std::fstream m_fileStream;
uint8_t m_output;
bool m_running;
};
main.cpp:
#include <iostream>
#include <DebugLib.h>
using namespace std;
int main()
{
DebugLib gDebugger = DebugLib();
gDebugger.Init(DEBUG_LOG_TO_SCREEN);
cout << "Running!" << endl;
gDebugger.Shutdown();
cin.get();
return 0;
}
As soon as I declare m_fileStream I get the error. Do I have a wrong declaration ?
When I remove all the use of m_fileStream in DebugLib.cpp, the code compiles fine, and runs (but of course not as attended)
I couldn't find a duplicate even though I've seen this asked before, so:
Let's start by explaining the error messages. I'll ignore the line numbers and error codes, as those are rarely useful until after you've understood (or at least read) the rest of the error message.
'DebugLib::DebugLib(const DebugLib &)': attempting to reference a deleted function
This is the main error: an attempt to use a function that is deleted, namely the copy constructor for DebugLib. Since you did not explicitly specify a copy constructor, it is up to the compiler to define one for you. The compiler will define a naive copy if possible. If this definition is not possible, it will delete the copy constructor for you.
As you noticed, the compiler is able to define a naive copy until you add a field that cannot be copied (such as std::fstream).
note: compiler has generated 'DebugLib::DebugLib' here
This is a clarifying note that helps the error refer to two lines in your program. The line number that came with the main error message is where you tried to do the copy, and the line number that comes with this note is where the copy constructor is generated. The compiler is trying to be helpful because it doesn't know which location you'll want to change to address this error.
note: 'DebugLib::DebugLib(const DebugLib &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::basic_fstream<char,std::char_traits<char>>::basic_fstream(const std::basic_fstream<char,std::char_traits<char>> &)'
This note explains the thing you noticed: copying your class is prevented because the std::fstream member cannot be copied. This message uses the name basic_fstream at this point, so it helps to know that fstream is an instantiation of the basic_fstream template. So that mess of code at the end of this note just names the copy constructor of std::fstream.
note: 'std::basic_fstream<char,std::char_traits<char>>::basic_fstream(const std::basic_fstream<char,std::char_traits<char>> &)': function was explicitly deleted
This is a further clarification. The line before this said "deleted or inaccessible". This line clarifies that to "explicitly deleted".
Now that we have read the error, we can go look at the lines to which it refers. The troublesome line is
DebugLib gDebugger = DebugLib();
This line requests that a DebugLib object be default constructed then copied to gDebugger. And there's the problem: it cannot be copied! The solution is to simplify your logic by removing the copy. You can invoked the default constructor directly on gDebugger. (This works for other constructors as well, should your code need them.)
DebugLib gDebugger{};
As a bonus, your code is shorter.

How to resolve ambiguity between constructors taking std::string and std::vector

I want to make a "Tag" class that can have its name specified either as a dot separated name like "this.is.my.name" or as a vector of strings, like {"this","is","my","name"}.
When I try to do this, I am sometimes getting told by the compiler that my calls are ambiguous. I want to know (1) why this is ambiguous at all, and (2) why it is only ambiguous sometimes.
Here is my example code, which you can also view and compile here on Coliru
#include <string>
#include <vector>
#include <iostream>
class Tag
{
public:
explicit Tag(std::string name);
explicit Tag(std::vector<std::string> name);
};
Tag::Tag(std::string name)
{
//here 'name' will be a dotted collection of strings, like "a.b.c"
}
Tag::Tag(std::vector<std::string> name)
{
//here 'name' will be a vector of strings, like {"a","b","c"}
}
int main(int argc, char**argv)
{
Tag imaTag{{"dotted","string","again"}};
Tag imaTagToo{"dotted.string"};
//everything is fine without this line:
Tag imaTagAlso{{"dotted","string"}};
std::cout << "I made two tags" << std::endl;
}
With the indicated line, I get the following error:
g++ -std=c++11 -O2 -Wall -pthread main.cpp && ./a.out
main.cpp: In function 'int main(int, char**)':
main.cpp:28:39: error: call of overloaded 'Tag(<brace-enclosed initializer list>)' is ambiguous
Tag imaTagAlso{{"dotted","string"}};
^
main.cpp:18:1: note: candidate: 'Tag::Tag(std::vector<std::__cxx11::basic_string<char> >)'
Tag::Tag(std::vector<std::string> name)
^~~
main.cpp:13:1: note: candidate: 'Tag::Tag(std::__cxx11::string)'
Tag::Tag(std::string name)
^~~
Tag imaTagAlso{{"dotted","string"}}; says construct a Tag, call it imaTagAlso and initialize it with {"dotted","string"}. The issue with that is std::string can be constructed by a pair of iterators and since string literals can decay to const char*'s, they qualify as iterators. So you could either call the string constructor using the "iterators", or you could call the vector constructor using its std::initializer_list constructor. To work around this you can use
Tag imaTagAlso{{{"dotted"},{"string"}}};
which says construct a Tag, call it imaTagAlso and initialize it with {{"dotted"},{"string"}} and now {"dotted"} and {"string"} become elements of the std::initializer_list for the vector constructor.
You could also (since c++14) use std::string's user defined literal operator (""s) like
Tag imaTagAlso{{"dotted"s,"string"s}};
which makes each element of the braced-init-list std::string's, and the vector constructor will be chosen.

Unit testing a vector string

I have this really simple line of code in my production-code(A.cpp) as follows:
std::string A::getString(int i) {
return sVect_[i];
}
with the header as follows:
class A{
public:
std::string getString(int i);
...
private:
vector<std::string> sVect_;
...
};
I've been trying to test the getString() function using googletest but an error keeps popping out:
error: invalid conversion from 'char* (*)(const char*, int)throw ()' to 'int'
error: initializing argument 1 of 'std::string A::getString(i)'
This was my test program:
TEST(ATest, getString){
A a;
EXPECT_EQ("c", a.getString(i));
}
I couldn't quite grasp the workaround of the vector string and how to call it in my test program without ever changing the production code. I even use the hack, adding #define statements, to access the private member but still couldn't do it.
How do my test actually looks like to successfully call that function?
Note: I'm on Linux and using gcc. Thank you in advance guys.
Perhaps the error message is misleading. Have you defined i globally somewhere else? To me it looks like in the local scope because it does not know what the value of the variable i is, it is misbehaving in an unexpected way
TEST(ATest, getString){
A a;
EXPECT_EQ("c", a.getString(i)); //here what is the 'i' and where is it defined
}

Initializing Objects and assign it to no pointer variables

I am having trouble initializing a couple of objects.I am writing a program that will perform frontier based exploration for a mobile robot using Player/Stage simulation 2.0. I have a class called Explorer. The objects I am having difficulty initializing are robot, pp, lp. I look at the reference page online, and I believe it is because there is no assignment operator for this but im hoping there is another way to do it.;
This is my header
#ifndef EXPLORER_H_
#define EXPLORER_H_
#include <libplayerc++/playerc++.h>
#include <iostream>
#include <fstream>
#include <math.h>`
#include <list>
#include "Map.h"
using namespace PlayerCc;
using namespace std;
struct Pose {
double x;
double y;
double theta;
};
struct Frontier {
int startRow;
int startCol;
int endRow;
int endCol;
double score;
};
class Explorer {
public:
Explorer();
void Explore(Map *map);
void performLaserSweep(Map *map);
void detectandgroupFrontiers(Map *map);
Frontier score_pick_Frontier();
void goToFrontier(Frontier f);
private:
PlayerClient robot;
Position2dProxy pp;
LaserProxy *lp;
Pose pose;
list<Frontier> unexploredFrontiers;
};
#endif /* EXPLORER_H_ */
this is my .cc file all that matters is the constructor so that is all i am showing
#include "Explorer.h"
Explorer::Explorer() {
robot = new PlayerClient("127.0.0.1", 6665);
pp = new Position2dProxy(robot, 0);
lp = new LaserProxy(robot, 0);
if (lp == NULL) {
cerr << "Error initializing LASER" << endl;
exit(1);
}
pp.SetMotorEnable(true);
}
Thank you in advance for the help
this is the compiler error
Explorer.cc: In constructor ‘Explorer::Explorer()’:
Explorer.cc:11: error: no matching function for call to ‘PlayerCc::Position2dProxy::Position2dProxy()’
/usr/include/player-2.0/libplayerc++/playerc++.h:1566: note: candidates are: PlayerCc::Position2dProxy::Position2dProxy(PlayerCc::PlayerClient*, uint)
/usr/include/player-2.0/libplayerc++/playerc++.h:1553: note: PlayerCc::Position2dProxy::Position2dProxy(const PlayerCc::Position2dProxy&)
Explorer.cc:13: error: base operand of ‘->’ has non-pointer type ‘PlayerCc::PlayerClient’
Explorer.cc:13: error: expected unqualified-id before ‘new’
Explorer.cc:13: error: expected ‘;’ before ‘new’
Explorer.cc:14: error: no matching function for call to ‘PlayerCc::Position2dProxy::Position2dProxy(PlayerCc::PlayerClient&, int)’
/usr/include/player-2.0/libplayerc++/playerc++.h:1566: note: candidates are: PlayerCc::Position2dProxy::Position2dProxy(PlayerCc::PlayerClient*, uint)
/usr/include/player-2.0/libplayerc++/playerc++.h:1553: note: PlayerCc::Position2dProxy::Position2dProxy(const PlayerCc::Position2dProxy&)
Explorer.cc:15: error: no matching function for call to ‘PlayerCc::LaserProxy::LaserProxy(PlayerCc::PlayerClient&, int)’
/usr/include/player-2.0/libplayerc++/playerc++.h:900: note: candidates are: PlayerCc::LaserProxy::LaserProxy(PlayerCc::PlayerClient*, uint)
/usr/include/player-2.0/libplayerc++/playerc++.h:881: note: PlayerCc::LaserProxy::LaserProxy(const PlayerCc::LaserProxy&)
make: *** [all] Error 1
robot in the Explorer class is not a pointer, but you are trying to initialize it with the new keyword:
robot = new PlayerClient("127.0.0.1", 6665); // this won't work
Same thing with the variable pp.
One of the notes on an error you're getting: note: candidates are: PlayerCc::Position2dProxy::Position2dProxy(PlayerCc::PlayerClient*, uint) also suggest that the constructor needs a PlayerClient pointer.
Try this in the Explorer class:
PlayerClient *robot;
And don't forget do delete it when you are done with it.
An easy way to spot errors like these are looking closely at the error messages. When the error says error: base operand of ‘->’ has non-pointer type it simply means that you are trying to use the pointer operator -> on something that is not a pointer.
Rather than change your class's members to pointers (which comes with its own complications), consider initializing the members rather than assigning to them. Try a Google on "c++ member initializer list" (this one result may be a good place to start: http://www.cplusplus.com/forum/articles/17820/)
From the error it tells candidates are: PlayerCc::Position2dProxy::Position2dProxy(PlayerCc::PlayerClient*, uint) but you are passing robot which is not declared as a pointer. You have declared it as PlayerClient robot; However, you are using robot as a pointer to an object.
So change that to PlayerClient *robot; and this error should be taken care off.

Passing fstream reference as function parameter in C++11

I've just encountered an problem today: The following code seems to work in MSVC++ 2010 but not with Clang LLVM 4.1 (with GNU++11).
#include <fstream>
void foo(std::fstream& file){
file << "foo";
}
int main() {
std::fstream dummy("dummy");
foo(dummy);
return 0;
}
generates
Invalid operands to binary expression (std::fstream (aka basic_fstream<char>) and const char[4])
on Clang. I thought passing iostream arguments by reference would be common practice in C++. I'm not even sure if this is related to clang, C++11 or anything else.
Any idea how I can pass streams to functions then?
I assume that your original code (that you only partially posted in your original question) looked something like this:
#include <iosfwd>
void foo(std::fstream& file){
file << "foo";
}
int main() {
std::fstream dummy("dummy");
foo(dummy);
return 0;
}
Indeed, this gives the following error message with clang++ 3.2
Compilation finished with errors:
source.cpp:4:10: error: invalid operands to binary expression ('std::fstream' (aka 'basic_fstream<char>') and 'const char [4]')
file << "foo";
~~~~ ^ ~~~~~
source.cpp:8:17: error: implicit instantiation of undefined template 'std::basic_fstream<char, std::char_traits<char> >'
std::fstream dummy("dummy");
^
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/iosfwd:118:11: note: template is declared here
class basic_fstream;
^
2 errors generated.
Unfortunately, you only posted the first error message but not the second.
From the second error message, it is obvious that you only #include <iosfwd> but not #include <fstream>. If you fix that, everything will be OK.
Please post both the complete code and all the error messages next time.