Searched around and couldn't find any info on this besides redirecting to files so hopefully someone can help me out.
I have a console application that launches and hooks another process, by default the new process output displays in the first console application.
What I would like to do is prepend a datetime value to all the output, the problem is that I don't control the output from the child process (3rd party app) so the easy solution of adding a datetime to all printed values is unavailable. Is it possible to prepend a string to all stdout?
Since you confirmed C++ (which implies a solution for
std::cout is what is needed, and not for stdout): the
obvious solution is a filtering streambuf:
class TimeStampStreambuf : public std::streambuf
{
std::streambuf* myDest;
std::ostream* myOwner;
bool myIsAtStartOfLine;
protected:
int overflow( int ch )
{
// To allow truly empty lines, otherwise drop the
// first condition...
if ( ch != '\n' && myIsAtStartOfLine ) {
std::string tmp = now();
// function now() should return the timestamp as a string
myDest->sputn( tmp.data(), tmp.size() );
}
myIsAtStartOfLine = ch == '\n';
ch = myDest->sputc( ch );
return ch;
}
public:
TimeStampStreambuf( std::streambuf* dest )
: myDest( dest )
, myOwner( nullptr )
, myIsAtStartOfLine( false )
{
}
TimeStampStreambuf( std::ostream& owner )
: myDest( dest.rdbuf() )
, myOwner( &owner )
, myIsAtStartOfLine( false )
{
myOwner->rdbuf( this );
}
~TimeStampStreambuf()
{
if ( myOwner != nullptr ) {
myOwner->rdbuf( myDest );
}
}
};
And to install it:
// scoped:
TimeStampStreambuf anyName( std::cout );
// Time stamping will be turned off when variable goes out
// of scope.
// unscoped:
std::streambuf* savedStreambuf = std::cout.rdbuf();
TimeStampStreambuf name( savedStreambuf );
// In this case, you have to restore the original streambuf
// yourself before calling exit.
As far as I know, the articles explaining this (from C++
Reports, Sept. 1998) are not on line.
EDIT:
I've actually found them:
http://gabisoft.free.fr/articles-en.html. I can't believe that
the link still works; it's been years since I had an account
with Free.
Related
I don't understand memory management in C++ Arrow API. I use Arrow 1.0.0 and I'm reading CSV file. After a few runs of ReadArrowTableFromCSV, my memory is full of allocated data. Am I missing something? How can I free that memory? I don't see any method in memory pool to clear all allocated memory. Code Listing below.
void LoadCSVData::ReadArrowTableFromCSV( const std::string & filePath )
{
auto tableReader = CreateTableReader( filePath );
ReadArrowTableUsingReader( *tableReader );
}
std::shared_ptr<arrow::csv::TableReader> LoadCSVData::CreateTableReader( const std::string & filePath )
{
arrow::MemoryPool* pool = arrow::default_memory_pool();
auto tableReader = arrow::csv::TableReader::Make( pool, OpenCSVFile( filePath ),
*PrepareReadOptions(), *PrepareParseOptions(), *PrepareConvertOptions() );
if ( !tableReader.ok() )
{
throw BadParametersException( std::string( "CSV file reader error: " ) + tableReader.status().ToString() );
}
return *tableReader;
}
void LoadCSVData::ReadArrowTableUsingReader( arrow::csv::TableReader & reader )
{
auto table = reader.Read();
if ( !table.ok() )
{
throw BadParametersException( std::string( "CSV file reader error: " ) + table.status().ToString() );
}
this->mArrowTable = *table;
}
std::unique_ptr<arrow::csv::ParseOptions> LoadCSVData::PrepareParseOptions()
{
auto parseOptions = std::make_unique<arrow::csv::ParseOptions>( arrow::csv::ParseOptions::Defaults() );
parseOptions->delimiter = mDelimiter;
return parseOptions;
}
std::unique_ptr<arrow::csv::ReadOptions> LoadCSVData::PrepareReadOptions()
{
auto readOptions = std::make_unique<arrow::csv::ReadOptions>( arrow::csv::ReadOptions::Defaults() );
readOptions->skip_rows = mNumberOfHeaderRows;
readOptions->block_size = 1 << 27; // 128 MB
readOptions->column_names.reserve( mTable->GetNumberOfColumns() );
for ( auto & colName : mTable->GetColumnsOrder() )
{
readOptions->column_names.emplace_back( colName );
}
return readOptions;
}
std::unique_ptr<arrow::csv::ConvertOptions> LoadCSVData::PrepareConvertOptions() const
{
auto convertOptions = std::make_unique<arrow::csv::ConvertOptions>( arrow::csv::ConvertOptions::Defaults() );
for ( auto & col : mTable->GetColumsInfo() )
{
convertOptions->column_types[col.second.GetName()] = MyTypeToArrowDataType( col.second.GetType() );
}
convertOptions->strings_can_be_null = true;
return convertOptions;
}
std::shared_ptr<arrow::io::ReadableFile> LoadCSVData::OpenCSVFile( const std::string & filePath )
{
MTR_SCOPE_FUNC();
auto inputFileResult = arrow::io::ReadableFile::Open( filePath );
if ( !inputFileResult.ok() )
{
throw BadParametersException( std::string( "CSV file reader error: " ) + inputFileResult.status().ToString() );
}
return *inputFileResult;
}
Maciej, the method TableReader::Read should return shared_ptr<arrow::Table>. The arrow::Table itself has a number of shared pointers to structures which eventually contain the data. To free up the data you will need to make sure that the arrow::Table and any copies of it are destroyed. This should happen as soon as that shared_ptr goes out of scope. However, it appears you are storing the table in a member variable here (which is to be expected, you probably want to use the data after you read it):
this->mArrowTable = *table;
So now you have a second copy of the arrow::Table instance. You could reassign this->mArrowTable to a new blank table or you could destroy whatever this is. Of course, if you are making any other copies of the table then you will need to ensure those go out of scope as well.
Well guys/girls, I already asked this but I think I didn't explain good and I couldn't find the solution so I'll ask again with more details and explain more the context of my problem.
I have two classes that contain user data and I want to save them in binary files. On the other hand, I have a template class responsible for save these classes.
There is a really important fact that I have to mention: in the beginning I chose encode a auxiliary class for any class that I would save. This auxiliary class is responsible of writing/reading of data. The original classes have string members and the auxiliary classes have pointers to char. But recently, looking for more simplicity and flexibility, I chose to combine the original class, that contains the benefits of string class ;and its auxiliary that has the pointers that makes the class more comfortable at the moment of save it. So, instead of have two classes, I have one class that handles the input/output of data and the write/read of data.
This change looks something like this:
class AuxNOTE;
//Original Class: Input/Output of Data
class NOTE{
private:
string _Category;
string _Description;
public:
NOTE() : _Category( "" ) , _Description( "" ) { }
NOTE( const NOTE & note ) : _Category( note._Category )
, _Description( note._Description ) { }
NOTE( string category , string description ) : _Category( category)
, _Description( description ) { }
NOTE( const AuxNOTE & aux ) : _Category( aux._Category )
, _Description( aux._Description ) { }
NOTE & operator=( const NOTE & note ){
_Category = note._Category;
_Description = note._Description;
return *this;
}
NOTE & operator=( const AuxNOTE & aux ){
_Category = string( aux._Category );
_Description = string( aux._Description );
return *this;
}
string GetCategory() const { return _Category; }
string GetDescription() const { return _Description; }
void SetCategory( string category ) { _Category = category; }
void SetDescription( string description ) { _Description = description; }
};
//Auxliary Class: Writing/Reading of Data to/from binary files
class AuxNOTE{
private:
char _Category[50];
char _Description[255];
public:
AuxNOTE(){ }
AuxNOTE( const NOTE & note ){
strcpy( _Category , note._Category );
strcpy( _Description , note._Description);
}
AuxNOTE & operator=( const NOTE & note ){
strcpy( _Category , note._Category );
strcpy( _Description , note._Description );
return *this;
}
};
What I have now is something like this:
//Class NOTE: Input/Output of Data and Writing/Reading to/from binary files.
// .h file
class NOTE{
private:
char * _Category;
char * _Description;
public:
NOTE();
NOTE( const NOTE & note );
NOTE( string category , string description );
NOTE & operator=( const NOTE & note )
string GetCategory() const;
string GetDescription() const;
void SetCategory( string category );
void SetDescription( string description );
};
// .cpp file
#include "NOTE.h"
NOTE :: NOTE() : _Category( nullptr ) ,_Description( nullptr )
{
}
NOTE :: NOTE( string description , string category )
: _Category ( new char[ category.size() + 1 ] )
, _Categoria( new char[ description.size() + 1 ] )
{
strcpy( _Categoria , category.c_str() );
strcpy( _Descripcion , description.c_str() );
}
NOTE :: NOTE (const NOTE & copy )
: _Category( nullptr )
, _Description nullptr )
{
if( copy._Description != nullptr ){
_Description = new char[ strlen( copy._Description ) + 1 ];
strcpy( _Description , copy._Description );
}
if( copy._Category != nullptr ){
_Category = new char[ strlen( copy._Category ) + 1 ];
strcpy( _Category , copy._Category );
}
}
NOTE :: ~NOTE() {
if( _Description != nullptr ) delete [] _Description;
if( _Category != nullptr ) delete [] _Category;
}
//Get Methods
string NOTE :: GetDescription() const { return string(_Description); }
string NOTE :: GetCategory() const { return string(_Category); }
//Set Methods
void NOTE :: SetDescription( string description ){
if( _Description != nullptr ) delete [] _Description;
_Description = new char[ description.size() + 1 ];
strcpy( _Description , description.c_str() );
}
void NOTE :: SetCategory( string category ){
if( m_Category != nullptr ) delete [] _Category;
_Category = new char[ category.size() + 1 ];
strcpy( _Category , category.c_str() );
}
//Operators
NOTE & NOTE :: operator=( const NOTE & note ){
if( note._Description != nullptr ) SetDescription( note.GetDescription() );
if( note._Category != nullptr ) SetCategory( note.GetCategory() );
return *this;
}
Note that the public interface looks like if the NOTE class works with string members but it doesn't because it works with pointers to char. Thus the NOTE class can be saved without any problem. However, the class is not responsible at all of writing/reading but I created another class that can save any class as long as these classes have members that can be saved.
And the class that is responsible of this is a template class and looks like this:
template< class T >
class SAVER{
private:
vector< T > _Vector;
string _File;
public:
SAVER( string file );
~SAVER();
};
template< class T >
SAVER< T > :: SAVER( string file ) : _File( file ){
assert( _File != "" );
ifstream file( _File , ios::binary );
if( file.is_open() ){
T obj;
while( file.read( reinterpret_cast<char*>(&obj) , sizeof(obj) ) )
_Vector.push_back( obj );
}
}
template< class T >
Saver< T > :: ~Saver() {
if( _Vector.empty() )
return;
ofstream file( _File , ios::binary | ios::trunc );
assert( file.is_open() );
auto itr = _Vector.begin();
auto end = _Vector.end();
while( itr != end ){
if ( !file.write( reinterpret_cast<char*>( &itr ) , sizeof(itr) ) )
break;
itr++;
}
}
The SAVER's constructor handles the reading and puts the data( e.g NOTE objects ) in its vector. The destroyer handles the writing of all vector's objects into the corresponding binary file.
I got to clear that my errors aren't compile error but they are runtime errors.
Now, This is the problem I have:
When I execute the entire program, it has to read the binary file but it breaks. I open it with the debugger and I see that the program finishes in this line with a "segmentation fault error" and this comes from the SAVER constructor:
NOTE :: ~NOTE() {
if( _Description != nullptr ) delete [] _Description; //It breaks at this line
if( _Category != nullptr ) delete [] _Category;
}
In the debugger I can see the value of _Description and next to it appears an memory error that says: error: Cannot access memory at address (value of _Description).
Why is this happen? Do you see any error? If you need more information or you don't understand something just let me know.
First, search the internet for "c++ serialization library". What you are performing is called serialization.
Pointers and any class that contains pointers cannot be written verbatim to a file. The pointers are locations in memory. There is no guarantee by most Operating Systems that your program will have the exact memory locations next time it is executed. Your program may run in different areas of memory which change where your data is stored.
There are techniques around this, such as either writing the quantity first, then the data or writing the data then some kind of sentinel (such as the '\0' in C-Style strings).
Consider not writing as a binary file, but using formatted textual representations. A number will be read in by many platforms and converted to native representations. A native representation written in binary mode to a file, many not be the same on another platform (look up "Endianess"). Also, most text editors and word processors can easily read text files. Reading and interpreting a binary file is more difficult.
Unless your application's bottleneck is I/O bound and the I/O timing is critical, consider using textual representation of data. It is easier to read (especially when debugging your program) and easily portable.
In the debugger I can see the value of _Description and next to it appears an memory error that says: error: Cannot access memory at address (value of _Description).
Sure, you cannot deserialize pointers from your binary. You need to store their size information and contents in the file instead.
I'm a bit of a noob when it comes to programming, but as a small project I decided I'd try to make a VERY simple text-based adventure game just for some fun and practice. I have no idea how these types of games are normally made, but I decided to make a text file containing all the actual text rather than typing it all in the code, so here's what my "gamelocationdata.txt" file currently looks like.
[castleStart]
{
=castleStart=
You find yourself in a dark room inside a castle.
The walls are made of mossy stone and the entire room has a very eerie atmosphere.
There is a green bottle on the floor.
There are 2 exits, east and south.
Both exits are large doorways with grand arches.
You can see a large room through the south exit, but the east exit looks very dark and
somewhat frightening.
What will you do?
#"look arch" castleStartLookArch
#"look wall" castleStartLookWall
#"look bottle" castleStartLookBottle itemcond: hasBottle "0"
#"take bottle" castleStartLookBottle itemcond: hasBottle "0"
#"pick bottle" castleStartLookBottle itemcond: hasBottle "0"
#"go south" castleHall
#"go east" castleDark loccond: hasBeenCastleDark "0"
#"wait" castleStartWait
}
[castleStartLookArch]
{
=castleStart=
The arches above the doors look very fancy.
You can't quite figure out why this tiny room deserves to be decorated as such.
#(castleStart)
}
[castleStartLookWall]
{
=castleStart=
The wall is made of stone masonry in an old-fashioned way.
Some stones are covered in moss and there are cobwebs in the corners.
#(castleStart)
}
[castleStartWait]
{
=castleStart=
You sit still and admire the wall for a while.
Nothing happens.
#(castleStart)
}
[castleStartLookBottle]
{
=castleStart=
You pick the bottle up. It is covered in cobwebs.
The bottle is green and the label reads "1337". It contains a purple liquid.
Do you want to put the bottle in your backpack?
#"yes" castleStartTakeBottle
#"no" castleStartNoTakeBottle
}
[castleStartTakeBottle]
{
=castleStart=
You take the bottle and put it in your backpack.
+item: Bottle1337
+itemcond: hasBottle "1"
#(castleStart)
}
[castleStartNoTakeBottle]
{
=castleStart=
You put the bottle back down again.
#(castleStart)
}
[useBottle1337]
{
=curLocation=
You open the bottle and drink its contents.
It tastes very sweet.
You suddenly feel slightly stronger and more powerful.
+strength: 5
+remove_item: Bottle1337
#(castleStart)
}
[castleHall]
{
=castleHall=
You walk though the southern doorway and enter the grand hall of the castle.
It seems like the entire castle is just as old and worn out as the walls in that room,
though still very nicely decorated.
There are only a few candles on the walls, and they are somehow lit despite
the castle seeming very empty. There is not not a person to be seen.
You can go back north or proceed south through the hall.
#(castleStart)
}
[castleDark]
{
=castleStart=
You slowly tread into the dark room to the east, looking
around you as your surroundings get darker and darker.
Suddenly, you hear a noise. It sounds like the growling of an angry dog!
Horrified, you hastily turn around and run back.
+loccond: hasBeenCastleDark "1"
#(castleStart)
}
I realize I may have bitten of more than I can chew, but this is how the formatting I made up is supposed to work:
Example: [castleStart] is the name of a "location", and the curly
braces that come after encapsulate everything that has to do with that
location.
Example: =castleStart= is the location to print for the player when
they ask where they currently are.
The stuff that comes after that is what will be printed on screen when the player
"enters" that location.
After the location text, there are a bunch of options that all start
with a "#".
Example: #"wait" castleStartWait If the player types "wait", they will
be taken to the "location" named [castleStartWait].
Example: #"look bottle" castleStartLookBottle itemcond: hasBottle "0"
If the player types "look bottle", they will be taken to the location
named [castleStartLookBottle] as long as they meet the "item
requirement" that they do not already have the bottle.
Example: #"go east" castleDark loccond: hasBeenCastleDark "0" If the
player types "go east", they will be taken to the location named
[castleDark] as long as they meet the "location requirement" that they
haven't already been there.
Example: #(castleStart) This will use the same options as the ones listed in [castleStart].
Example: +strength: 5 This should add 5 points to the player's "strength" stat when they enter the location and print some hardcoded message like "You have acquired 5 strength points!"
Now, here's the problem: How do I write the function that reads and parses the data of a specific location and stores them in specific std::strings?
For example, if I do
readAndParseLocationData( castleStart );
it should look for [castleStart] in the text file, then read what's between the equals signs (=castleStart=) and store that in "std::string printLoc", then read the text after and store in a "std::string locText" and so on.
This is all the code I have so far:
//main.cpp
#include <iostream>
#include <string>
#include "ClearScreen.h"
#include "LocationData.h"
int main()
{
ClearScreen();
std::cout << "I am a banana!\n\n"; // this is just a test
readAndParseLocationData( "castleHall" );
printLocationData( "castleStart" ); // this is supposed to be used for debugging the location data by printing it.
return 0;
}
--
//LocationData.cpp
#include "LocationData.h"
#include <fstream>
#include <iostream>
#include <string>
void readAndParseLocationData( std::string location )
{
location.insert( 0,"[" );
location.append( "]" );
std::ifstream locfile( "gamelocationdata.txt" );
if( locfile.is_open() )
{
std::string line;
bool foundFile = false;
for( unsigned int curLine = 0; getline( locfile,line ); curLine++ )
{
if( line.find( location ) != std::string::npos )
{
std::cout << "found: " << location << ", line: " << curLine << "\n";
foundFile = true;
}
}
if( !foundFile )
{
std::cout << "\nERROR: Location " << location << " not found in data file!\n";
}
locfile.close();
}
else
{
std::cout << "\nERROR: Unable to open location data file!\n";
}
}
void printLocationData( std::string location )
{
//TODO: Implement
}
All I've managed to make it do (through extensive googling) is look for the location name and print what line it's on to the console.
I'm using Visual Studio Express 2013 on Windows 7.
I'd also love to hear if there is any way to improve my code or formatting in general!
It seems that what you want to do is parse the file just-in-time as you come across the locations you wish to access. This is a good idea if you expect your game to become too large to feasibly parse once and store in memory, but for a small example like this, it's probably unnecessary.
However, if you wish to implement this for learning purposes, there are a few things to look into. First of all, your current idea would involve re-parsing the file every time you want to look at the some location. A better idea would be to devise some kind of representation for the location data and implement a cache of some kind. A possible way to do this (only taking the names and location text into account) would be:
class Location
{
private:
std::string name;
std::string displayName;
std::string locationText;
}
std::unordered_map<std::string, Location> cache;
Location& readAndParseLocationData( std::string location )
{
//if we have already parsed this location
if (cache.count(location))
{
return cache[location];
}
else
{
Location parsed_location{};
//do the parsing
cache[location] = parsed_location;
}
}
In order to actually do the parsing, I would write a simple recursive descent parser. For your case, it would look something like this (in pseudocode):
Until we have found the location:
Look for a location
If this location matches:
Read the location into a string
Look for a opening brace
Read the location display name into a string
Read the rest into a string up until a closing brace
For future readers who happen to stumble upon this 2 year old question, here's the final code I ended up using to parse the location data.
(It's old and not pretty by any means, but note that the solution to my main problem was learning about std::getline and the various manipulation methods of std::string - mainly find() and substr().)
struct Location final
{
std::string displayName;
std::string optionsName;
std::string locationText;
std::string defaultOption;
std::string shop;
std::vector<int> battleLevel;
std::vector<int> battleChanceLevel;
std::vector<int> battleChancePercentage;
std::vector<std::string> battleEnemy;
std::vector<std::string> battleChanceEnemy;
std::vector<std::string> addItems;
std::vector<std::string> addWeapons;
std::vector<std::string> addConds;
std::vector<std::string> addStats;
std::vector<std::string> removeItems;
std::vector<std::string> removeWeapons;
std::vector<std::string> removeConds;
std::vector<std::string> removeStats;
std::unordered_set<std::string> options;
std::unordered_map<std::string, std::string> resultLoc;
std::unordered_map<std::string, std::string> conds;
};
std::unordered_map<std::string, Location> locationCache;
std::unordered_map<std::string, std::string> locationNewFileCache;
Location& loadLocationData( const std::string& location, const std::string& filename, const bool overRideData, const bool dataFile )
{
if( ::locationCache.count( location ) && overRideData == false ) // If we already parsed this location...
return ::locationCache[ location ]; // Return cached data.
else
{
auto filePath = std::string( "game/data/" );
if( !dataFile )
filePath.append( "locations/" );
std::ifstream locFile( filePath + filename );
if( locFile.is_open() )
{
bool foundLoc = false;
for( std::string line; std::getline(locFile, line); )
{
Location parsedLocation;
if( line.find( "[" + location + "]" ) != std::string::npos )
{
foundLoc = true;
// Parse optionsname/next filename.
std::string optsName; std::getline( locFile, optsName );
if( optsName.find( ".txt" ) != std::string::npos )
::locationNewFileCache[ location ] = optsName;
else
parsedLocation.optionsName = optsName;
// Parse display name.
std::string dispName; std::getline( locFile, dispName );
parsedLocation.displayName = dispName;
// Parse location text.
std::string locText;
for( std::string scanLine; ( (scanLine.empty()) ? true : scanLine.back() != '}' ) && std::getline( locFile, scanLine ); )
{
if( !scanLine.empty() && scanLine.front() == '{' )
locText = scanLine;
else
locText.append( "\n" + scanLine );
}
if( locText.size() >= 2 )
{
locText.erase( locText.begin() ); // Remove { at beginning.
locText.pop_back(); // Remove } at end.
}
parsedLocation.locationText = locText;
// Parse rest.
bool endReached = false;
for( std::string scanLine; !endReached && std::getline( locFile, scanLine ); )
{
if( !scanLine.empty() )
{
switch( scanLine.front() )
{
case '*': endReached = true; break; // END
case '#': // OPTION / DEFAULT OPTION
if( scanLine.at( 1 ) == '"' ) // OPTION
{
scanLine.erase( 0, 2 );
auto optionName = scanLine.substr( 0, scanLine.find( '\"' ) );
parsedLocation.options.insert( optionName );
scanLine.erase( 0, scanLine.find( '\"' ) + 2 );
auto optionResultLoc = scanLine.substr( 0, scanLine.find( ';' ) );
parsedLocation.resultLoc[ optionName ] = optionResultLoc;
scanLine.erase( 0, scanLine.find( ';' ) + 1 );
if( !scanLine.empty() ) // if the option has conditions...
{
auto condType = scanLine.substr( 0, scanLine.find( ':' ) );
scanLine.erase( 0, scanLine.find( ':' ) + 2 );
if( condType == "loccond" || condType == "itemcond" || condType == "statcond" )
parsedLocation.conds[ optionName ] = scanLine;
else
logError( "Invalid location data syntax in " + filename + "!", "Invalid condition" );
}
}
else if( scanLine.at( 1 ) == '(' ) // DEFAULT OPTION
{
scanLine.erase( 0, 2 );
parsedLocation.defaultOption = scanLine.substr( 0, scanLine.find( ')' ) );
}
else
logError( "Invalid location data syntax in " + filename + "!", "Neither option nor default option specified" );
break;
case '+': // ACTION (ITEM / STAT / CONDITION)
scanLine.erase( scanLine.begin() );
auto action = scanLine.substr( 0, scanLine.find( ':' ) );
scanLine.erase( 0, scanLine.find( ':' ) + 2 );
auto value = scanLine;
if( action == "item" )
parsedLocation.addItems.push_back( value );
else if( action == "remove_item" )
parsedLocation.removeItems.push_back( value );
else if( action == "weapon" )
parsedLocation.addWeapons.push_back( value );
else if( action == "remove_weapon" )
parsedLocation.removeWeapons.push_back( value );
else if( action == "itemcond" || action == "loccond" )
parsedLocation.addConds.push_back( value );
else if( action == "battle" )
{
auto enemyName = value.substr( 0, value.find( ' ' ) );
value.erase( 0, value.find( ' ' ) + 1 );
auto nEnemies = std::stoi( value.substr( 0, value.find( ',' ) ) );
value.erase( 0, value.find( ',' ) + 1 );
auto level = std::stoi( value );
for( auto i = 0; i < nEnemies; ++i )
{
parsedLocation.battleEnemy.push_back( enemyName );
parsedLocation.battleLevel.push_back( level );
}
}
else if( action == "battlechance" )
{
auto enemyName = value.substr( 0, value.find( ' ' ) );
value.erase( 0, value.find( ' ' ) + 1 );
auto chance = std::stoi( value.substr( 0, value.find( ',' ) ) );
value.erase( 0, value.find( ',' ) + 1 );
auto level = std::stoi( value );
parsedLocation.battleChanceEnemy.push_back( enemyName );
parsedLocation.battleChancePercentage.push_back( chance );
parsedLocation.battleChanceLevel.push_back( level );
}
else if( action == "shop" )
parsedLocation.shop = value;
else
parsedLocation.addStats.push_back( action + " " + value ); // Assume it's a stat.
break;
}
}
}
::locationCache[ location ] = parsedLocation;
return ::locationCache[ location ];
}
}
}
else
logError( "Unable to open location data file " + filename + "!" );
}
static Location dummyLocation;
return dummyLocation;
}
I have the following code, when I run the code below I get 'R6025 run-time error in Visual C++'
CommandParameterAndValue param( "Key", "value" );
parameters.AddParameter( ¶m );
parameters.HasParameter( "akeyval" );
I am lost, any ideas? Is it something to do with the casting?
typedef std::vector<iCommandParameter *> ParamsVectorList;
class CommandParametersList
{
public:
.... functions here ....
void AddParameter( iCommandParameter *param );
bool HasParameter( std::string parameterKey );
protected:
ParamsVectorList m_parameters;
};
void CommandParametersList::AddParameter( iCommandParameter *param )
{
m_parameters.push_back( param );
}
bool CommandParametersList::HasParameter( std::string parameterKey )
{
ParamsVectorList::iterator it;
CommandParameterAndValue *paramItem = NULL;
bool returnValue = false;
for ( it = m_parameters.begin(); it != m_parameters.end(); it++ )
{
paramItem = static_cast<CommandParameterAndValue *>( *it );
if ( paramItem->GetKey().compare( parameterKey ) == 0 )
{
returnValue = true;
break;
}
}
return returnValue;
}
I need more information to give a complete answer, but if you look here: http://support.microsoft.com/kb/125749
That run-time error means you tried to call a pure virtual function - it couldn't find an implementation. I would suggest running through a debugger and finding which line of code throws this error. Than it should be easy to understand and fix. It's probably happening here:
if ( paramItem->GetKey().compare( parameterKey ) == 0 )
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.