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

E.Freitas
  • 542
  • 1
  • 4
  • 18
  • Note that `CString`is a very peculiar class, in that you can `reinterpret_cast()` it (!) You shouldn't, of course, but existing code may be abusing it. – MSalters Sep 06 '11 at 22:40

6 Answers6

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.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • 3
    This is the correct answer, just be wary that this won't trigger for variadic functions. – Mooing Duck Sep 06 '11 at 21:11
  • @Mooing: That's an interesting point that I'd never considered. – Oliver Charlesworth Sep 06 '11 at 21:12
  • 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. – Mooing Duck Sep 06 '11 at 21:13
  • You're looking at outdated docs. In the [newer versions](http://msdn.microsoft.com/en-us/library/5bzxfsea%28v=VS.100%29.aspx) 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` :-) – Praetorian Sep 06 '11 at 21:15
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.

Baltasarq
  • 12,014
  • 3
  • 38
  • 57
  • 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. http://en.wikipedia.org/wiki/Is-a – Mooing Duck Sep 06 '11 at 21:16
  • 1
    @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. – AJG85 Sep 06 '11 at 21:20
  • 1
    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 – Mooing Duck Sep 06 '11 at 21:34
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.

Community
  • 1
  • 1
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • 1
    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. – Oliver Charlesworth Sep 06 '11 at 20:58
  • 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 Golodetz Sep 06 '11 at 21:00
  • @Stuart: If you make your base-class destructor protected, then you won't be able to instatiate the base-class! – Oliver Charlesworth Sep 06 '11 at 21:14
  • @Oli: I should have mentioned that your base classes should be abstract :) – Stuart Golodetz Sep 06 '11 at 21:16
  • @Stuart: Don't worry, I'm comfortable with the concept that base classes should generally be pure virtual. But "never" is a strong word... – Oliver Charlesworth Sep 06 '11 at 21:19
  • @Oli: If you're not using things in a polymorphic context, then you should not be using public inheritance. See the full explanation in the linked answer. Making the base destructor protected is one way to enforce this kind of a contract, but `QString`'s destructor is not `protected` either. – Billy ONeal Sep 06 '11 at 21:49
1

Define a typecast operator for your CString class as follows:

operator const char*() { return this->toStdString().c_str(); }
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • The standard refers to this as a user defined conversion operator. (There's no such thing as a "typecast" in standard lingo) – Billy ONeal Sep 06 '11 at 21:52
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.

AJG85
  • 15,849
  • 13
  • 42
  • 50
  • 1
    "Never inherit from objects that don't have virtual destructors". No. See my comment to @Billy's answer. – Oliver Charlesworth Sep 06 '11 at 21:04
  • 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 Sep 06 '11 at 21:09
  • 1
    @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. – Billy ONeal Sep 06 '11 at 21:50
  • 1
    @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 Sep 06 '11 at 21:51
  • @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. – AJG85 Sep 06 '11 at 22:59
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().

alexisdm
  • 29,448
  • 6
  • 64
  • 99