views:

1658

answers:

3

For a school assignment I have to implement a project in C++ using Borland C++ Builder.

As the VCL uses AnsiString for all GUI Components I have to convert all of my std::strings to AnsiString for the sake of displaying.

std::string inp = "Hello world!";
AnsiString outp(inp.c_str());

works of course but is a bit tedious to write and code duplication I want to avoid. As we use Boost in other contexts I decided to provide some helper functions go get boost::lexical_cast to work with AnsiString. Here is my implementation so far:

std::istream& operator>>(std::istream& istr, AnsiString& str) {
    istr.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit);
    std::string s;
    std::getline(istr,s);
    str = AnsiString(s.c_str());
    return istr;
}

In the beginning I got Access Violation after Access Violation but since I added the .exceptions() stuff the picture gets clearer. When the conversion is performed I get the following Exception:

ios_base::eofbit set [Runtime Error/std::ios_base::failure]

Does anyone have an idea how to fix it and can explain why the error occurs? My C++ experience is very limited.

The conversion routine the other way round would be:

std::ostream& operator<<(std::ostream& ostr,const AnsiString& str) {
    ostr << (str.c_str());
    return ostr;
}

Maybe someone will spot an error here too :)

With best regards!

Edit:

At the moment I'm using the edited version of Jem, it works in the beginning. After a while of using the programm the Borland Codeguard mentions some pointer arithmetic in already freed regions. Any ideas how this could be related?

The Codeguard log (I'm using the german version, translations marked with stars):

------------------------------------------
Fehler 00080. 0x104230 (r) (Thread 0x07A4):
Zeigerarithmetik in freigegebenem Speicher: 0x0241A238-0x0241A258. **(pointer arithmetic in freed region)**
| d:\program files\borland\bds\4.0\include\dinkumware\sstream Zeile 126:
|      { // not first growth, adjust pointers
|      _Seekhigh = _Seekhigh - _Mysb::eback() + _Ptr;
|>     _Mysb::setp(_Mysb::pbase() - _Mysb::eback() + _Ptr,
|       _Mysb::pptr() - _Mysb::eback() + _Ptr, _Ptr + _Newsize);
|      if (_Mystate & _Noread)
Aufrufhierarchie: **(stack-trace)**
   0x00411731(=FOSChampion.exe:0x01:010731) d:\program files\borland\bds\4.0\include\dinkumware\sstream#126
   0x00411183(=FOSChampion.exe:0x01:010183) d:\program files\borland\bds\4.0\include\dinkumware\streambuf#465
   0x0040933D(=FOSChampion.exe:0x01:00833D) d:\program files\borland\bds\4.0\include\dinkumware\streambuf#151
   0x00405988(=FOSChampion.exe:0x01:004988) d:\program files\borland\bds\4.0\include\dinkumware\ostream#679
   0x00405759(=FOSChampion.exe:0x01:004759) D:\Projekte\Schule\foschamp\src\Server\Ansistringkonverter.h#31
   0x004080C9(=FOSChampion.exe:0x01:0070C9) D:\Projekte\Schule\foschamp\lib\boost_1_34_1\boost/lexical_cast.hpp#151

Objekt (0x0241A238) [Größe: 32 Byte] war erstellt mit new **(Object was created with new)**
| d:\program files\borland\bds\4.0\include\dinkumware\xmemory Zeile 28:
|   _Ty _FARQ *_Allocate(_SIZT _Count, _Ty _FARQ *)
|   { // allocate storage for _Count elements of type _Ty
|>  return ((_Ty _FARQ *)::operator new(_Count * sizeof (_Ty)));
|   }
| 
Aufrufhierarchie: **(stack-trace)**
   0x0040ED90(=FOSChampion.exe:0x01:00DD90) d:\program files\borland\bds\4.0\include\dinkumware\xmemory#28
   0x0040E194(=FOSChampion.exe:0x01:00D194) d:\program files\borland\bds\4.0\include\dinkumware\xmemory#143
   0x004115CF(=FOSChampion.exe:0x01:0105CF) d:\program files\borland\bds\4.0\include\dinkumware\sstream#105
   0x00411183(=FOSChampion.exe:0x01:010183) d:\program files\borland\bds\4.0\include\dinkumware\streambuf#465
   0x0040933D(=FOSChampion.exe:0x01:00833D) d:\program files\borland\bds\4.0\include\dinkumware\streambuf#151
   0x00405988(=FOSChampion.exe:0x01:004988) d:\program files\borland\bds\4.0\include\dinkumware\ostream#679

Objekt (0x0241A238) war Gelöscht mit delete **(Object was deleted with delete)**
| d:\program files\borland\bds\4.0\include\dinkumware\xmemory Zeile 138:
|   void deallocate(pointer _Ptr, size_type)
|    { // deallocate object at _Ptr, ignore size
|>   ::operator delete(_Ptr);
|    }
| 
Aufrufhierarchie: **(stack-trace)**
   0x004044C6(=FOSChampion.exe:0x01:0034C6) d:\program files\borland\bds\4.0\include\dinkumware\xmemory#138
   0x00411628(=FOSChampion.exe:0x01:010628) d:\program files\borland\bds\4.0\include\dinkumware\sstream#111
   0x00411183(=FOSChampion.exe:0x01:010183) d:\program files\borland\bds\4.0\include\dinkumware\streambuf#465
   0x0040933D(=FOSChampion.exe:0x01:00833D) d:\program files\borland\bds\4.0\include\dinkumware\streambuf#151
   0x00405988(=FOSChampion.exe:0x01:004988) d:\program files\borland\bds\4.0\include\dinkumware\ostream#679
   0x00405759(=FOSChampion.exe:0x01:004759) D:\Projekte\Schule\foschamp\src\Server\Ansistringkonverter.h#31

------------------------------------------

Ansistringkonverter.h is the file with the posted operators and line 31 is:

std::ostream& operator<<(std::ostream& ostr,const AnsiString& str) {
    ostr << (str.c_str()); **(31)**
    return ostr;
}

Thanks for your help :)

A: 

Your conversion converts one line of the input. That's a serious problem if there are two lines, and a rather fatal bug if there are none. The best solution in this case is not a new operator>>, but a template specialization. Top of my head:

template< > AnsiString lexical_cast<AnsiString, std::string>(std::string const& s)
{
    return AnsiString(s.c_str());
}

(You're not supposed to overload other peoples templates. In std::, it's illegal, elsewhere just bad practice)

MSalters
@James: Of course you can specialize template functions - only partial specialization is not possible for them.
Georg Fritzsche
+1  A: 

Still not using boost, but may be a first step to solve your current problem. You may try this:

std::istream& operator>>(std::istream& istr, AnsiString& str) {
    istr.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit);
    std::string s;
    istr >> s;
    str = AnsiString(s.c_str());
    return istr;
}

EDIT: more complete solution, taking op's comments into account:

std::istream& operator>> (std::istream& istr, AnsiString& str) 
{ 
    std::string tmp;

    std::istreambuf_iterator<char> it(istr), end; 
    std::copy(it, end, std::inserter(tmp, tmp.begin())); 

    str = AnsiString(tmp.c_str());

    return istr; 
}

However, having different operator >> behavior for std::string and AnsiString is probably ok for your needs, but not very nice in general. You can still give it an explicit name.

Jem
I think that will only copy a single word
James Hopkin
Yes, but it may be the expected behaviour (or not). Reading the entire buffer could be done at an upper level.I do not know streams by heart, but it is probably possible here to even avoid the temporary string, by using stream.get()
Jem
I switched from >> to getline() some time ago because I want to read the entire string. So reading the whole string is what I want :)
David Klein
If I put the istr.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit); call into your edited suggestion it crashes with "ios_base::failbit set", strange :)
David Klein
I think that it is normal. As you read everything in the stream, by definition you reach the end of it, and an exception is thrown. If you do not catch it (using try / catch), it causes the crash you're experiencing (a unhandled exception error ?).With my example, you should either not set the exceptions bits, or catch exceptions but only report those which are actually errors.
Jem
A: 

In addition to MSalters comments, It would be more efficient to pass the std::string's actual length to AnsiString as well, so that it does not need waste CPU cycles to calculating the length manually:

template<> System::AnsiString lexical_cast<System::AnsiString, std::string>(std::string const& s)
{
    return System::AnsiString(s.c_str(), s.length());
}
Remy Lebeau - TeamB