I know this can look like a rookie question already asked a thousand time. But I searched for the exact answer and I haven't found one...
I'm working on a code that, to sum up, fill an XML with different data.
I'm trying to optimize a part of it. The "naïve" code is the following:
xml << "<Node>";
for(auto& input : object.m_vec)
{
if(input == "Something")
{
xml << input;
}
}
xml << "</Node>";
for(auto& input : object.m_vec)
{
if(input == "SomethingElse")
{
xml << "<OtherNode>";
xml << input;
xml << "</OtherNode>";
break;
}
}
The important thing is, while more than one input fit in <Node></Node>, only one fit in <OtherNode></OtherNode> (explaining the break;) and it may not exist either (explaining the xml << in-between the if statement).
I think I could optimize it such like:
std::vector<std::string>* VecPointer;
xml << "<Node>";
for(auto& input : object.m_vec)
{
if(input == "Something")
{
xml << input;
}
else if(input == "SomethingElse")
{
VecPointer = &input;
}
}
xml << "</Node>";
if(!VecPointer->empty())
{
xml << "<OtherNode>"
<< *VecPointer
<< "</OtherNode>";
}
The point for me here is that there is no extra memory needed and no extra loop. But the pointer to the local variable bothers me. With my beginner's eyes I can't see a case where it can lead to something wrong.
Is this okay? Why? Do you see a better way to do it?
You need to make sure your compairson also looks for an existing value within the VecPointer, since your original second loop only cares about the first value it comes across.
else if(VecPointer && input == "SomethingElse")
Don't look for ->empty(), as that's accessing the pointer and asking whether the pointed to vector is empty. If there's nothing to point to in the first place, you're going to have a bad time at the -> stage of the statement. Instead, if against it, since it's a pointer.
if(VecPointer)
Finally, you're using a Vector to save that one value from m_vec, which from other code I'm assuming is not a vector<vector<string>> but a vector<string> - in the latter case, your VecPointer should be std::string*
std::string* VecPointer = nullptr;
I'm trying to optimize a part of it.
...
Is this okay?
Maybe not! This may already be a poor use of your time. Are you sure that this is what's hurting your performance? Or that there's a performance problem at all?
Remember Don Knuth's old adage: Premature optimization is the root of all evil...
Do you see a better way to do it?
Consider profiling your program to see which parts actually take up the most time.
On an unrelated note, you could use standard library algorithms to simplify your (unoptimized) code. For example:
if (std::ranges::find(std::begin(object.m_vec) std::end(object.m_vec), "SomethingElse"s )
!= std::end(object.m_vec))
{
xml << "<OtherNode>" << whatever << "</OtherNode>";
}
Related
I've recently begun using flags to handle an input loop's validity condition so it can be checked elsewhere inside the loop rather than having to redo the same check multiple times. However, I'm unsure how best to assign the flag. Is there a generally standard practice regarding this, or just personal style? What, differences are there in the compiled code, if any?
For example, instead of the following code:
bool isValidSize;
do {
std::cout << "Enter the font size (8-12): ";
std::cin >> fontSize;
if (fontSize >= MIN_FONT_SIZE && fontSize <= MAX_FONT_SIZE) {
isValidSize = true;
} else {
isValidSize = false;
std::cout << "Invalid size. ";
}
} while (!isValidSize);
the if-statement can be changed to make it more clear what isValidSize is set to at a glance:
isValidSize = (fontSize >= MIN_FONT_SIZE && fontSize <= MAX_FONT_SIZE);
if (!isValidSize) {
std::cout << "Invalid size. ";
}
Would this be compiled as an extra if-check? Is there any portability benefit to having the assignment separate from anything else? From just looking at the code, it seems the benefit of the first way is possibly only one branch but an additional assignment per rep and also has an else?
There are no differences: proof.
Tested on GCC 6.3 with optimisations (-O3).
Go for what you think is the more readable one.
This is where I'm having the problem. I need to remove a object from the array once the creature dies.
for each(BaseObject monster in MonsterList)
{
if(MonsterSelect == monster.GetName())
{
Damage = Player.GetATK() - monster.GetDEF();
monster.SetHP(monster.GetHP() - Damage);
cout << monster.GetName() << " has taken " << Damage << "." << endl;
if(monster.GetAliveFlag() == false)
{
cout << monster.GetName() << " has died." << endl;
MonsterList.remove(monster);//This is where the object needs to be removed.
int sdf = 234;
}
}
}
You should use an ordinary for loop with iterators and apply method erase for the list.
Something like this
for ( auto it = MonsterList.begin(); it != MonsterList.end(); )
{
//...
if( MonsterSelect == it->GetName())
{
//,,,
if( it->GetAliveFlag() == false)
{
it = MonsterList.erase( it );
//...
}
else
{
++it;
}
}
else
{
++it;
}
}
You can reformat the loop that it would be more readable.
I suppose that each time when the current monster satisfies some condition you have to remove only this one monster in the list.
As for method remove then it removes all elements of the list that are equal to the given monster.
I'm going to interpret this as asking how you'd accomplish this basic task in decently written C++.
First, I'd get rid of the for loop. Second, I'd move most of the logic into one class or the other. Right now, these look like quasi-classes to me--i.e., all the real logic is in separate code operating on a couple of what are basically dumb data objects, disguised as classes by using accessors to get at the raw data.
So, if you were doing this in actual C++, you might end up with a map (or multimap) to let you find monsters by name. So, you'd have something like this:
auto monster = monsters.find(monsterSelect);
if (monster == monsters.end())
// Not found
if (monster.kill(player.attack())) {
cout << monsterSelect << " has died.\n";
monsters.erase(monster);
}
For the moment, I'm assuming there's really only a single monster by any one name. If this might really be an attack against multiple monsters, you have a couple of choices.
The preferred one (at least IMO) is to decouple the "battle the monster" part from the "remove dead monster(s) from the list" part. I'd probably use a Boost filter-iterator to iterate across all the monsters by a given name, then the remove/erase idiom to remove all the dead monsters from the list. (Note, however, that the remove/erase idiom is specific to sequential collections, and not suitable for associative containers).
I've been having an issue with a game I've been making in my C++ game programming class for school. For some reason, after calling a function which I'm using to manage the inventory based stuff, the function seems to complete and work (I think this because I put in cout commands at the end of it and they printed correctly, also the function runs twice in a row, and they both run), my entire game crashes and doesn't reach the next line. I tried commenting out all the code in the function and it still crashed. I commented out the function calls and it worked, but I still can't tell what is wrong with it. I'll put the code for the function and the section were I make the calls:
string inventoryFunction(int h, string ab)
{
if(h == 1)
inventory.push_back(ab);
else
if(h == 2)
{
for(int i=0; i < inventory.size(); i++)
{
if(inventory[i] == ab)
inventory[i].erase();
}
}
else
if(h == 3)
{
cout << inventory[0];
for(int i=1; i < inventory.size(); i++)
cout << ", " << inventory[i];
}
}
The function call:
if(answer.find("village") != string::npos)
{
cout << endl;
cout << "While looking around your village,\nyou found a stone sword and a cracked wooden shield!" << endl;
inventoryFunction(1, "stone sword");
inventoryFunction(1, "cracked wooden shield");
cout << "Would you like to set off on your adventure now?" << endl;
cin >> answer2;
capitalizeLower(answer2);
Not sure there's anything there likely to cause a crash, my advice would be to single-step your code in the debugger to see where it's falling over. It's quite possible the bug is somewhere totally different and it's just being exacerbated by the function calls modifying the vector.
That's the nature of bugs unfortunately, you can never really tell where they're actually coming from without looking closely :-)
However, there are a couple of issues with the code that I'd like to point out.
First, with regard to:
inventory[i].erase();
That doesn't do what you think it does. inventory[i] is the string inside your vector so it's simply erasing the string contents.
If you want to remove the string from the vector, you need something like:
inventory.erase (inventory.begin() + i);
Second, I'd tend to have three separate functions for addToInventory, removeFromInventory and listInventory.
It seems a little ... unintuitive ... to have to remember the magic values for h to achieve what you want to do, and there's no real commonality in the three use cases other than access to the inventory vector (and that's not really reason enough to combine them into the same member function).
On top of that, your function appears to be returning a string but you have no actual return statements and, in fact, none of the three use cases of your function require anything to be passed back.
The signature is better off as:
void inventoryFunction(int h, string ab)
In terms of the second and third points above, I'd probably start with something like:
void addToInventory (string item) {
inventory.push_back(ab);
}
void removeFromInventory (string item) {
for (int i = 0; i < inventory.size(); i++) {
if (inventory[i] == ab) {
inventory.erase (inventory.begin() + i);
break;
}
}
void listInventory () {
cout << inventory[0];
for (int i = 1; i < inventory.size(); i++)
cout << ", " << inventory[i];
}
You may also want to look into using iterators exclusively for the second and third functions rather than manually iterating over the collection with i.
It'll save you some code and be more "C++ic", a C++ version of the "Pythonic" concept, a meme that I hope will catch on and make me famous :-)
So by changing the inventoryFunction to a void function like #Retired Ninja said, the crash has stopped occurring and now the program is working great.
Also, #paxdiablo pointed out that I was using the inventory[i].erase() thing incorrectly, so thanks a bunch to him, because now I won't have to come back on here later to try to fix that :D
string inventoryFunction(int h, string ab)
should return a string but does not have any return statements. Of course it works, after you change it to a void function, which correctly does not return anything. Interesting is, that you are able co compile this code without an error - normally a compiler would show you this problem.
I am very new to C++.
How I can "redirect" code to certain position?
Basically, what should I put instead of comments lines here:
if ( N>1 ) {
// What should be here to make the code start from the beginning?
}
else {
// What should be here to make the code start from certain point?
}
I understand that C++ is not scripting language, but in case the code is written as script, how I can make redirect it?
Thank you
A goto command will do what you want but it's generally frowned on in polite circles :-)
It has its place but you would be possibly better off learning structured programming techniques since the overuse of goto tends to lead to what we call spaghetti code, hard to understand, follow and debug.
If your mandate is to make minimal changes to code which sounds like it may already be badly written, goto may be the best solution:
try_again:
n = try_something();
if (n > 1)
goto try_again;
With structured programming, you would have something like:
n = try_something();
while (n > 1)
n = try_something();
You may not see much of a difference between those two cases but that's because it's simple. If you end up with your labels and goto statements widely separated, or forty-two different labels, you'll beg for the structured version.
Use functions, loops etc to control the "flow" of your application. Think about code as reusable pieces, anything that is going to be reused should be placed in a function or looped through.
Here is an example:
void main()
{
int i = 0;
SayHello();
if (i < 1)
{
SayHello();
i++;
}
else
{
SayGoodbye();
}
}
void SayHello()
{
cout << "Hello" << endl;
}
void SayGoodbye()
{
cout << "Goodbye" << endl;
}
I'm not entirely certain what you mean by "redirect", but consider the following:
if (N > 1) {
speak();
} else {
do_something_else();
}
as paxdiablo has already stated the goto method isn't good practice. It would be better to use functions that do a specific thing, this way debugging is easier and someone can actually follow what your code is doing (or at least what it is supposed to do).
I have a program which should read commands from the console and depending on the command perform one of several actions. Here is what I have so far:
void ConwayView::listening_commands() {
string command;
do {
cin >> command;
if ("tick" == command)
{
// to do
}
else if ("start" == command)
{
// to do for start
}
...
} while (EXIT != command);
}
Using a switch in place of the if statements helps a little if there are a large amount of commands. What patterns do you suggest using to provide the interactive command line?
There are multiple ways to solve this and it's debatable what the "right" solution is. If I were to solve it for my own work, I would create a table of a custom struct. Something like:
struct CommandStruct {
char *command;
int (*commandHandler)(/*params*/);
} commandTable[] = {
{ "tick", tickCommand },
{ "start", startCommand },
...
};
Then my processing loop would walk through each element of this table, looking for the right match, such as:
for (int i = 0; i < TABLE_SIZE; ++i) {
if (command == commandTable[i].command) { /* using whatever proper comparison is, of course */
commandTable[i].commandHandler(/*params*/);
break;
}
}
Not really a pattern, but often a good approach:
#include <map>
#include <functional>
#include <string>
#include <iostream>
typedef std::map< std::string, std::function<void(void)> > command_dict;
// ^^^^^^^^
// the signature of your commands. probably should have an error code.
void command1() { std::cout << "commanda" << std::endl; }
void command2() { std::cout << "commandb" << std::endl; }
void command3() { std::cout << "commandc" << std::endl; }
int main() {
command_dict c;
c["a"] = &command1;
c["b"] = &command2;
c["c"] = &command3;
std::string input;
while(std::getline(std::cin, input)) { // quit the program with ctrl-d
auto it = c.find(input);
if(it != end(c)) {
(it->second)(); // execute the command
} else {
std::cout << "command \"" << input << "\" not known" << std::endl;
}
}
}
If the number of command is small and possible parameters are really few, you could keep on with switch case !
If the number of commands increases, consider the command design pattern (which is IMHO some sort of strategy pattern disguised: cf Using a strategy pattern and a command pattern for the differences between command and strategy patterns).
If most of your commands are all sharing a part of the same behaviour, don't forget the template method pattern.
If the complexity for creating your command objects increases ( i.e. there is complexity in decoding/understanding the input of your command line), you should start looking at the interpreter design pattern
If while designing with the help of the interpreter pattern, you happen to see some complexity ( if the interpreter needs too much work, you see syntax issues and so on ), then you should probably look at DSL, domain specific language, and design your own language that fits (and only fits) to you own inputs.
The if-else ladder is fine.
It can in principle be replaced with a map<string, Function>, but that gains you nothing for this concrete case (it is added complexity for no particular gain, even with a high number of commands).
When I wrote this initially I forgot to mention though:
Make your command handlers separate functions.
If you don’t, then the if-else ladder can become quite messy… The map solution requires separate functions, and can therefore appear to be a little more clean than an everything-directly-here if-else ladder. But it’s really the separate functions that then provide a measure of clarity, while the map detracts a little (adding extra work in maintaining the map, and an extra level of indirection to cope with).
On the other hand, since “read commands from the console” indicates interactive input, with a user in the picture, you don’t want to read two or more commands from the same line of input. Because that would screw up the prompting, and can seem quite baffling to the user. So instead of reading a “word” of input at a time using >>, use std::getline to a read a full line of input at a time.
Use the new and improved way to preform a bunch of commands at will:
int happy_time = 5;
int a = happy_time;
int muddy_dirt = 1;
int b = muddy_dirt;
int c = happy_time * muddy_dirt //really messy stuff
that's probably the least complicated way to do it...
You must use database like access if your command is large.