C++ questions

Discussion of chess software programming and technical issues.

Moderators: hgm, Dann Corbit, Harvey Williamson

User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: C++ stringstream

Post by lucasart »

mcostalba wrote:
lucasart wrote:Your code works, but I don't understand how!
Would you care to explain this C++ magic ?

Code: Select all


std::string(" ", !name.empty()) 

Calls std::string following constructor

string ( const char * s, size_t n );
    Content is initialized to a copy of the string formed by the first n characters in the array of characters pointed by s.
So if name is empty (when first part of the name is parsed) it is called std::string(" ", 0) that simply returns an empty string, otherwise it is called std::string(" ", 1) that returns a blank.
Very elegant!
I didn't know std::string had such a constructor. Now I can see why :)
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: C++ stringstream

Post by lucasart »

How do I find an element in a vector, with a given predicate ? I have a vector of options:

Code: Select all

struct Option
{
	enum Type { Boolean, Integer };
	Type type;
	std::string name;
	int value, min, max;
};

std&#58;&#58;vector<Option> options;
Basically, I want to write this code in a more C++ idiomatic style, using std::find_if (or something similar)

Code: Select all

for &#40;auto i = options.begin&#40;); i < options.end&#40;); i++) &#123;
	if &#40;i->name = "Hash" && i->type == Option&#58;&#58;Boolean&#41;
		break;
&#125;

if &#40;i < options.end&#40;)) &#123;
	// do something with *i
&#125;
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: C++ stringstream

Post by lucasart »

OK here's my attempt to solve the problem. In non technical terms, I want to:
* maintain a sorted list of options, which must always remain unique (according to a certain predicate)
* insert, and find options in this sorted list

From what I understood googling around, it seems that std::set is more appropriate than std::vector for that. Here is my attempt:

Code: Select all

#include <iostream>
#include <string>
#include <set>

struct Option &#123;
	std&#58;&#58;string name;
	enum Type &#123;Boolean, Integer&#125;;
	Type type;
	int value, min, max;
	
	bool operator < &#40;const Option& o&#41; const
	&#123; return type < o.type && name < o.name; &#125;
&#125;;

std&#58;&#58;set<Option> options;

int main&#40;)
&#123;
	// define a few options
	Option o1 = &#123;"Hash", Option&#58;&#58;Boolean, true, false, true&#125;;
	Option o2 = &#123;"Hash", Option&#58;&#58;Integer, 32, 1, 8192&#125;;
	Option o3 = &#123;"Verbose", Option&#58;&#58;Boolean, true, false, true&#125;;
	
	// insert them in the set
	options.insert&#40;o1&#41;;
	options.insert&#40;o2&#41;;
	options.insert&#40;o3&#41;;
	
	// search for this one
	Option o = &#123;"Hash", Option&#58;&#58;Integer, 0, 0, 0&#125;;
	auto i = options.find&#40;o&#41;;

	// display the found option
	std&#58;&#58;cout << i->name << '\t'
		<< i->min << '\t'
		<< i->max << '\t'
		<< &#40;i->type == Option&#58;&#58;Boolean ? "bool" &#58; "integer")
		<< std&#58;&#58;endl;
&#125;
And the output is:

Code: Select all

Hash	0	1	bool
while I would expect to find the Integer Hash option instead. Could anyone help me fix this code ?

Thank you
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: C++ stringstream

Post by lucasart »

I found my mistake: the operator< was incorrect. Should be:

Code: Select all

bool operator < &#40;const Option& o&#41; const
&#123; return type < o.type || name < o.name; &#125;
And STL basically defines the equality predicate as

Code: Select all

&#40;o1 == o2&#41; <=> !&#40;o1<o2 || o1>o2&#41;
which perfectly explains why the first element of the std::set was a good find!

Anyway, stay tuned: I'll probably more stupid questions like that, as I try to figure out how to use C++ STL.
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: C++ stringstream

Post by mcostalba »

lucasart wrote: From what I understood googling around, it seems that std::set is more appropriate than std::vector for that.
Yes, but perhaps std::map it is even more, it avoids you to define compare operators and is more suitable (IMHO) for the "pick one option with a given key" scenario. I see std::set better in case you just need to find out if an element is in a set or not.

Code: Select all

#include <iostream>
#include <map>
#include <string>


struct Option &#123;
   std&#58;&#58;string name;
   enum Type &#123;Boolean, Integer&#125;;
   Type type;
   int value, min, max;
&#125;;

typedef std&#58;&#58;pair<std&#58;&#58;string, Option&#58;&#58;Type> Key;

std&#58;&#58;map<Key, Option> options;

int main&#40;)
&#123;
   // define a few options
   Option o1 = &#123;"Hash", Option&#58;&#58;Boolean, true, false, true&#125;;
   Option o2 = &#123;"Hash", Option&#58;&#58;Integer, 32, 1, 8192&#125;;
   Option o3 = &#123;"Verbose", Option&#58;&#58;Boolean, true, false, true&#125;;

   // insert them in the map
   options&#91;std&#58;&#58;make_pair&#40;o1.name, o1.type&#41;&#93; = o1;
   options&#91;std&#58;&#58;make_pair&#40;o2.name, o2.type&#41;&#93; = o2;
   options&#91;std&#58;&#58;make_pair&#40;o3.name, o3.type&#41;&#93; = o3;

   // search for this one
   Key key = std&#58;&#58;make_pair&#40;"Hash", Option&#58;&#58;Integer&#41;;

   // here is our option
   auto i = options&#91;key&#93;;

   // display the found option
   std&#58;&#58;cout << i.name << '\t'
             << i.min << '\t'
             << i.max << '\t'
             << &#40;i.type == Option&#58;&#58;Boolean ? "bool" &#58; "integer")
             << std&#58;&#58;endl;
&#125;
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: C++ stringstream

Post by lucasart »

mcostalba wrote:
lucasart wrote: From what I understood googling around, it seems that std::set is more appropriate than std::vector for that.
Yes, but perhaps std::map it is even more, it avoids you to define compare operators and is more suitable (IMHO) for the "pick one option with a given key" scenario. I see std::set better in case you just need to find out if an element is in a set or not.

Code: Select all

#include <iostream>
#include <map>
#include <string>


struct Option &#123;
   std&#58;&#58;string name;
   enum Type &#123;Boolean, Integer&#125;;
   Type type;
   int value, min, max;
&#125;;

typedef std&#58;&#58;pair<std&#58;&#58;string, Option&#58;&#58;Type> Key;

std&#58;&#58;map<Key, Option> options;

int main&#40;)
&#123;
   // define a few options
   Option o1 = &#123;"Hash", Option&#58;&#58;Boolean, true, false, true&#125;;
   Option o2 = &#123;"Hash", Option&#58;&#58;Integer, 32, 1, 8192&#125;;
   Option o3 = &#123;"Verbose", Option&#58;&#58;Boolean, true, false, true&#125;;

   // insert them in the map
   options&#91;std&#58;&#58;make_pair&#40;o1.name, o1.type&#41;&#93; = o1;
   options&#91;std&#58;&#58;make_pair&#40;o2.name, o2.type&#41;&#93; = o2;
   options&#91;std&#58;&#58;make_pair&#40;o3.name, o3.type&#41;&#93; = o3;

   // search for this one
   Key key = std&#58;&#58;make_pair&#40;"Hash", Option&#58;&#58;Integer&#41;;

   // here is our option
   auto i = options&#91;key&#93;;

   // display the found option
   std&#58;&#58;cout << i.name << '\t'
             << i.min << '\t'
             << i.max << '\t'
             << &#40;i.type == Option&#58;&#58;Boolean ? "bool" &#58; "integer")
             << std&#58;&#58;endl;
&#125;
Thank you Marco. Couple of questions
1/ what happens if I insert several options with the same key ? will I get them all in the map, or will the newly inserted one come and overwrite the one with the same key everytime ? I need to ensure uniqueness, so that's important.
2/ after

Code: Select all

auto i = options&#91;key&#93;;
how do I know if the option corresponding to the key has been found or not ?
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: C++ stringstream

Post by mcostalba »

Hi Lucas,

1) std::map will overwrite so ensures uniqueness, std::multimap is used when more than one element with a given key is needed.

2) You don't. If a corrisponding option is not found a new one, corresponding to your key, is inserted. In case you need to check in advance, but it depends on the context. For instance in your case I guess options names are hardcoded in the program so don't find a given option is a bug. In this case an assert is perhaps more suitable:

Code: Select all

   // search for this one
   Key key = std&#58;&#58;make_pair&#40;"Hash", Option&#58;&#58;Integer&#41;;

   assert&#40;options.count&#40;key&#41;);

   // here is our option
   auto i = options&#91;key&#93;; 
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: C++ stringstream

Post by lucasart »

mcostalba wrote:Hi Lucas,

1) std::map will overwrite so ensures uniqueness, std::multimap is used when more than one element with a given key is needed.

2) You don't. If a corrisponding option is not found a new one, corresponding to your key, is inserted. In case you need to check in advance, but it depends on the context. For instance in your case I guess options names are hardcoded in the program so don't find a given option is a bug. In this case an assert is perhaps more suitable:

Code: Select all

   // search for this one
   Key key = std&#58;&#58;make_pair&#40;"Hash", Option&#58;&#58;Integer&#41;;

   assert&#40;options.count&#40;key&#41;);

   // here is our option
   auto i = options&#91;key&#93;; 
Thanks! OK now I'm using an std::map, and I typically have to change engine options at runtime. Here is a sample code to modify the value of the option corresponding to key = (type, name)

Code: Select all

	Option&#58;&#58;Key key = std&#58;&#58;make_pair&#40;type, name&#41;;
	auto it = options.find&#40;key&#41;;
	
	if &#40;it != options.end&#40;))
	&#123;
		options&#91;key&#93;.value = value;
	&#125;
"it" is an std::map<Option::Key, Option>::iterator, right ? So *it should simply be an Option that I can modify. But when I try to use

Code: Select all

it->value = value;
the compiler insults me with an incomprehensible error message (as always in C++ with STL):

Code: Select all

error&#58; ‘struct std&#58;&#58;pair<const std&#58;&#58;pair<Engine&#58;&#58;Option&#58;&#58;Type, std&#58;&#58;basic_string<char> >, Engine&#58;&#58;Option>’ has no member named ‘value’
The point is that

Code: Select all

options&#91;key&#93;.value = value;
is inefficient, because we already have found the iterator (having done a tree-search). To modify the Option that we've already found, we have to look for it again (because options[key] does yet another tree-search) ? This can't be right... I'm sure there's a way to use my iterator directly.
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
mar
Posts: 2552
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: C++ stringstream

Post by mar »

Shouldn't that be it->second = value instead?
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: C++ stringstream

Post by Sven »

lucasart wrote:I typically have to change engine options at runtime
I'd assume that typically you only change option values at runtime while the set of supported options itself stays constant. Your "struct Option" data structure combines a static, descriptive part specifying what your program supports, and a dynamic part containing a value per option, initially set to some default.

Sven