C++ STL Set: Cannot find() last element inserted - c++

I am in the process of writing an application in which I use the Set class in the C++ STL. I've discovered that the call to set->find() always seems to fail when I query for the last element I inserted. However, if I iterate over the set, I am able to see the element I was originally querying for.
To try to get a grasp on what is going wrong, I've created a sample application that exhibits the same behavior that I am seeing. My test code is posted below.
For the actual application itself, I need to store pointers to objects in the set. Is this what is causing the weird behavior. Or is there an operator I need to overload in the class I am storing the pointer of?
Any help would be appreciated.
#include <stdio.h>
#include <set>
using namespace std;
#define MySet set<FileInfo *,bool(*)(const FileInfo *, const FileInfo*)>
class FileInfo
{
public:
FileInfo()
{
m_fileName = 0;
}
FileInfo( const FileInfo & file )
{
setFile( file.getFile() );
}
~FileInfo()
{
if( m_fileName )
{
delete m_fileName;
m_fileName = 0;
}
}
void setFile( const char * file )
{
if( m_fileName )
{
delete m_fileName;
}
m_fileName = new char[ strlen( file ) + 1 ];
strcpy( m_fileName, file );
}
const char * getFile() const
{
return m_fileName;
}
private:
char * m_fileName;
};
bool fileinfo_comparator( const FileInfo * f1, const FileInfo* f2 )
{
if( f1 && ! f2 ) return -1;
if( !f1 && f2 ) return 1;
if( !f1 && !f2 ) return 0;
return strcmp( f1->getFile(), f2->getFile() );
}
void find( MySet *s, FileInfo * value )
{
MySet::iterator iter = s->find( value );
if( iter != s->end() )
{
printf( "Found File[%s] at Item[%p]\n", (*iter)->getFile(), *iter );
}
else
{
printf( "No Item found for File[%s]\n", value->getFile() );
}
}
int main()
{
MySet *theSet = new MySet(fileinfo_comparator);
FileInfo * profile = new FileInfo();
FileInfo * shell = new FileInfo();
FileInfo * mail = new FileInfo();
profile->setFile( "/export/home/lm/profile" );
shell->setFile( "/export/home/lm/shell" );
mail->setFile( "/export/home/lm/mail" );
theSet->insert( profile );
theSet->insert( shell );
theSet->insert( mail );
find( theSet, profile );
FileInfo * newProfile = new FileInfo( *profile );
find( theSet, newProfile );
FileInfo * newMail = new FileInfo( *mail );
find( theSet, newMail );
printf( "\nDisplaying Contents of Set:\n" );
for( MySet::iterator iter = theSet->begin();
iter != theSet->end(); ++iter )
{
printf( "Item [%p] - File [%s]\n", *iter, (*iter)->getFile() );
}
}
The Output I get from this is:
Found File[/export/home/lm/profile] at Item[2d458]
Found File[/export/home/lm/profile] at Item[2d458]
No Item found for File[/export/home/lm/mail]
Displaying Contents of Set:
Item [2d478] - File [/export/home/lm/mail]
Item [2d468] - File [/export/home/lm/shell]
Item [2d458] - File [/export/home/lm/profile]
**Edit
It's kind of sad that I have to add this. But as I mentioned before, this is a sample application that was pulled from different parts of a larger application to exhibit the failure I was receiving.
It is meant as a unit test for calling set::find on a set populated with heap allocated pointers. If you have a problem with all the new()s, I'm open to suggestions on how to magically populate a set with heap allocated pointers without using them. Otherwise commenting on "too many new() calls" will just make you look silly.
Please focus on the actual problem that was occurring (which is now solved). Thanks.
***Edit
Perhaps I should have put these in my original question. But I was hoping there would be more focus on the problem with the find() (or as it turns out fileinfo_comparator function that acts more like strcmp than less), then a code review of a copy-paste PoC unit test.
Here are some points about the code in the full application itself.
FileInfo holds a lot of data along with the filename. It holds SHA1 sums, file size, mod time, system state at last edit, among other things. I have cut out must of it's code for this post. It violates the Rule of 3 in this form (Thanks #Martin York. See comments for wiki link).
The use of char* over std::string was originally chosen because of the use of 3rd_party APIs that accept char*. The app has since evolved from then. Changing this is not an option.
The data inside FileInfo is polled from a named pipe on the system and is stored in a Singleton for access across many threads. (I would have scope issues if I didn't allocate on heap)
I chose to store pointers in the Set because the FileInfo objects are large and constantly being added/removed from the Set. I decided pointers would be better than always copying large structures into the Set.
The if statement in my destructor is needless and a left over artifact from debugging of an issue I was tracking down. It should be pulled out because it is unneeded.

Your comparison function is wrong - it returns bool, not integer as strcmp(3). The return statement should be something like:
return strcmp( f1->getFile(), f2->getFile() ) < 0;
Take a look here.
Also, out of curiosity, why not just use std::set<std::string> instead? STL actually has decent defaults and frees you from a lot of manual memory management.

It looks to me like your FileInfo doesn't work correctly (at least for use in a std::set). To be stored in a std::set, the comparison function should return a bool indicating that the two parameters are in order (true) or out of order (false).
Given what your FileInfo does (badly designed imitation of std::string), you'd probably be better off without it completely. As far as I can see, you can use std::string in its place without any loss of functionality. You're also using a lot of dynamic allocation for no good reason (and leaking a lot of what you allocate).
#include <set>
#include <iostream>
#include <iterator>
#include <string>
int main() {
char *inputs[] = { "/export/home/lm/profile", "/export/home/lm/shell", "/export/home/lm/mail" };
char *outputs[] = {"Found: ", "Could **not** find: "};
std::set<std::string> MySet(inputs, inputs+3);
for (int i=0; i<3; i++)
std::cout
<< outputs[MySet.find(inputs[i]) == MySet.end()]
<< inputs[i] << "\n";
std::copy(MySet.begin(), MySet.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
Edit: even when (or really, especially when) FileInfo is more complex, it shouldn't attempt to re-implement string functionality on its own. It should still use an std::string for the file name, and implement an operator< that works with that:
class FileInfo {
std::string filename;
public:
// ...
bool operator<(FileInfo const &other) const {
return filename < other.filename;
}
FileInfo(char const *name) : filename(name) {}
};
std::ostream &operator(std::ostream &os, FileInfo const &fi) {
return os << fi.filename;
}
int main() {
// std::set<std::string> MySet(inputs, inputs+3);
std:set<FileInfo> MySet(inputs, inputs+3);
// ...
std::copy(MySet.begin(), MySet.end(),
std::ostream_iterator<FileInfo>(std::cout, "\n"));
}

In your constructor:
FileInfo( const FileInfo & file )
{
setFile( file.getFile() );
}
m_fileName seems to be not initialized.

Related

Copy only necessary objects from PDF file

I've got a huge PDF file with more than 100 pages and I want to separate them to single PDF files (containing only one page each). Problem is, that PoDoFo does not copy just the page, but the whole document because of the references (and so each of the 100 PDF files have same size as the 100-page PDF). A relevant mailing list post can be found, unfortunately there is no solution provided.
In source code of function InsertPages there is explanation:
This function works a bit different than one might expect.
Rather than copying one page at a time - we copy the ENTIRE document
and then delete the pages we aren't interested in.
We do this because
1) SIGNIFICANTLY simplifies the process
2) Guarantees that shared objects aren't copied multiple times
3) offers MUCH faster performance for the common cases
HOWEVER: because PoDoFo doesn't currently do any sort of "object
garbage collection" during a Write() - we will end up with larger
documents, since the data from unused pages will also be in there.
I have tried few methods to copy only relevant objects, but each of them failed.
Copy all pages and remove irrelevant ones
Use XObject wrapping: FillXObjectFromDocumentPage and FillXObjectFromExistingPage
Copy object by object
Use RenumberObjects with bDoGarbageCollection = true
but none of them worked out. Does anybody have an idea or working solution for this problem?
The only solution is to use another PDF library. Or wait for garbage collection to be implemented.
The problem is stated in the quote you mentioned:
> during a Write() - we will end up with larger documents, since the
> data from unused pages will also be in there.
This means podofo always puts the entire PDF content in your file, no matter what. The whole PDF is there, you just don't see parts of it.
Dennis from the podofo support sent me an working example of optimized version of InsertPages function which is actually fixing page references and decreases document size significantly!
void PdfMemDocument::InsertPages2(const PdfMemDocument & rDoc, std::vector<int> pageNumbers)
{
std::unordered_set<PdfObject*> totalSet;
std::vector<pdf_objnum> oldObjNumPages;
std::unordered_map<pdf_objnum, pdf_objnum> oldObjNumToNewObjNum;
std::vector<PdfObject*> newPageObjects;
// Collect all dependencies from all pages that are to be copied
for (int i = 0; i < pageNumbers.size(); ++i) {
PdfPage* page = rDoc.GetPage(pageNumbers[i]);
if (page) {
oldObjNumPages.push_back(page->GetObject()->Reference().ObjectNumber());
std::unordered_set<PdfObject*> *set = page->GetPageDependencies();
totalSet.insert(set->begin(), set->end());
delete set;
}
}
// Create a new page object for every copied page from the old document
// Copy all objects the pages depend on to the new document
for (auto it = totalSet.begin(); it != totalSet.end(); ++it) {
unsigned int length = static_cast<unsigned int>(GetObjects().GetSize() + GetObjects().GetFreeObjects().size());
PdfReference ref(static_cast<unsigned int>(length+1), 0);
PdfObject* pObj = new PdfObject(ref, *(*it));
pObj->SetOwner(&(GetObjects()));
if ((*it)->HasStream()) {
PdfStream *stream = (*it)->GetStream();
pdf_long length;
char* buf;
stream->GetCopy(&buf, &length);
PdfMemoryInputStream inputStream(buf, length);
pObj->GetStream()->SetRawData(&inputStream, length);
free(buf);
}
oldObjNumToNewObjNum.insert(std::pair<pdf_objnum, pdf_objnum>((*it)->Reference().ObjectNumber(), length+1));
GetObjects().push_back(pObj);
newPageObjects.push_back(pObj);
}
// In all copied objects, fix the object numbers so they are valid in the new document
for (auto it = newPageObjects.begin(); it != newPageObjects.end(); ++it) {
FixPageReferences(GetObjects(), *it, oldObjNumToNewObjNum);
}
// Insert the copied pages into the pages tree
for (auto it = oldObjNumPages.begin(); it != oldObjNumPages.end(); ++it) {
PdfObject* pageObject = GetObjects().GetObject(PdfReference(oldObjNumToNewObjNum[(*it)], 0));
PdfPage *page = new PdfPage(pageObject, std::deque<PdfObject*>());
GetPagesTree()->InsertPage(GetPageCount() - 1, page);
}
}
std::unordered_set<PdfObject *>* PdfPage::GetPageDependencies() const
{
std::unordered_set<PdfObject *> *set = new std::unordered_set<PdfObject *>();
const PdfObject* pageObj = GetObject();
if (pageObj) {
PdfVecObjects* objects = pageObj->GetOwner();
if (objects) {
set->insert((PdfObject*)pageObj);
objects->GetObjectDependencies2(pageObj, *set);
}
}
return set;
}
// Optimized version of PdfVecObjects::GetObjectDependencies
void PdfVecObjects::GetObjectDependencies2(const PdfObject* pObj, std::unordered_set<PdfObject*> &refMap) const
{
// Check objects referenced from this object
if (pObj->IsReference())
{
PdfObject* referencedObject = GetObject(pObj->GetReference());
if (referencedObject != NULL && refMap.count(referencedObject) < 1) {
(refMap).insert((PdfObject *)referencedObject); // Insert referenced object
GetObjectDependencies2((const PdfObject*)referencedObject, refMap);
}
}
else {
// Recursion
if (pObj->IsArray())
{
PdfArray::const_iterator itArray = pObj->GetArray().begin();
while (itArray != pObj->GetArray().end())
{
GetObjectDependencies2(&(*itArray), refMap);
++itArray;
}
}
else if (pObj->IsDictionary())
{
TCIKeyMap itKeys = pObj->GetDictionary().GetKeys().begin();
while (itKeys != pObj->GetDictionary().GetKeys().end())
{
if ((*itKeys).first != PdfName("Parent")) {
GetObjectDependencies2((*itKeys).second, refMap);
}
++itKeys;
}
}
}
}
void FixPageReferences(PdfVecObjects& objects, PdfObject* pObject, std::unordered_map<pdf_objnum, pdf_objnum>& oldNumToNewNum) {
if( !pObject)
{
PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
}
if( pObject->IsDictionary() )
{
TKeyMap::iterator it = pObject->GetDictionary().GetKeys().begin();
while( it != pObject->GetDictionary().GetKeys().end() )
{
if ((*it).first != PdfName("Parent")) {
FixPageReferences(objects, (*it).second, oldNumToNewNum);
}
++it;
}
}
else if( pObject->IsArray() )
{
PdfArray::iterator it = pObject->GetArray().begin();
while( it != pObject->GetArray().end() )
{
FixPageReferences(objects, &(*it), oldNumToNewNum),
++it;
}
}
else if( pObject->IsReference() )
{
//PdfObject* referencedObj = objects.GetObject(pObject->GetReference());
pdf_objnum oldnum = pObject->GetReference().ObjectNumber();
pdf_objnum newnum = oldNumToNewNum[oldnum];
if (!newnum) throw new std::exception("No new object number for old object number");
*pObject = PdfReference(newnum, 0);
}
}

Vector with custom objects: crash when I want to use one received with .at method

I have a class named CConfig, I'm creating new object:
std::vector< CConfig > docs;
CConfig newfile( "somefile.xml", "root" );
printf("%s", newfile.GetTagValue( "servername" )); // this works
docs.push_back( newfile );
When I'm getting this object with .at method
CConfig file = docs.at(0);
printf("%s", file.GetTagValue( "servername" )); // this crashes
Where's the problem?
(im sorry if formatting is wrong but currently I don't use javascript because my bandwidth is ended and max speed is 1kb/s so I'll try to fix it later)
CConfig.h:
class CConfig
{
TiXmlDocument m_doc;
TiXmlElement* m_pRoot;
bool m_bIsLoaded;
public:
CConfig ( void ) {};
CConfig ( const char * pszFileName, const char * pszRootName );
~CConfig ( void ) {};
const char* GetTagValue ( const char * pszTagName );
const char* GetTagAttribute ( const char * pszTagName, const char * pszAttributeName );
TiXmlElement* GetRootElement ( void ) { return m_pRoot; };
bool IsAvailable ( void ) { return m_bIsLoaded; };
};
CConfig.cpp
#include "CConfig.h"
CConfig::CConfig( const char * pszFileName, const char * pszRootName )
{
m_bIsLoaded = m_doc.LoadFile( pszFileName );
if( m_bIsLoaded )
m_pRoot = m_doc.FirstChildElement( pszRootName );
}
const char * CConfig::GetTagValue( const char * pszTagName )
{
if( m_bIsLoaded && m_pRoot )
{
TiXmlElement * element = m_pRoot->FirstChildElement( pszTagName );
if( element )
return element->GetText();
}
}
const char * CConfig::GetTagAttribute( const char * pszTagName, const char * pszAttributeName )
{
if( m_bIsLoaded && m_pRoot )
{
TiXmlElement * element = m_pRoot->FirstChildElement( pszTagName );
if( element )
return element->Attribute( pszAttributeName );
}
}
I'm using tinyxml
Your issue is with pointers to old memory. When you add an item to an array, it is copied. Later you leave that scope and the original is destroyed, but ask yourself where the pointer in your copy is pointing? Still to the first (now deleted) object's memory. Uh-oh.
The simplest fix (while avoiding large copy operations) is to make m_doc into a shared pointer (available in the standard in C++11, or via Boost in C++03). That will then handle everything for you rule-of-3 wise. And because the underlying memory won't move, m_pRoot will remain valid until the last copy has been deleted.
If copy-space is not an issue, then fix your Rule of Three violation by properly adding a copy constructor:
CConfig(const CConfig& obj)
: m_doc(obj.m_doc)
, m_bLoaded(obj.m_bLoaded)
, m_pRoot()
{
if (m_bLoaded)
m_pRoot = m_doc.GetRootElement();
}
An assignment operator is also likely in order, but if you don't need it, hide it by declaring it (but not implementing it) as private or use the C++11 delete attribute feature.
Interestingly enough, you don't even need the m_bLoaded member. A non-NULL root pointer can indicate your loaded-state, but that is a separate issue. This at least be enough to get you up and running.

Seg fault unique_ptr with factory design attempt

I'm getting a segmentation fault when trying to use a unique_ptr to create instances of derived classes.
Before, I had coded every instantiation of the seven derived classes, one after the other and the code was working properly.
Current code is the following:
typedef std::unique_ptr<Comum> ComumPtr;
ComumPtr createInstance ( string dom, map<string, string> & config, map<string, string> & config_fields )
{
ComumPtr ptr; // initialized to nullptr.
if ( dom == "voice" ) {
ptr.reset ( new Voice (config, config_fields) );
// } else if ( dom == "account" ) { // FOR OTHER DERIVED CLASSES
// ptr.reset ( new Account (config, config_fields) );
}
return ptr;
}
// At main function:
for (vector<string>::const_iterator cit = for_domain.begin(); cit != for_domain.end(); ++cit) {
const char * section (cit->c_str());
string fsn = *cit + "_fields";
const char * fields_section_name (fsn.c_str());
const char * db_section ("Database");
map <string, string> domain_config = cfg.getSectionConfig (config_file.c_str(), section);
map <string, string> domain_config_fields = cfg.getSectionConfig (config_file.c_str(), fields_section_name);
map <string, string> database_config = cfg.getSectionConfig (config_file.c_str(), db_section);
std::unique_ptr<Comum> domain = createInstance(*cit, domain_config, domain_config_fields);
domain->readDatabaseFields (database_config); // <- segmentation fault
Do you see any reason for this to seg fault?
function createInstance has the chance to return nullptr, you need to check the pointer is valid:
if (domain.get())
{
domain->readDatabaseFields (database_config);
}
This line is the error:
ComumPtr ptr; // initialized to nullptr.
While I understand that nullity is so easy, it's also the best way to shoot yourself in the foot (whether in C++ or Java), because now any single use of the result of this function need be checked.
Instead, you could:
use a Null object: the method readDatabaseFields will just do nothing
choose to throw an exception rather than returning a null pointer
None of the above alternative is inherently better than the other, it very much depends on the situation; however both are better than returning a null unique_ptr.
Supposing you'd elect the exception method:
ComumPtr createInstance ( string dom, map<string, string> & config, map<string, string> & config_fields )
{
if ( dom == "voice" ) {
return ComumPtr ( new Voice (config, config_fields) );
}
if ( dom == "account" ) {
return ComumPtr ( new Account (config, config_fields) );
}
throw std::runtime_error("Unknown config field");
}

What is the difference between these?

Can anyone please explain me the difference about the below used methods to insert a new object in the map container? I already know about pointers and such, I'm not really deep into virtual memory, only the basics (addresses etc..)
#include "StdAfx.h"
#include <windows.h>
#include <cstdlib>
#include <iostream>
#include <map>
using namespace std;
class CUser
{
public:
CUser() { Init(); };
~CUser() {};
public:
BOOL m_bActive;
BOOL m_bLoggedIn;
SYSTEMTIME m_sysTime;
void Init();
};
void CUser::Init()
{
(*this).m_bActive = FALSE;
m_bLoggedIn = FALSE;
GetSystemTime( &m_sysTime );
}
int main(int argc, char *argv[])
{
map<DWORD, CUser*>mUserMap;
//what is the difference between this
{
CUser pUser;
pUser.m_bActive = FALSE;
pUser.m_bLoggedIn = FALSE;
GetSystemTime( &pUser.m_sysTime );
mUserMap.insert( make_pair( 351, &pUser ) );
}
//and this?
{
CUser *pUser = new CUser;
if( pUser )
{
pUser->m_bActive = TRUE;
pUser->m_bLoggedIn = TRUE;
GetSystemTime( &pUser->m_sysTime );
mUserMap.insert( make_pair( 351, pUser ) );
}
}
/* map<DWORD, CUser*>::iterator it = mUserMap.find( 351 );
if( it == mUserMap.end() )
std::cout << "Not found" << std::endl;
else
{
CUser *pUser = it->second;
if( pUser )
std::cout << pUser->m_sysTime.wHour << std::endl;
} */
return 0;
}
In the first case, pUser is created on the stack, and will automatically be deleted when its name goes out of scope (i.e. at the next closing curly bracket). Generally speaking, it's unwise to insert pointers to stack objects into containers, because the object will cease to exist while the container still have a value pointing to it. This can cause a crash in the best case. In the worst case, it could cause erratic and hard to locate bugs in distant parts of the code.
//what is the difference between this
{
CUser pUser;
pUser.m_bActive = FALSE;
pUser.m_bLoggedIn = FALSE;
GetSystemTime( &pUser.m_sysTime );
mUserMap.insert( make_pair( 351, &pUser ) );
}
this creates a local object: your pUser variable only exists inside the scope of this block, and ceases to exist when the last } is reached. That means its destructor is called, and the memory it lived in is reclaimed and may be reused.
Now, when you store a pointer to this short-lived object in your map, you're storing a problem. If you de-reference that pointer at any time after the closing } of this block, you're invoking undefined behaviour. It may work. It may work sometimes, and then start to fail. Basically, it's a logical error and a good source of unpredictable bugs.
//and this?
{
CUser *pUser = new CUser;
if( pUser )
{
pUser->m_bActive = TRUE;
pUser->m_bLoggedIn = TRUE;
GetSystemTime( &pUser->m_sysTime );
mUserMap.insert( make_pair( 351, pUser ) );
}
}
here you explicitly create an instance which will outlive the enclosing scope, and all is good. You don't need to check if new returns NULL though: it'll throw an exception unless you explicitly request it not to.
The difference is here is that the object created by the call to new is created on the heap and not the stack. This means that once the pointer goes out of scope, the memory allocated is still in existence on the heap and you can safely reference it through the pointer stored in your map.
In the first case, you create an object on the stack and add its address to the map. This means that when your locally created variable goes out of scope it is destroyed and the pointer in your map now points to a variable that is no longer in existence. This will undoubtedly lead to problems in your code.
Use the first approach if you must use pointers and not actual objects themselves.
When you use new the memory will persist until you delete it (or get another object to take care of it like a shared pointer). Stack objects are destroyed as soon as they go out of scope.
{
CUser pUser;
pUser.m_bActive = FALSE;
pUser.m_bLoggedIn = FALSE;
GetSystemTime( &pUser.m_sysTime );
mUserMap.insert( make_pair( 351, &pUser ) );
}
//pUser is not available here
pUser (Object) unavailable (deleted), pointer in mUserMap is invalid!
{
CUser *pUser = new CUser;
if( pUser )
{
pUser->m_bActive = TRUE;
pUser->m_bLoggedIn = TRUE;
GetSystemTime( &pUser->m_sysTime );
mUserMap.insert( make_pair( 351, pUser ) );
}
}
//pUser is not available here
pUser (Pointer!!) unavailable (deleted), memory is still claimed so pointer in mUserMapis valid!

Problem with realloc implementation resulting in Access violation

I have a custom string class that malloc/realloc/free's internally; For certain strings appending works fine, but on certain others it will always fail (small or large allocations). The same code worked fine on a different project, although it was an ANSI build.
I believe I'm implementing this correctly, but most likely I've overlooked something. The error occurs when I attempt to utilize the "szLog" buffer once the log has been opened. This simply contains the path to the program files directory (40 characters total). Using this same buffer without the "Log file '" prefix works fine, so it's an issue with the realloc section. And yes, the log does open properly.
I get 0xC0000005: Access violation reading location 0x00660063. only when realloc is used (but as previously stated, it doesn't always fail - in this situation, when szLog is input - but other variable strings/buffers do it too).
HeapReAlloc is the failing function inside realloc.c, errno being 22.
I've stripped the comments to try and keep the post as small as possible! Any help would be much appreciated.
gData.szLogStr is a UString, IsNull is a definition for "x == NULL" and unichar is simply a typedef for wchar_t
class UString : public Object
{
private:
unichar* mpsz;
unichar* mpszPrev;
UINT muiAlloc;
UINT muiLen;
public:
... other functions ...
UString& operator << (const unichar* pszAdd)
{
if ( IsNull(pszAdd) )
return (*this);
if ( IsNull(mpsz) )
{
muiAlloc = ((str_length(pszAdd)+1) * sizeof(unichar));
if ( IsNull((mpsz = static_cast<unichar*>(malloc(muiAlloc)))) )
{
SETLASTERROR(ERR_NOT_ENOUGH_MEMORY);
muiAlloc = 0;
return (*this);
}
mpszPrev = mpsz;
muiLen = str_copy(mpsz, pszAdd, muiAlloc);
}
else
{
UINT uiNewAlloc = (muiAlloc + (str_length(pszAdd) * sizeof(unichar)));
if ( muiAlloc < uiNewAlloc )
{
uiNewAlloc *= 2;
/* Fails */
if ( IsNull((mpsz = static_cast<unichar*>(realloc(mpsz, uiNewAlloc)))) )
{
SETLASTERROR(ERR_NOT_ENOUGH_MEMORY);
mpsz = mpszPrev;
return (*this);
}
mpszPrev = mpsz;
muiAlloc = uiNewAlloc;
}
muiLen = str_append(mpsz, pszAdd, muiAlloc);
}
return (*this);
}
and this being called from within main via:
UString szConf;
unichar szLog[MAX_LEN_GENERIC];
szConf << ppszCmdline[0];
szConf.replace(_T(".exe"), _T(".cfg"));
if ( GetPrivateProfileString(_T("Application"), _T("LogFile"), NULL, szLog, sizeofbuf(szLog), szConf.str()) == 0 )
{
UINT uiLen = str_copy(szLog, szConf.str(), sizeofbuf(szLog));
szLog[uiLen-3] = 'l';
szLog[uiLen-2] = 'o';
szLog[uiLen-1] = 'g';
}
if ( ApplicationLog::Instance().Open(szLog, CREATE_ALWAYS) )
{
gData.szLogStr.clear();
/* Erroring call */
gData.szLogStr << _T("Log file '") << szLog << _T("' opened");
APP_LOG(LL_WriteAlways, NULL, gData.szLogStr);
ObjMgr::Instance().DumpObjects(LogDumpedObjects);
}
You are programming in c++, so use new and delete. To 'renew', allocate a new memory area large enough to hold the new string, initialize it with the correct values and then delete the old string.
What is str_length() returning when it fails? I'd trace the value of muiAlloc and see what you're actually trying to allocate. It may not be a sane number.
Are you certain that whatever's in szLog is null-terminated, and that the buffer has room for whatever you're copying into it? It's not clear if str_copy is safe or not. It may be a wrapper around strncpy(), which does not guarantee a null terminator, but some people mistakenly assume it does.