• Advertisement
Sign in to follow this  

Generating a Unique (numbered) Name in C++

This topic is 3293 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to replicate a simple piece of code in C++ which was written in a Basic variant. Essentially, I start out with a name, which can be anything. I then check in an associative array to see if the name is already used. If it's not, I use it. If it is, I add a number to the end of the name until I end up with a unique name. So if I tried to create an object called "Item" 5 times, I would end up with "Item", "Item1", "Item2", "Item3" and "Item4" respectively. I do this with a simple integer increment and then convert the integer to a string, and concatenate. C++ doesn't make converting from int to string particularly easy. I'm wondering what the most elegant way of doing this is. I'm currently leaning towards writing a class for it. Create an instance of the class, then call the increment method which, internally, uses a char array and a simple loop to cycle the digits through all the permutations. But by God that seems like a messy and bloated way of doing what should be a simple procedure. Is there a cleaner, neater way to do this?

Share this post


Link to post
Share on other sites
Advertisement
It sounds like representing names in your map as strings is causing you undue stress. Why can't a name be a struct consisting of a string and an int? Once you have that (plus a suitable less-than operator), finding the highest-numbered item with a given string-part would be as easy as a std::upper_bound.

Share this post


Link to post
Share on other sites
Quote:
Original post by sybixsus
Is there a cleaner, neater way to do this?
There most certainly is.

From the least to the most preferable:
a) Using std::stringstream from the C++ standard library.
b) Using boost::lexical_cast from the boost library.

I'm too tired to trust myself to give you bug free examples of them all but a quick bit of searching reveals that MaulingMonkey has serendipitously handled them before!

Edit: After sweeping the sleep away from my eyes I see that MaulingMonkey's examples are actually of the complete reverse problem (strings -> integers), I'll leave it as an exercise for you to figure out how to swap them around [grin]

[Edited by - dmatter on January 10, 2009 11:08:43 PM]

Share this post


Link to post
Share on other sites
Quote:
C++ doesn't make converting from int to string particularly easy. I'm wondering what the most elegant way of doing this is.


What is so difficult about:


#include <sstream>
...

int val = 10; // integer value
std::string str; // string

std::stringstream buf;
buf << val;
str = buf.str();


Share this post


Link to post
Share on other sites
Quote:
Original post by sybixsus
I'm trying to replicate a simple piece of code in C++ which was written in a Basic variant. Essentially, I start out with a name, which can be anything. I then check in an associative array to see if the name is already used. If it's not, I use it. If it is, I add a number to the end of the name until I end up with a unique name.

So if I tried to create an object called "Item" 5 times, I would end up with "Item", "Item1", "Item2", "Item3" and "Item4" respectively. I do this with a simple integer increment and then convert the integer to a string, and concatenate. C++ doesn't make converting from int to string particularly easy. I'm wondering what the most elegant way of doing this is.

I'm currently leaning towards writing a class for it. Create an instance of the class, then call the increment method which, internally, uses a char array and a simple loop to cycle the digits through all the permutations. But by God that seems like a messy and bloated way of doing what should be a simple procedure.

Is there a cleaner, neater way to do this?


Here is the string stream method in a template function for easy conversions:

template <class out_type, class in_type>
out_type cast_stream(const in_type & inValue){
std::stringstream ss;
ss << inValue;
out_type outValue;
ss >> outValue;
return outValue;
}
...

std::string result;
for(int i = 0;i < 5;i++){
result = "item" + cast_stream<std::string>(i);
...
}






You can also write your own overloads for the << and >> operators and use those in your own classes to perform conversions where it makes sense.

*edit: haha, a bit late on the draw I guess.

Share this post


Link to post
Share on other sites
Not everyone wants to include boost just to cast from an int to a string. bost::lexical_cast is obviously preferred if you are planning on using boost, but if you are not I don't see what's wrong with writing a basic cast function.

If you could expand on exactly what is wrong with the code I posted I would be grateful.

Share this post


Link to post
Share on other sites
Quote:
Original post by M2tM
Not everyone wants to include boost just to cast from an int to a string. bost::lexical_cast is obviously preferred if you are planning on using boost, but if you are not I don't see what's wrong with writing a basic cast function.

If you could expand on exactly what is wrong with the code I posted I would be grateful.


I too was curious. After poking around it looks like boost's lexical cast manages to perform specializations that allow it to avoid redundant allocations/copies (for example, if the input is a string, then streaming the string into the stringstream is an excessive allocation and copy; if it's an output, streaming out to string is another excessive allocation and copy). Additionally, it looks like it does error checking and exception throwing.

If there's anything else that boost's lexical cast does better, or if I've gotten any of that wrong, I'd love to know as well. The header is certainly easier for a compiler to parse than for a human: the boost lexical_cast header.

Share this post


Link to post
Share on other sites
Quote:
Original post by M2tM
If you could expand on exactly what is wrong with the code I posted I would be grateful.

The two biggies:
1) It doesn't handle conversions to strings when the buffer has spaces in it. Ex: if you have an ordered pair class that has a text representation of "(1, 2)", the returned string would only contain "(1,".
2) It doesn't detect error conditions such as converting the string "fred" to an int.

boost::lexical_cast also does a lot of work to avoid compiler/standard library bugs including a memory leak in MSVC 2005 (without the service pack) that your code would suffer from.

Share this post


Link to post
Share on other sites
Quote:
Original post by M2tM
Not everyone wants to include boost just to cast from an int to a string.
Keep in mind that you don't really have to 'include Boost' to use lexical_cast; it's header only, so all you really have to do is include a single header file (if that's what you meant by 'include Boost', then never mind, although it does seem somewhat limiting to deprive oneself of such a simple and useful tool due to not wanting to include a few headers).

I'll admit though that even with the single-header Boost libraries the include trees can be fairly deep, and maybe this is what you're objecting to. For others who might read this thread though, I just wanted to clarify that Boost is not an 'all or nothing' library, but rather a collection of individual libraries, many if not most of which are header-only and require only a single inclusion.

Share this post


Link to post
Share on other sites
Quote:
Original post by jyk
Quote:
Original post by M2tM
Not everyone wants to include boost just to cast from an int to a string.


Keep in mind that you don't really have to 'include Boost' to use lexical_cast; it's header only, so all you really have to do is include a single header file (if that's what you meant by 'include Boost', then never mind, although it does seem somewhat limiting to deprive oneself of such a simple and useful tool due to not wanting to include a few headers).


Right.

So essentially M2tM, you're spending time to write a (poor) copy of the wheel so you than can then include that header everywhere, instead of downloading a single well vetted file and include that everywhere.

It doesn't even really matter if your code is perfect, it's always a worse choice.

Share this post


Link to post
Share on other sites
Whilst using boost::lexical_cast for casting an integer to a string is clearly superior to writing a casting function in terms of std::stringstream. It does seem to me that the OP's actual objective isn't necessarily that task. The OP just wishes to append an integer to the end of a string and, although I would still favour lexical_cast for the optimisations, this is the sort of thing that string-streams are classically used/good for:


#include <string>
#include <utility>
#include <map>
#include <sstream>
#include <algorithm>
#include <iostream>
#include <iterator>

class enumerate_name
{
private:
typedef std::map<std::string, int> usecount_map;
usecount_map usecounts;

public:
std::string operator()(const std::string & name)
{
std::pair<usecount_map::iterator, bool> result =
usecounts.insert(std::make_pair(name, 0));

if (result.second) return name;

std::ostringstream ss(name, std::ostringstream::ate);
ss << ++result.first->second;
return (*this)(ss.str());
}
};

int main()
{
const char * names[] = { "item", "item", "item", "item1" };

std::transform(names,
names + 4,
std::ostream_iterator<std::string>(std::cout, " "),
enumerate_name());
}


[Edited by - dmatter on January 11, 2009 12:23:35 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by dmatter
Whilst using boost::lexical_cast for casting an integer to a string is clearly superior to writing a casting function in terms of std::stringstream. It does seem to me that the OP's actual objective isn't necessarily that task. The OP just wishes to append an integer to the end of a string and, although I would still favour lexical_cast for the optimisations, this is the sort of thing that string-streams are classically used/good for:

*** Source Snippet Removed ***

Just a minor (admittedly pedantic :-) detail for the sake of portability: note that std::ostream_iterator is defined in the <iterator> header.

Share this post


Link to post
Share on other sites
Quote:
Original post by Metsan
Just a minor (admittedly pedantic :-) detail for the sake of portability: note that std::ostream_iterator is defined in the <iterator> header.
Thanks, I had copied the includes in afterwards and missed one off the bottom. [smile]

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
It doesn't even really matter if your code is perfect, it's always a worse choice.
I think worse is a relative term here. If you know what you are converting from and what you are converting to, there may be preferable solutions. Preferable, in my case, was getting rid of a warning cause by boost::lexical_cast. Time for a story:

I try to use boost::lexical_cast whenever I can, but I ended up getting warnings when casting from something to std::string. I'm building a static library that people include in their projects, and the problem was that the warning wasn't showing when I built the library, but it would show up when customers tried to use the library in their projects. It wasn't a major warning (it just said a return statement in boost::lexical_cast was unreachable because of a throw statement), but it's not acceptable for me to be giving people libraries which will produce warnings in their projects. So I wrote my own toString() function that used std::ostringstream to convert to std::string. It's simple and doesn't do any real error checking, but since I know I'm only using to to convert an int or float to a std::string, I consider it to be worth it to get rid of that warning.

Moral of the story: use boost::lexical_cast if possible, but be aware of alternate solutions should the necessity ever arise.

Share this post


Link to post
Share on other sites
Thanks guys. I was generally aware of Boost, but not any of the libraries in particular. It looks like something I should read up on more. There could be some other things in there which I'll find useful. Having been programming mainly in BlitzMax lately, I do like the look of Boost::Foreach.

I'm just getting back into C++ after a very long absence ( and I was only a beginner then ) so I'm trying to avoid anything which might have memory useage requirements I'm not aware of. I read about StringStream as a solution, but since I've never used it, I wasn't sure if there were any implications as regards freeing it after using it. It looks from the examples you've posted as though it's allocated on the stack and any memory used is returned automatically when you finish it.

I think I'll go with Boost::Lexical_Cast. I'd only seen boost before in BlitzMax, and therefore I wasn't aware that it could be included on a header by header basis. That fits very well with the kind of programmer I am, so that seems like a good solution.

Thanks for all the advice.

Share this post


Link to post
Share on other sites
Quote:
Original post by sybixsus
I do like the look of Boost::Foreach.
There is also a std::for_each, it's not as nice to use as BOOST_FOREACH for the time being, but I imagine it might make the boost version redundant in the next C++ standard where it's usage pattern is greatly simplified.

Quote:
I read about StringStream as a solution, but since I've never used it, I wasn't sure if there were any implications as regards freeing it after using it.
Nope, in fact, there rarely are any occasions where you need to manually free memory from standard library components; they all employ the RAII idiom (Resource Acquisition is Initialisation) so they clean up after themselves, thus your observation...

Quote:
It looks from the examples you've posted as though it's allocated on the stack and any memory used is returned automatically when you finish it.

... is correct [smile]

Share this post


Link to post
Share on other sites
Thanks for the info. I didn't spend a long time writing that obviously, and I haven't used it for much other than basic conversions from built in number types to string and back. I'll get boost::lexical_cast now that I'm aware of the issues.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement