I am trying to reduce code redundancy using the overload of an output operator to a stream instead of print functions.
//***.h
class MainWind : public QWidget
{
Q_OBJECT
public:
explicit MainWind(QWidget *parent = nullptr);
~MainWind();
MainWind *operator<<(const QString &str);
private:
Ui::MainWind *ui;
};
//***.cpp
MainWind *MainWind::operator<<(const QString &str)
{
ui->serverConsole->insertPlainText(str);
return this;
}
At this moment everything compiles successfully.
But when I try to use:
//other.cpp
MainWind *mainWind = new MainWind;
mainWind << QString("str");
I got this error:
ServerSocket.cpp:39: error: invalid operands to binary expression
('MainWind *' and 'QString')
qstring.h:1410: candidate function not viable: no known conversion from >'MainWind *' to 'QDataStream &' for 1st argument
...
And there are a lot of candidates for this position)
Or
//other.cpp
MainWind *mainWind = new MainWind;
mainWind <<"str";
I got this error:
ServerSocket.cpp:39: error: invalid operands to binary expression
('MainWind *' and 'const char [4]') ServerSocket.cpp:39: error:
invalid operands of types 'MainWind*' and 'const char [4]' to binary
'operator<<'
CurrentSession::inst().mainWind() << "str";
^
What could be the problem?
ADDITION TO THIS QUESTION:
Attempt to use:
//*.h
friend MainWind *operator<<(MainWind *out,QString &str);
//***.cpp
MainWind * operator<<(MainWind *out, QString &str)
{
out->ui->serverConsole->insertPlainText(str);
return out;
}
Compilation of previous code is successful.
According to the idea, if the first operand could not be a pointer, this code would not compile...
But when using this:
//other.cpp
MainWind *mW = new MainWind;
mW << "str";
Compilation go to error:
ServerSocket.cpp:37: error: invalid operands of types 'MainWind*' and 'const char [4]' to binary 'operator<<'
mW << "str";
^
You need to use *mainWind << QString("str");. The LHS has to be an object, not a pointer.
While at it, I strongly recommend changing the operator<< function to return a reference to the object, not a pointer.
MainWind& operator<<(const QString &str);
and the implementation to
MainWind& MainWind::operator<<(const QString &str)
{
ui->serverConsole->insertPlainText(str);
return *this;
}
That will allow you to chain the operator.
*mainWind << QString("str") << QString("Second string");
You overloaded << on MainWnd not MainWnd*.
*mainWind << QString("str");
Also you want QString const&
//other.cpp
MainWind *mainWind = new MainWind;
mainWind <<"str";
The reason is that mainWind << "str" looks for an operator<<() that accepts two arguments, the first of which is a MainWind *.
Whereas, you have defined a MainWind::operator<<() which is called with the first argument a MainWind &. There is no direct conversion from a MainWind * to a MainWind & (or to a MainWind). Hence the error message.
One way to get the code to compile is to change mainWind <<"str" to *mainWind << "str". The * dereferences the pointer, and produces a reference, which is what your operator<<() expects.
The catch is then that
*mainWind << "str" << "another str";
will not compile, since it is equivalent to
(*mainWind).operator<<("str") << "another str";
where (*mainWind).operator<<("str") returns a MainWind *. This causes the same problem (again) when trying to stream "another str".
The real fix is to change operator<<() so it returns a reference
// within the class definition of MainWind
MainWind &operator<<(const QString &str);
// definition of the operator<<()
MainWind &MainWind::operator<<(const QString &str)
{
ui->serverConsole->insertPlainText(str);
return *this;
}
and change the calling code to either
//other.cpp version 2
MainWind *mainWind = new MainWind;
*mainWind <<"str";
// this will work too
*mainWind << "str" << "another str";
// note reliance on cleaning up mainWind to avoid a memory leak
delete mainWind;
There is no other fix that would allow you to use mainWind << "str" since overloading non-member operator<<() is only permitted on class or enumerated types, not on pointers.
Related
I have following overloaded function
void testFun(QByteArray& arr){
QTextStream out(stdout);
out << "QByte" << endl;
}
void testFun(QString str){
QTextStream out(stdout);
out << "QStr" << endl;
}
Why function void testFun(QString str) is called if I use const QByteArray as argument.
It means - this block of code:
QByteArray bA("aaa");
const QByteArray bB(bA);
testFun(bA);
testFun(bB);
gives following output:
QByte
QStr
Since the first overload takes a non-const QByteArray, it does not get used.
C++ has a feature called Converting Constructors. This means that a single-argument constructor that is not marked as explicit can be used automatically to convert one type into another for function overload resolution.
QString has such a constructor that takes a const QByteArray&. Therefore, when selecting what function overload to use, the compiler can first convert the QByteArray to a QString using that constructor, and then proceed to pass that QString into your second function.
Please see http://en.cppreference.com/w/cpp/language/overload_resolution for more information.
The underlying cause is that Qt (and by extension: you!) allows writing non-typesafe code: there's an implicit conversion from QByteArray to QString. Define QT_RESTRICTED_CAST_FROM_ASCII project-wide, wipe the build folder and rebuild the project. The call that uses the const object won't compile then, and you'll have to fix the function signatures: you should have been taking const references all along.
void testFun(const QByteArray &arr){
QTextStream out(stdout);
out << "QByte" << endl;
}
void testFun(const QString &str) {
QTextStream out(stdout);
out << "QStr" << endl;
}
In main:
void HandleAction(const RandomWriter & rw, string choice)
{
if(choice == "P")
{
string fileInput;
cout << "Change input file: " << endl;
cin >> fileInput;
rw.SetFilename(fileInput);
}
}
In a RandomWriter class:
void RandomWriter::SetFilename(string filename)
{
string text = GetFullFile(filename);
if (text != "")
{
fullText = text;
this->filename = filename;
}
/
Why am i getting this error when i try to pass fileInput as a parameter to SetFileName?
Thanks in advance guys!
||=== error: passing 'const RandomWriter' as 'this' argument of 'void RandomWriter::SetFilename(std::string)' discards qualifiers [-fpermissive]|
In the HandleAction function you say that the rw is a reference to a constant RandomWriter object. Then you try to call a member function on the rw object that tries to modify the constant object. That is of course not allowed, you can't modify constant objects.
So the simple solution is to remove the const part of the argument specification:
void HandleAction(RandomWriter & rw, string choice) { ... }
// ^^^^^^^^^^^^^^^^^
// Note: No longer constant
On a related note, you should probably use references to constant objects for the strings though, no need to copy them all the time.
Your RandomWriter parameter rw is declared const in your HandleAction() method, and is thus immutable and unable to be changed by your call to SetFilename().
So I got stuck on a simple print-function today and I really don't know how to fix this problem. Basically I want to pass my Strings to a function in a std::cout-style like this:
foo(std::ostringstream() << "Hello" << " World!");
And from what I've read it should be possible by a function along the lines of
void foo(std::ostringstream& in)
but when implementing I get a somewhat strange behavior:
#include <iostream>
#include <sstream>
void foo(std::ostringstream& in)
{
std::cout << in.str();
}
int main()
{
std::ostringstream working;
working << "Hello" << " World!";
foo(working); // OK
std::ostringstream notWorking;
notWorking << "Hello";
foo(notWorking<<" World!"); // Not OK?!
return 0;
}
While the first call of foo seems fine and works like expected, the second one refuses to compile even though they should (from my perspective) be technically the same thing.
Error:
error C2664: 'void foo(std::ostringstream &)': cannot convert parameter 1 from 'std::basic_ostream<char,std::char_traits<char>>' to 'std::ostringstream &'
I'm using MS Visual Studio 2013 Express on Win7 x64
The overloads of shifting operators for IO operations take the base streams by reference. That is:
std::ostream& operator<<( std::ostream& os , foo myfo )
{
os << myfoo;
return os;
}
So use std::ostream instead of std::ostringstream as function parameter:
void f( std::ostream& stream );
It's simply because the second expression
notWorking<<" World!"
returns a std::ostream& not a std::ostringsream&.
The reason for your error is that you’re trying to implicitly downcast an ostream into an ostringstream because the result of the << operator is always ostream& regardless of the concrete string type. Changing foo() to take an ostream& ought to solve this, unless you need to rely specifically on ostringstream features in the implementation of foo, in which case you can dynamic_cast.
Also, by constructing a temporary ostringstream in your foo(std::ostringstream() << "Hello" << " World!"); example, you obtain an rvalue. foo() expects an lvalue reference to the stream. You might be able to get away with changing foo() to take an rvalue reference: std::ostream&&.
I am really confused on why I am getting following compilation error.
Microsoft Visual Studio Compiler.
error C2678: binary '=' : no operator found which takes a left-hand operand of type 'const std::string' (or there is no acceptable conversion)
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <iterator>
class MyException {
public:
MyException( std::string message,
int line = 0) : m_message(message),
m_line(line) {}
const char* what() const throw(){
if ( m_line != 0 ) {
std::ostringstream custom_message;
custom_message << "Parsing Error occured at ";
custom_message << m_line << " Line : ";
custom_message << m_message;
m_message = custom_message.str();
}
return m_message.c_str();
}
private:
std::string m_message;
int m_line;
};
int main(int argc, char **argv) {
try {
// do something
}catch(MyException &e){
std::cout << e.what();
}
}
Error is coming at line
m_message = custom_message.str();
You declare the method as const
const char* what() const throw(){
but then you try to change the object
m_message = custom_message.str();
so you get an error.
What you should do instead is construct the custom message in the constructor.
class MyException {
public:
MyException(const std::string& message, int line = 0) :
m_message(message), m_line(line) {
if ( m_line != 0 ) {
std::ostringstream custom_message;
custom_message << "Parsing Error occured at ";
custom_message << m_line << " Line : ";
custom_message << m_message;
m_message = custom_message.str();
}
}
const char* what() const throw(){
return m_message.c_str();
}
private:
std::string m_message;
int m_line;
};
Also I changed your code to pass the std::string by reference, which is usual practice.
You are trying to assign to MyException::m_message inside a const-qualified method MyException::what(). Inside such what() the entire *this object is considered to be const, which means that m_message member is also const. You can't assign anything to a const-qualified std::string object, since std::string's assignment operator requires a modifiable (i.e. a non-const one) object on the left-hand side. You are supplying a const one.
If you really want to be able to modify the m_message inside what(), you should declare it as mutable member of the class (in this case it appears to be a good idea). Or use some other approach.
As #john noted, in your specific case it makes more sense to actually build m_message in constructor instead of postponing it till what(). I don't really understand why you'd even want to rebuild your m_message every time you call what(). Unless your m_line is expected to change somehow from one call to what() to another, there's really no need to do it every time.
In addition to the other answers;
You're not including the <string> header, which may be the cause of a problem later.
Something that used to get me a lot is that some std:: headers include others, which allows you to use a class, but maybe only with limited functionality because the std:: headers that they include are the bare minimum that is needed for that file to run. This is quite an annoyance because sometimes you declare a std:: class such as string and you haven't included the header, the definition will be fine but everything else may or may not work - leading you to a lot of debugging because the definition worked fine.
See the declaration of the what() function, it is marked const (the second const on the line). That means that it cannot alter any member variables, in your case the m_message string. That is why you get the error.
Now, how do you fix it?
Your code is wrong, your what() function will prepend the "Parsing Error occured at " etc. text each time you invoke the what() function. So, instead of doing that having to modify the m_message member, I suggest that you format the entire message in the ctor of the class:
MyException(std::string message, int line = 0)
{
if (line != 0)
{
std::ostringstream custom_message;
custom_message << "Parsing Error occured at ";
custom_message << line << " Line : ";
custom_message << message;
m_message = custom_message.str();
}
else
m_message = message;
}
I have a class that holds a reference to a stringstream (used as an overall application log). How do I add text to the referenced stringstream?
An example (as I cannot post actual source here...)
main
stringstream appLog;
RandomClass myClass;
.....
myClass.storeLog(&applog);
myClass.addText("Hello World");
cout << appLog.str().c_str() << endl;
RandomClass cpp
void RandomClass::storeLog(stringstream *appLog)
{
m_refLog = appLog;
}
void RandomClass::addText(const char text[])
{
m_refLog << text; //help here...?
}
I'm getting the following error in my real app using a very similar setup and method structure as above.
error C2296: '<<' : illegal, left operand has type 'std::stringstream *'
error C2297: '<<' : illegal, right operand has type 'const char [11]'
I know the error is because i'm using a reference and still trying to do '<<', but how else am I to do it? m_refLog-><<???
De-reference the pointer first
void RandomClass::addText(const char text[])
{
if ( m_refLog != NULL )
(*m_refLog) << text;
}
In the constructor, initialize the member pointer to stringstream with NULL
RandomClass::RandomClass() : m_refLog(NULL)
{
...
}
It looks like your m_refLog member is a StringStream * (i.e. a pointer-to-StringStream), not a StringStream (or a StringStream &. That is the source of your compile errors.
You have a pointer, not a reference. Dereference it to obtain the stream itself.
(Recall that ptr->foo() is equivalent to (*ptr).foo().)
I'd also recommend that your functions accept const std::string& instead of pointers to C-style char buffers.
And the .c_str() in your example is redundant.
void RandomClass::addText(const std::string& text) {
(*m_refLog) << text;
}