Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Lexical Unit
Sep 16, 2003

Let's say I want to write this code:
code:
	const int N = 6;
	int A1[N] = {1, 3, 5, 7, 9, 11};
	int A2[N] = {1, 2, 3, 4, 5, 6};
	my_container result;
	std::merge (A1, A1 + N, A2, A2 + N, 
        	std::inserter (result, result.begin ()));
And have it work for the class I've written called my_container. Is there some kind of checklist of items I need to implement to get this to work, or do I need to write another class, say my_container_insert_iterator, and implement it myself (note this is what I've already done but I'd like to know if I can do away with it)?

Also, in trying to get code like that shown above to compile I was meet with this error from gcc 4.0.0:
code:
stl_algo.h: In function `_OutputIterator std::merge(_InputIterator1
	_InputIterator1, _InputIterator2, _InputIterator2, _OutputIterator)
	[with _InputIterator1 = int*, _InputIterator2 = int*, 
	_OutputIterator = std::insert_iterator<my_container>]':
	
main.cpp:183:   instantiated from here

stl_algo.h:2998: error: no match for 'operator=' in 
	'__result. std::insert_iterator<_Container>::operator*
	[with _Container = my_container]() = * __first2'
	
stl_iterator.h:531: note: candidates are: std::insert_iterator<_Container>& 
	std::insert_iterator<_Container>::operator=(typename _Container::const_reference)
	[with _Container = my_container]

stl_iterator.h:491: note:                 std::insert_iterator<my_container>& 
	std::insert_iterator<my_container>::operator=(const std::insert_iterator<my_container>&)

Adbot
ADBOT LOVES YOU

Lexical Unit
Sep 16, 2003

Hrm, that may be the case but then is this not correct?
code:
typedef my_container& reference;
typedef const my_container& const_reference;
This is my "learn more about c++ by making a totally STL compliant container class" project and I'm trying to do it all by the book.

I have my own my_container_insert_iterator that I can pass to merge() which does the trick just fine, but I'd like to nix it in favor of std::insert_iterator<my_container> for neatness's sake. I already have const_iterator and iterator defined for my class and they work fine with STL algorithms, it's just the standard insert iterator that's giving me guff.

Lexical Unit
Sep 16, 2003

Another question (still haven't solved the first as of yet):
code:
#include <iostream>
using namespace std;

template<typename T>
struct foo {
	// ...
	template<typename S>
	void bar(S l);
};

template<typename T>
template<typename S>
void foo<T>::bar(S l) {
	cout << "not an int: " << l << endl;
}

template<typename T>
template<>
void foo<T>::bar(int l) {
	cout << "an int: " << l << endl;
}

int main() {
	foo<int> f;
	f.bar ("test");
	f.bar (5);
}
Error:
code:
18: error: invalid explicit specialization before '>' token
18: error: enclosing class templates are not explicitly specialized
How do I specialize method templates of class templates? :downs:

Lexical Unit
Sep 16, 2003

That Turkey Story posted:

No. reference and const_reference are references to the value_type, not references to the container type.
:downs: Thanks, that certainly clears up that mess. Not sure how you deciphered the exact problem from that error though, here's what my thought process was, line by line:

1. Ok, there's an error when I try to call merge().

2. It's this merge() call that's the problem on line 183 of main.cpp

3. In the STL code there's some operator*() calling a operator=() that either isn't defined or is bad.

4. One operator=() takes typename my_container::const_reference.
4a. Check that I have typedef for const_reference, I do, ok.

5. The other operator=() is just the copy-assignment operator for std::insert_iterator<my_container>

6. Huh... I don't get it. I've got const_reference typedefed, so what's the problem? :confused:

It never occurred to me that my_container::const_reference was wrong, though I can see now how that would logically be something further to check. And this error:
code:
stl_algo.h:2998: error: no match for 'operator=' in 
	'__result. std::insert_iterator<_Container>::operator*
	[with _Container = my_container]() = * __first2'
was a bit confusing so I wasn't even sure my reasoning about what the problem was was entirely correct. What's up with the __result. and * __first2? I'm not sure how to read that.

And when you say "get used to reading the error messages" do you mean simply from continued experience, or is there a better way you can suggest?

Lexical Unit
Sep 16, 2003

That Turkey Story posted:

You read it almost fully and were just about there but then you stopped!
Thanks for that awesome reply; I just re-read that error message and my brain was totally like, "oooooooohhhh... now I get it."

Well, back to work! FYI, I'm working on a Modern-C++-y trie class that supports any type as keys (so long as the user provides policies for their key type, defaults of course work with std::string). I'm basically modeling it more or less after std::set(). I think boost's spirit library has already implemented something like this as an implementation detail, I think they use a ternary search tree though. Fun stuff.

Lexical Unit
Sep 16, 2003

Let's say that now I want to make my container exception safe. I'm not sure where I can find information on what parts of the STL are exception safe (and at what level). If anyone can recommend a book or website that would help in this area please do. I have Sutter's Exceptional C++, just not on hand at the moment, so if that's good enough just let me know. I can go re-read it now that most of it isn't over my head.

In the mean time my immediate quandary involves conceptually reassuring myself that the following is no-throw guarantee exception safe:
code:
template< /* ... */ >
class trie
{
/* ... */
	private:
		typedef trie* trie_node;

		struct trie_node_delete
		{
			void operator()(trie_node const &node) throw()
			{
				delete node;
			}
		};

		struct trie_node_less { /* ... */ };
		typedef std::set<trie_node, trie_node_less> subtrie;

		subtrie children;
};

template< /* ... */ >
trie< /* ... */ >::~trie() throw()
{
	for_each (this->children.begin (), this->children.end (), trie_node_delete ());
}
What I'm unsure of is what exception guarantees does for_each(), begin(), end(), and other functions that those functions entail give me? Is this exception safe or not? If it is, do I have the no-throw guarantee?

Lexical Unit
Sep 16, 2003

Beautiful.

Lexical Unit
Sep 16, 2003

code:
main()
{
    int c; // 1

    while (c != '.') { // 2
        c = getchar(); // 3
        putchar(c); // 4
    } // 5
}
Just take it easy and reason out each line.

1: c isn't initialized so you don't have any idea what's in c at the moment.

2: You're testing the value of c, but you don't know what c is. This is bad.

3: c becomes the return value of getchar(). getchar() will not return until the end of file or end of line is reached. So in your example let's say you type in a word like "stuff."

4: Whatever character c is, show it.

5: Go back to the top, and 2: since c isn't a "." (it's a "s") we continue to line 3 again.

3: c becomes the next character from stdin, you've already given the next character before, it's "t". getchar() won't wait for you to hit enter this time because it doesn't need to.

...

Eventually c becomes "." and we end the loop, having printed out "stuff" to stdout. The reason stdin works like this is because it's line-buffered, that's just how it works. Here's an ok discussion with some more information.

fret logic posted:

I'm in WinXP right now so I can't try it out
Can't you use Visual Studio Express — it's free to download.

Lexical Unit
Sep 16, 2003

In C++ I'm wondering what's standard operating procedure when defining a class's typedef for size_type. In other words, if I have a template class which is a container for typename T and I want to define container<T>::size_type, what's the best thing to do?

I considered typedef typename T::size_type size_type; but that doesn't work if T is int, for example. So then I considered using std::char_traits somehow because typedef typename std::char_traits<T>::int_type size_type; seems to work, but that could just be coincidental.

It looks like gcc's implementation of the STL mostly just uses std::size_t, but say I use typedef size_t size_name; and I have a method that returns a size_type and then the container is instantiated with some_crazy_string which uses some_crazy_size_type as it's some_crazy_string::size_type typedef and some_crazy_size_type is incompatible with std::size_t... then I've got a bug, right?

Edit: Or, is the best option to add another template parameter called SizeT and make it default to typename T::size_type, then use typedef SizeT size_type;?

Lexical Unit fucked around with this message at 00:57 on Mar 5, 2008

Lexical Unit
Sep 16, 2003

Avenging Dentist posted:

Just use std::size_t. I'm not 100% sure what you're worried about, but size_t is perfectly acceptable, and it's not like the STL avoids it because they were too lazy to add another template argument. :)
Contrived example ahoy!
code:
#include <iostream>
#include <set>

struct weird_thing {
	// ... insert insanity here ...

	typedef double size_type;

	weird_thing() {
		// Generate some crazy example sizes that might occur:
		static double z = 0;
		z += .2;
		size_ = z;
	}
	
	double size() const {
		return size_;
	}
	
	private:
		size_type size_;
};

template<typename T>
struct container {
	typedef std::size_t size_type;			// Gives BAD answer
//	typedef typename T::size_type size_type;	// Would give the correct answer
	
	// Lets say for some crazy reason we want the size of all items
	// totaled up as a method of our container, the problem is
	// what type does the method return, or use for its running total?
	size_type get_total_item_size() const {
		typename item_set::iterator i = items.begin ();
		size_type total = i->size ();
		for (++i; i != items.end (); ++i)
		{
			total += i->size ();
		}
		return total;
	}
	
	void add() {
		T item;
		items.insert (item);
	}

	private:
		template<typename F>
		struct LessT {
			bool operator()(const F &lhs, const F &rhs) const {
				return lhs.size () < rhs.size ();
			}
		};
		
		// Let's say container orders only by the size of it's items
		// such that items are sorted from shortest item to longest item
		typedef typename std::set<T, LessT<T> > item_set;
		item_set items;
};

int main () {
	container<weird_thing> foo;
	
	// Add 5 weird_things to foo
	foo.add ();
	foo.add ();
	foo.add ();
	foo.add ();
	foo.add ();
	
	// This should be 3, but gives 1.
	std::cout << foo.get_total_item_size () << std::endl;
	
	return 0;
}

Lexical Unit
Sep 16, 2003

Ah I see. These standard container typedefs have a me a bit confused because they seem a little inconsistent from my perspective. It seems like value_type, pointer, const_pointer, reference, and const_reference, are all directly related to the type of the items in a container whereas size_type oddly has nothing at all to do with the type of the items in a container, but rather apparently only the number said items in the container. I always get hung up on these little details that people seem to think are obvious... :downs:

Lexical Unit
Sep 16, 2003

graffin posted:

I'm using it in C++, in the context of a template for a binary tree based dictionary. The object is an element in a node. In the dictionary, I'm trying to test the element to determine if it is an int or string.
I'm not sure exactly what you're trying to do or why, but type_traits might be applicable/helpful.

Lexical Unit
Sep 16, 2003

elevatordeadline, you're code works fine on my 64bit MacBook Pro running OS X 10.5 with gcc version 4.0.1 and also on a 32bit RHEL4AS machine with gcc version 3.4.6. You might open up your inttypes.h file and see what's in there. You may not find a perfectly portable solution, unfortunately.

Lexical Unit
Sep 16, 2003

I've been asking questions in this thread about this code I'm writing to help me learn to be a better C++ programmer for a while, here's what I've come up with thus far:

trie.h
main.cpp
QUnit.h
Edit: moved code from codepad.org to permanent hosting

So who wants to tell me how absolutely terrible my design/style/code is? :shobon:

Lexical Unit fucked around with this message at 01:17 on Mar 26, 2008

Lexical Unit
Sep 16, 2003

Ah good point, that hadn't crossed my mind. Originally I had just the lexical_cast version but then I thought, "shouldn't I be nice and support compiling when boost isn't installed?" and that prompted me to do what I did there. I'm not sure how I'd make the stringstream version throw the same exceptions in the same cases without basically re-writing boost::lexical_cast though. Do you think it would be better to just leave out all the conditional compiling and just flatly require boost?

Lexical Unit
Sep 16, 2003

Avenging Dentist posted:

Speaking of, have you been following the Trie discussion on the Boost developers list?
Nope, not at all. But I just subscribed.

Lexical Unit
Sep 16, 2003

Well, I'm already subscribed to the gcc, gcc-help, xorg, TAO, libstdc++, festival-talk, and boost-users lists, so a little more won't hurt. It gives me something to do during work down-time. Interesting that they're modeling it after std::map, I went with std::set instead. However I can certainly see why one modeled on map would be quite useful.

Lexical Unit
Sep 16, 2003

atof takes a const char*, you're giving it a char.

Beaten of course, I meant to hit preview and not post.

Lexical Unit
Sep 16, 2003

Hopefully that's just a dumbed down snippet of code and the real code allocates memory to number and populates operand... at least, that's what I assumed.

Lexical Unit
Sep 16, 2003

Is this:
code:
ifstream in("filename");
size_t file_size in.rdbuf()->in_avail();
different than this:
code:
ifstream in("filename");
size_t file_size in.rdbuf()->pubseekoff(ios::beg, ios::end);
in.rdbuf()->pubseekpos(ios::beg);
?

Edit: I mean, does it always get the same answer and does it have the same set of side effects and gotchas?

Lexical Unit fucked around with this message at 21:52 on Apr 22, 2008

Lexical Unit
Sep 16, 2003

Nope! Not without an equal sign after file_size in both examples :downs: See a running example here.

Lexical Unit fucked around with this message at 00:05 on Apr 23, 2008

Lexical Unit
Sep 16, 2003

That's what I thought but I was just checking since it seems to work for everything I've thrown at it. There's also this site which says that this code
code:
int FileSize(const char* sFileName)
{
  std::ifstream f;
  f.open(sFileName, std::ios_base::binary | std::ios_base::in);
  if (!f.good() || f.eof() || !f.is_open()) { return 0; }
  f.seekg(0, std::ios_base::beg);
  std::ifstream::pos_type begin_pos = f.tellg();
  f.seekg(0, std::ios_base::end);
  return static_cast<int>(f.tellg() - begin_pos);
}
Might return a value for which the "size of the file may be larger than what is reported."

Leading me to wonder if the pubseekoff() method has the same limitation, and if that limitation is due to the same internal buffer that in_avail() uses, which in turn leads me to wonder if maybe in_avail() really does work just as well as either seek(end)/seek(beg) methods.

Lexical Unit
Sep 16, 2003

JoeNotCharles posted:

why doesn't LongPacket just inherit from Packet
When you get the data out of the socket on the receiving end, it'll just be a bunch of bytes. Understanding how these bytes are packed is important. So there's a desire for the datatypes to be POD. At least that's what I assume... assuming I'm not misunderstanding something.

Smackbilly posted:

I was wondering if there is anything wrong with this
Isn't there a problem if optData isn't always the same length? If not, then it seems like it'll work.

Smackbilly posted:

or if there is a better way to do this
What about this?
code:
struct Packet {
  uint16_t foo;
  uint16_t bar;
  uint8_t isLong;
  uint16_t extraField1;
  uint16_t extraField2;
  char optData[];
};
Can't you just ignore extraField1 and extraField2 if isLong isn't set?

Lexical Unit fucked around with this message at 22:51 on May 5, 2008

Lexical Unit
Sep 16, 2003

Why does this assert?
code:
#include <vector>
#include <fstream>
#include <iterator>
#include <iostream>

using namespace std;

int main()
{
	fstream f ("blob", ios::in | ios::out | ios::trunc);
	streampos start = f.rdbuf ()->pubseekoff (0, ios::cur);
	char data[] = "this is just a test";
	f.write (data, sizeof (data));
	f.rdbuf ()->pubseekpos (start);
	vector<char> v;
	v.insert (v.end (), istream_iterator<char> (f), istream_iterator<char> ());
	assert (!f.fail ());
	return 0;
}
Edit: Never mind, I should have tried harder with google.

Answer: "After reading the last available character, the extraction not only reads past the end of the input sequence; it also fails to extract the requested character. Hence, failbit is set in addition to eofbit."

Lexical Unit fucked around with this message at 17:26 on May 6, 2008

Lexical Unit
Sep 16, 2003

Smackbilly posted:

But you will still need a constant recording how many valid values there are for the purpose of declaring the links[] array. Unfortunately there is no way to simply query an enum type and ask it how many valid values there are.

At my job I always see this sort of thing:

code:
enum Directions
{
  eNorth, eSouth, eWest, eEast, ..., 
  kNumDirections
};
Not sure how I feel about it.

Lexical Unit
Sep 16, 2003

I'm by no means a git expert but I use it for personal projects sometimes. In the past I've simply used git checkout to "unedit" (BitKeeper terminology) files. However it appears there are better options: http://bryan-murdock.blogspot.com/2007/07/git-revert-is-not-equivalent-to-svn.html

Lexical Unit
Sep 16, 2003

Paradoxish posted:

TinyXML is something like five source/header files. I don't see how linking against it statically will add any dependencies at all since end-users won't have to do anything. It'll be part of your executable. You can even just make the drat thing part of your project.

For large projects where multiple teams possibly working for different companies need to integrate code bases to provide some solution that's targeted for some specific hardware/software configuration (aka: platform), using code that isn't "default" [sic] (read: isn't speced to be part of the target platform) is a problem.

Sure, you can just compile statically and provide your binaries that will run on the target platform for deployment, but that doesn't solve the issue of integration testing. For my projects it's often the case that multiple companies are coding with respect to some spec, but our projects don't integrate with each other until late in the game. At that point someone from our company has travel to another company to do integration testing. Which just means we compile and run our code in the other company's environment and get both of our systems up and running and see that they actually work together as the spec says they should. Obviously there's usually problems at this point as it's the first time either company has interacted with each others' components.

So the problem with using some library that's not in the spec is that maybe you won't be able to compile your software during integration testing because some other company doesn't have that library installed. And as most often is the case that company would not be willing to install it just for your code, they'd expect you to conform to the spec instead.

One possible solution to that would simply be to package all of the necessary code and libraries into your codebase so you essentially don't care if the other company has them installed or not, because you're taking them along with you. But then there's the issue of wether or not you really want to incorporate some third party's code into your codebase or not. In many situations this would entail a full audit of the third party code and require additional further audits each time the third party released a new version that you might want to incorporate.

TL,DR: Inside I'm really crying about not being able to use boost where I work while I spout out half-assed justifications for it that I've heard from my boss. :smith:

Lexical Unit
Sep 16, 2003

Ugg boots posted:

How is this a problem from using "non-default" libraries? Instead of including a Header and CPP files in your project you include a Header and LIB files...
See my last paragraph. Basically what it amounts to is we can't do that because what we put into our code base is strictly regulated by agencies not under our control. So it would be much more of a hassle to try and package some third party code/library in our code base than it is to just re-invent the wheel where necessary or work around not having certain functionality that's readily available from third parties.

Sucks.

Lexical Unit
Sep 16, 2003

You're completely right in my estimation.

However the situation is that every few years we get to use a new version of RHEL (that's recently been stamped with a seal of approval) and that implicitly OKs a number of packages/libraries for our consumption. The assumption being that all those libraries/packages that come with some defined configuration/installation of that version of RHEL have undergone some kind of audit (though I highly doubt this is the case or if it is the case I highly doubt the usefulness or completeness of those audits).

Lexical Unit
Sep 16, 2003

Ok, so I'm using private inheritance and promoting the methods I want to expose in the base class with using directives (did I get those technical terms correct?) but I'm a bit confused about one thing.

Say I want to expose only the const version of a method, how do I do that? This doesn't work: using base::operator[] const;, and using base::operator[]; exposes both the const and non-const methods.

Is there a common workaround for this or is my syntax wrong?

\/\/ Oh well, that's what I thought. I was kinda hoping there was some c++ dark magic I could use to accomplish this. :)

Lexical Unit fucked around with this message at 20:27 on Jul 21, 2008

Lexical Unit
Sep 16, 2003

When is it safe to use this idiom:
code:
std::vector<T> v;
T* p = &v[0];
For example I want to do something like this:
code:
const size_t n = 10;
std::vector<int> ints (n);
ints.resize (n);
third_party_c_api_grab_10_ints (&ints[0]); // fill up ints vector with 10 ints
Safe?

Lexical Unit
Sep 16, 2003

That Turkey Story posted:

Edit: So in other words, yes, your second example is safe (though the first one isn't because the vector is empty).
Awesome. I thought I read somewhere that using that idiom to fill a vector was bad, but it was just on some crappy blog and I never really trusted it. And being that I almost never need to use that idiom, especially for filling up a vector, I never looked into it further. Thanks for the clarification :)

Also, I'm just curious if there's a better way to get a char** out of a std::vector<std::string> than just allocating a char** big enough to store the memory and filling it manually in a loop:

code:
void dress(std::vector<std::string>& pants)
{
	char** fruitloops = new char*[pants.size ()];
	for (size_t i = 0; i < pants.size (); ++i)
	{
		fruitloops[i] = new char[pants[i].size () + 1];
		strncpy (fruitloops[i], pants[i].c_str (), pants[i].size () + 1);
	}
	
	crappy_thrid_party_api (fruitloops, pants.size ());

	for (size_t i = 0; i < pants.size (); ++i)
	{
		delete [] fruitloops[i];
	}
	delete [] fruitloops;
}

Lexical Unit fucked around with this message at 18:19 on Jul 25, 2008

Lexical Unit
Sep 16, 2003

That Turkey Story posted:

First, watch out because that code isn't exception safe! If any of those new operations in the for loop throw bad_alloc, you are leaking memory.
Oops!

That Turkey Story posted:

Make sure you handle and rethrow or use RAII. A simple start is to make fruitloops a std::vector instead of a char** (or perhaps better, a smart pointer like boost::scoped_array if you have boost).
No boost :( Do you mean a std::vector<const char*>? I've got a std::vector<std::string>, so you mean populate a const char* version by iterating through the std::string version and calling std::string::c_str() on each element? I thought you couldn't do that because of how std::string::c_str() works.

That Turkey Story posted:

Second, do you mean the API takes a char** or char const* const*?
The API is this: http://tronche.com/gui/x/xlib/window-information/XInternAtoms.html (abandon all hope ye who clicks here). In particular I'm trying to construct the char** names parameter for the function's consumption, then I can promptly throw it away.

That Turkey Story posted:

If it's just an API not using const correctly, as in it's taking const-unqualified data even though it's guaranteed to not modify the data, you can just make a std::vector or scoped_array of char const* and then const_cast when passing the data through the API.
I believe that the function in question will leave the char** alone, but I'm a bit confused about how you mean to construct your std::vector regardless.

Lexical Unit
Sep 16, 2003

ShoulderDaemon posted:

Have you considered using XCB instead of xlib? It's much nicer.
Yes it certainly is. But xcb is not allowed for pretty much the same reason boost isn't allowed: bureaucracy.

Lexical Unit
Sep 16, 2003

Heh, I just wrote something like that (I didn't use the fancy call to std::transform()). I realized this was essentially the same thing with my question about the &vector[0] idiom. The pointer returned by c_str() is valid to use so long as the string remains unchanged, correct? I think I'm getting the hang of this... I hardly ever need to think about this because I'm usually not interfacing with C libraries.

It seems to work just fine, thanks for all the pointers ;)

Lexical Unit
Sep 16, 2003

So I want to know how y'all feel about this approach.

I need to Intern some Atoms in my program. You do this just once, it's like an initialization step. So we need to know beforehand which Atoms should be interned, and then we need to be able to reference these Atoms by name, but only this predefined list of Atoms should be able to be referenced.

After we have the enumeration of available Atoms, we need to then intern them, and in doing so we obtain their Atom values. An Atom value is just an integer value that is determined at runtime.

Then we can generate a mapping from Atom name to Atom value, and whenever a user wants a particular Atom value, he just looks it up by name in the map. Except we don't want the map to go from string Atom name to integer Atom value. We don't want that because we want to restrict the valid key values to only those Atom names which have been interned, not an arbitrary string.

So here goes:
code:
#define STRINGIFY(x) #x
#define EXPAND_AND_STRINGIFY(x) STRINGIFY((x))

bool is_paren(char c)
{
	return (c == ')' || c == '(');
}

std::string remove_parens(std::string &str)
{
	str.erase (std::remove_if (str.begin (), str.end (), is_paren), str.end ());
	return str;
}

#define AVAILABLE_ATOMS
	_NET_WM_STATE,		\
	_MOTIF_WM_HINTS,	\
	WM_NAME
//	Real list is longer, and might need to grow at some later time...

// Get enum values for all the available atoms. We will use this as
// the map key so that we don't have a string as the key, which would be bad.
enum available_atom
{
	AVAILABLE_ATOMS
};

class available_atoms : private std::map<available_atom, Atom>
{
  public:
	static available_atom* instance(Display* display)
	{
		if (!single.count (display))
		{
			available_atoms* new_available_atoms = new available_atoms (display);
			single.insert (std::make_pair (display, new_available_atoms));
		}
		return single[display];
	}

	const Atom& get(available_atom atom) const
	{
		return const_cast<const Atom&> (
			const_cast<available_atoms*> (this)->operator[] (atom));
	}

  private:
	available_atoms();

	// not copyable, not assignable
	available_atoms& operator=(const available_atoms&);
	available_atoms(const available_atoms&);

	typedef std::map<available_atom, Atom> base;

	static std::map<Display*, available_atoms*> single;
};

std::map<Display*, available_atoms*> available_atoms::single;

available_atoms::available_atoms(Display* display)
{
	// Get list of atoms as a string (note that we add a '(' and ')' to the string)
	char atom_names[] = EXPAND_AND_STRINGIFY (AVAILABLE_ATOMS);

	std::vector<const char*> names_strs;
	std::list<std::string> names;
	for (char* line = strtok (atom_names, ","); line != 0; line = strtok (0, ","))
	{
		std::string name = line;
		trim (name); // just remove whitespace
		names.push_back (remove_parens (name));
		names_strs.push_back (names.back ().c_str ());
	}

	// Intern all Atoms at once
	std::vector<Atom> atoms (names_strs.size ());
	atoms.resize (names_strs.size ());
	Status status = XInternAtoms (
		display,
		const_cast<char**> (&names_strs.front ()),
		names_strs.size (),
		False,
		&atoms.front ()
	);

	if (!status)
	{
		throw an_error (display);
	}

	// Generate the map now
	available_atom atom_enum_value = static_cast<available_atoms> (0);
	for (std::vector<Atom>::iterator i = atoms.begin (); i != atoms.end (); ++i)
	{
		base::insert (std::make_pair (atom_enum_value, *i));
		atom_enum_value = static_cast<available_atom> (atom_enum_value + 1);
	}
}
So then in the client code we have a singleton for every display that allows users to get references to the Atom values they might need, but only those Atoms which are interned (ie: only those Atoms which are enumerated as available_atom values).

The only thing I don't like is the code that generates the map. Basically I'm iterating over the values in the enum, but it feels kind of hackish. Comments? Suggestions? Disapprobations?

Lexical Unit fucked around with this message at 18:56 on Jul 28, 2008

Lexical Unit
Sep 16, 2003

http://www.gidforums.com/t-7134.html

Assignment is when you call operator=(), initialization is a constructor.

Also, the code would produce the wrong output if you did as you say, and put p2 = c++; p1 = c; in the body of the constructor.

Lexical Unit
Sep 16, 2003

In your code example you are not initializing cool::p1 with c. You are creating a new megaObj called p1, and initializing it with c. After the first line in cool's constructor, you have two megaObj's called p1. There's this->p1 (or cool::p1) and p1, they are not the same, and this->p1 is initialized by its default zero-parameter constructor.

Triple Tech posted:

Why, is it because of the order in which c is incremented? Or some other reason?
It's because in the initializer-list version of the code, c++ doesn't happen till after p1 has been initialized.

Lexical Unit fucked around with this message at 21:33 on Jul 28, 2008

Lexical Unit
Sep 16, 2003

beuges posted:

Why this:

...

instead of this:
code:
enum available_atom
{
	_NET_WM_STATE,
        _MOTIF_WM_HINTS,
        WM_NAME
};
What MarsMattel and Mustach said.

What I wanted is for the key to be the name of the Atom. But if the key was a string, then someone could typo and the program wouldn't work. I'd have to do runtime checking to make sure the key passed to get() was a valid key. And then what do I do if it isn't? Throw and exception probably.

So I made the enums be exactly what the key names should be, for clarity, and this will catch typos at compile time. Then I can use the same define, expand it and stringify it, and then parse it to get the string names that XInternAtoms() needs. In my real code I also use a std::map<available_atom, std::pair<Atom, std::string> > so that the user can get not only a reference to an Atom, but also the string name of an Atom if they so desire.

Adbot
ADBOT LOVES YOU

Lexical Unit
Sep 16, 2003

prolecat posted:

In that case, you're stuck setting the members of the struct in the body of the class constructor, barring some language feature I'm unaware of.
Well he could do
code:
TestObject::TestObject(int _struct_member):ts((test_struct){_struct_member}){ }
if he wanted, but he should really do what floWenoL said.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply