|
I think I've come up with a version of this class that will compile under PocketPC 2K2.
------- signature starts
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
Please review the Legal Disclaimer in my bio.
------- signature ends
|
|
|
|
|
I get garbage text when i concate long lines of text.
|
|
|
|
|
Hi folks!
I use the class in a Tokenizer class I've written, and (after some modifications proposed in a previous thread) it works fine. But why is it called CCOMString ( I mean the "COM" )? I don't know that much about COM, but normally you have a reference counter, which is increased, if a) the COM object is constructed or b) the object is copied or used by another object. I don't find that concept in CCOMString class, so I'm a bit curious what the name means.
Best regards
Andreas Haferburg
|
|
|
|
|
I don't want to kill the idea (well, actually I do), but why not using WTL strings with VC 6.0? And in VS.Net MFC and ATL share common CString implementation.
VC framework is already full of string implementations (CString, CComBSTR, _bstr_t, STL strings), so I don't see the point of adding yet another one.
Vagif Abilov
MCP (Visual C++)
Oslo, Norway
|
|
|
|
|
Back when we started our latest project, there just didn't exist a solid string class that met our needs. We needed to be able to use UNICODE and MBCS strings in the same build. That killed CString, CComBSTR, and _bstr_t. Then the problems with STLPort and bugs in VC6 strings made STL string unusable. Not to mention that STL strings don't support mixed string styles well. The support them, but just don't mix as well as we needed.
Thus we created our own.
If we started the project today, we would use the ATL strings since they seem to be not only full featured, but can mix W and A strings in the same application easily.
So, there are valid reasons to re-invent implementations. We had to re-write STL's hash_map since it made implementation decisions that relegated it to a limited set of problems. We improved performance by 4 fold with our implementation that was specifically tailored for our needs.
Just because there exists a solution in a problem set doesn't mean that solution fully covers the problem set.
Tim Smith
"Programmers are always surrounded by complexity; we can not avoid it... If our basic tool, the language in which we design and code our programs, is also complicated, the language itself becomes part of the problem rather that part of the solution."
Hoare - 1980 ACM Turing Award Lecture
|
|
|
|
|
OK, that explains it. I thought the code was new. But since it's probably older than first WTL implementation, you had all reasons to come up with this solution. Great code. I was just curious about practical aspects.
Best regards and thanks for sharing your code.
Vagif Abilov
MCP (Visual C++)
Oslo, Norway
|
|
|
|
|
I spent a lot of time trying to debug it and in vain. Even destructor don't frees allocated memory!
|
|
|
|
|
Make these changes:
1) CCOMString::~CCOMString(){
if (m_bAllocated){
free (m_pszString);
};
}
2) Remove the function and header for ReAllocString
3) Change all other occurences of ReAllocString to AllocString
4) Change AllocString:
void CCOMString::AllocString(int nLen){
ATLASSERT(nLen >= 0);
if (m_bAllocated){
m_pszString = (TCHAR*) realloc(m_pszString, (nLen+1) * sizeof(TCHAR));
}else{
m_pszString = (TCHAR*) malloc((nLen+1) * sizeof(TCHAR));
}
m_bAllocated=true;
m_pszString[nLen] = '\0';
}
5) Add m_bAllocated=false; to the top of every constructor
6) Add bool m_bAllocated; to the protected section of the header
7) Have yourself a great coding day with a great string class that includes perl style concatenation
|
|
|
|
|
I changed a bit in demo application to test leaks in CCOMString. After the loop i don't see the memory is released all. I think we still have leaks in CCOMString class.
void main()
long i = 0;
CCOMString zTotal;
while (TRUE)
{
CCOMString zTest;
zTest+=_T("1234567890");
zTest+=_T("QWERASDFZXCVTYUIFGHJVNBMO[P]K;'M.,/");
zTest+=_T("qwertyuiop[]\asdfghjkl;'zxcvbnm,./");
zTest+=_T("~!@#$%^&*()_+");
zTotal+=zTest;
i++;
zTest.Empty();
if (i == 10000)
{
zTotal.Empty();
break;
}
Sleep(1000);
}
Any suggestion?
|
|
|
|
|
is there a method or anyone has done similar, to compare a string with another string consists of wild card characters?
e.g.
"house" is equal to "hou*"
cheers,
cym
|
|
|
|
|
there is more general solution:
try search this server for "regular expresion"s
t!
|
|
|
|
|
|
The constructors for the CCOMString class are like this:
CCOMString::CCOMString(CCOMString& str)
{
m_pszString = NULL;
int nLen = str.GetLength();
AllocString(nLen);
_tcsncpy(m_pszString, (LPCTSTR) str, nLen);
}
A better way of programming could be like this, because
there is no similar or duplicate code for the same purpose:
CCOMString::CCOMString(CCOMString& str)
{
m_pszString = NULL;
operator=(str);
}
Kindest regards
Holger Persch
|
|
|
|
|
Have a look to the Format function:
void CCOMString::Format(LPCTSTR pszCharSet, ...)
{
va_list vl;
va_start(vl, pszCharSet);
TCHAR* pszTemp = NULL;
int nBufferSize = 0;
int nRetVal = -1;
do {
nBufferSize += 100;
delete [] pszTemp;
pszTemp = new TCHAR[nBufferSize];
nRetVal = _vsntprintf(pszTemp, nBufferSize, pszCharSet, vl);
} while (nRetVal < 0);
int nNewLen = _tcslen(pszTemp);
AllocString(nNewLen);
_tcscpy(m_pszString, pszTemp);
delete [] pszTemp;
va_end(vl);
}
If the formatted string length is equal to nBufferSize _vsntprintf will not
nullterminate the string. Because of this, _tcslen returns a wrong string
length and the string contains unexpected charaters after string copy.
A correct Format function could be look like this:
void CCOMString::Format(LPCTSTR pszCharSet, ...)
{
va_list vl;
va_start(vl, pszCharSet);
TCHAR* pszTemp = NULL;
int nBufferSize = 0;
int nRetVal = -1;
do {
nBufferSize += 100;
delete [] pszTemp;
pszTemp = new TCHAR[nBufferSize];
nRetVal = _vsntprintf(pszTemp, nBufferSize, pszCharSet, vl);
} while (nRetVal < 0);
AllocString(nRetVal);
_tcsncpy(m_pszString, pszTemp, nRetVal);
delete [] pszTemp;
va_end(vl);
}
Kindest regards
Holger Persch
|
|
|
|
|
as you know, my application program "without use MFC".
it is made by skineditor. so i want to press "shiftkey + NUM3 key" to display "#" and "shiftkey + NUM8 key" to display "*".
how can i do for it?
thanks your reply.
None
|
|
|
|
|
There are a couple of minor problems I would like to point out, but first let me say that I think this is a very good class
1. First there's a typo in the Format() function which prevents you from using the class in UNICODE builds. You called the _vsnprintf() function. However this function is made for chars, not TCHARS. To work with UNICODE, you should be calling the TCHAR-based version -- _vsntprintf() -- note the added 't'. Easy fix.
2. Next problem is that your class does not seem to protect itself from NULLs quite like MFC's CString does. With CString you can pass in NULLs anywhere you want (constructor, assignment operator, etc) and it has no problems. You might want to protect yourself from these. On this same issue, MFC's CString ensures that it's member string pointer always points to a valid string. They declare a global static empty string for this purpose. You might want to do the same.
3. Next problem with this class is that it introduces a dependency on the CRT. That somewhat goes against the spirit of ATL which allows you to to non-CRT builds. However that's really a minor quibble. I know how difficult it is to write a CRT-free string class. Fact is, I use ATL all time and I always use the CRT
4. Next quibble, you made the destructor virtual. I know that it's generally the thing one is always told to do when creating new classes, but in this case, it's probably not a good thing in the case of this class unless you are planning on having people derive from it. It changes the binary layout of the class so that people can't pass CCOMString's as arguments to variadic functions like Format() without explicitly casting them.
5. Next issue is a question really. I see in a lot of places you did stuff like this:
#ifdef _UNICODE
ATLASSERT(::IsBadStringPtrW((LPCTSTR) str, -1) == 0);
#else
ATLASSERT(::IsBadStringPtrA((LPCTSTR) str, -1) == 0);
#endif
You don't need all the #ifdef-s here. All you need to do is use the #defined name of the function which already did it for you. Like this
ATLASSERT(::IsBadStringPtr((LPCTSTR) str, -1) == 0);
That might cut down on your code clutter a bit.
All in all, still a good job!
Joe O'Leary
|
|
|
|
|
You could also use WTL's version of CString. And the nice thing is that Nenad (from Microsoft) is just about to release a new version soon that is compatible with VS.Net and includes fixes as well. Not sure about the other functionality, but the CString is pretty good.
Steve Maier, MCSD
|
|
|
|
|
WTL CString is OK but it doesn't alloc memory in chunk. If you code like this, you will die:
CString strTemp;
for (int i=0; i<1000000; i++) strTemp += _T('*');
WTL String allocs & frees memory 1000000 times and you have no way to "reserve" memory.
In MFC CString, it's better in the Release built because it allocs memory in chunk (64, 256, etc.)
|
|
|
|
|
True, but how many times do you really stress a class like that in a real pogram? There are alot of things especially in the STL that is included with VC6 that cannot handle being stressed like that, but work just fine for more reasonable usage. COM and MFC have had their problems too.
Of course if you knew you were going to write code like you have there, most programmers that I know would have allocated the entire buffer at once. This can be done with GetBuffer(1000000) in WTL. The same goes for WTL, STL, MFC, or char*. With allocating it out first, the following code only takes about 1 sec at most to run instead of the 400+ of the loop that you wrote.
CString strTemp2;
strTemp2.GetBuffer(1000000);
for (int i=0; i<1000000; i++)
strTemp2 += _T('*');
Every system has its pluses and minuses. I will be one of the first to say that WTL still can use a few things and is not for every project, but I think that Nenad has done a good job with it overall. It just takes some knowledge in how you are going to use your tool and how that tool works to get the best performance possible. The nice thing about WTL is that you get all of the source to see what is happening.
Plus you could always either derive a new class and change how things are allocated or just directly change your CString. Either way it up to the developer to use the tool in a smart way.
Steve Maier, MCSD
|
|
|
|
|
Yes, strTemp2.GetBuffer(1000000) does allocate 1000000 buffer bytes of memory. But in many places you do not know how big is your string in advance.
My solution is not very good, but it work: I modified the BOOL CString::AllocBuffer(int nLen) function so that it can allocate 64, 128, 256, etc like MFC CString. Because AllocBuffer is not a virtual function, I may have to write a lot of code if I derive a new class from CString.
|
|
|
|
|
Why spending time to create an own made CString variant? Because you have to include the MFC42 file with your application file? You don't have to, just link you app with the static variant of MFC. And those who think that the entire MFC with be added statically to the application: wrong, just the necessary code.
|
|
|
|
|
The format method does not properly deal with long output strings. Changing _vstprintf to _vsntprintf and passing in the current size fixes the problem. A obvious oversight given the code logic supports arbitrary output sizes, but it certainly confirms the need to test completely before use...
|
|
|
|
|
If you do decide to use exception handling, please use Win32 SEH. Win32 SEH is platform independent and is the only exception handling mechanism supported by Windows CE.
|
|
|
|
|
Unfortunately, SEH does not correctly call destructors during the stack unwind. So, if you're using smart pointers in a SEH guarded block, you will have a resource leak.
|
|
|
|
|
Currently under CE we have not experienced any problems with SEH. In fact, it has worked remarkably well and fits will into our architecture which is embedded in nature.
Demir Ateser
Intermec Technologies Corp.
|
|
|
|