2

I have an MFC source file that I need to compile under Qt. This file uses MFC/ATL CString. Specifically it uses a CString as an argument to iostream::open(). I have written a CString class that inherits from QString so that I can use most of QStrings' functionality.

My main concern is that I cannot get my CString implementation to work where iostream::open() is called:

Here is a bit of my class declaration:

    class CString : public QString {
    public:
        CString() : QString() {}
        CString(const QString& other) : QString(other) {}
        CString(const CString& other) : QString(other) {}
        CString(_In_opt_z_ const XCHAR* pszSrc) : QString( pszSrc ) { *this = pszSrc; }
        CString(const char* pszSrc) : QString( pszSrc ) {}
        ...
    }

And, here is a portion of code to use the CString:

ofstream outfile;
CString Path("dump.txt");

outfile.open(Path);

The error is:

no matching function for call to 'std::basic_ofstream >::open(CString&)'

Under 'normal' circumstances, I would simply do something like:

outfile.open(Path.toStdString().c_str());

However, that is not an option. No modification of the original code is authorized. :(

Is there a way to do this, or am I going to have to rebuild the class using the same, rather more complex and lengthy code, that Microsoft uses in cstringt.h?

Thanks

1
  • Note that CStringis a very peculiar class, in that you can reinterpret_cast<const char*&>() it (!) You shouldn't, of course, but existing code may be abusing it. Commented Sep 6, 2011 at 22:40

6 Answers 6

5

According to this, CString has an overloaded operator LPCTSTR, which is how this works without any explicit conversions.

My guess is that if you want to emulate this behaviour, you'll need to provide a similar overload. LPCTSTR is somewhat retarded; so operator const char * is probably better.

Sign up to request clarification or add additional context in comments.

4 Comments

This is the correct answer, just be wary that this won't trigger for variadic functions.
@Mooing: That's an interesting point that I'd never considered.
My code at work uses printf all over, and I have to manually cast CStrings to pass to them. Not that your answer is wrong, it's just a caution.
You're looking at outdated docs. In the newer versions of ATL/MFC, CString is a typedef for CStringT class, which itself inherits from CSimpleStringT. This class defines operator PCXSTR, which is a lot more intuitive than operator LPCTSTR :-)
2

First of all, the correct design for your CString is to wrap QString, instead of inherit from it. After all, you're just rewiring the method calls.

class CString {
public:
    /* ... */
private:
    QString internal;
};

The compiler is complaining about ofstream::open() method not defined for taking a CString argument. This means that the CString was not being taken directly (after all, ofstream is an standard class). You could write a cast in the class so it is converted to char * when required.

class CString {
public:
    /*...*/
    operator const char *()
    {
         return internal.c_str();
    }
private:
    QString internal;
};

This will probably solve the problem. Hope this helps.

3 Comments

Why not inherit (since you don't mention destructors)? The idea of a CString is a QString. The idea of a CString does not have a QString. en.wikipedia.org/wiki/Is-a
@Mooing Duck: Composition can be preferable to inheritance in many cases. In this case because QString does not have a virtual destructor. However you could also provide a free function for conversion instead if the "string has a string" makes your eyebrows raise.
Despite being the opposite of what I was taught, I kind of like this better actually. This does prevent common errors at no real cost, doesn't it? +1
1

Define a typecast operator for your CString class as follows:

operator const char*() { return this->toStdString().c_str(); }

1 Comment

The standard refers to this as a user defined conversion operator. (There's no such thing as a "typecast" in standard lingo)
1

I have written a CString class that inherits from QString so that I can use most of QStrings' functionality

Do not do this. QString does not have a virtual destructor, and is not intended to be inherited from. In this respect, QString is no different than std::basic_string which is outlined in this other answer.

6 Comments

As always, the lack of a virtual destructor is not a blanket reason to not derive from something. If you never intend to use it in a polymorphic context, then there's no problem.
Technically speaking, a non-virtual destructor is fine as long as you never delete polymorphically. Incidentally, when you don't want to support polymorphic deletion, you should make your destructor non-virtual and protected.
@Stuart: If you make your base-class destructor protected, then you won't be able to instatiate the base-class!
@Oli: I should have mentioned that your base classes should be abstract :)
@Stuart: Don't worry, I'm comfortable with the concept that base classes should generally be pure virtual. But "never" is a strong word...
|
1

Don't attempt to shoehorn MFC code into a Qt environment. Also never inherit from objects that don't have virtual destructors. Which includes QString and most of STL such as std::string

std::ofstream is part of standard C++ and expect a c-style string const char*

Do something like this with QString directly:

ofstream outfile;
QString path("dump.txt");
outfile.open(path.toStdString().c_str());

Or use std::string directly:

ofstream outfile;
std::string path("dump.txt");
outfile.open(path.c_str());

MFC is windows dependent so using it in Qt removes the advantage of cross platform code and doesn't make much sense otherwise as the Qt framework in my opinion is superior to MFC in almost every way.

5 Comments

"Never inherit from objects that don't have virtual destructors". No. See my comment to @Billy's answer.
Technically you can but you shouldn't. Never was used to denote that both the design flaw of inheriting from something not intended to be a base class as well as the potential bugs if state is added to the derived class or if base class pointers are used polymorphically is never a good idea even if possible. Instead use composition.
@AJG85: And in the case of the kind of extensions that one might write for QString, it would be even better to just use free functions.
@AJG85: I would change that to be "never inherit from classes that have neither virtual destructors or protected destructors". Any class that doesn't have one of those two is not intended to be used as a base class.
@Billy ONeal I agree completely with both statements. In fact I mentioned the free function approach on Baltasarq's answer which for the OP's question would probably be best.
0

You should try a simple:

typedef QByteArray CString;

// And your code should work as expected
ofstream outfile;
CString Path("dump.txt");
outfile.open(Path);

QByteArray has most of QString functions, and the operator const char *.

And if you still want to write a new wrapper class around a QString, thanks to that operator you can use QString::toAscii() instead of the longer QString::toStdString()::c_str().

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.