Jump to content
  • Advertisement
Sign in to follow this  
Mantear

[C++] template question

This topic is 4031 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

Greetings, I've got a class that stores its value as a std::string. This value can be set using a public accessor method that is templated and overloaded to accept many different inputs. The generic 'set' template method takes in ints, shorts, floats, etc, passes the value through a std::stringstream for conversion, then stores the value as a string. I have a couple specialized versions of the 'set' method which handle input that the stringstream cannot. This all works fine. My problem comes with the 'get' method. Originally, my 'get' method required the caller to pass by reference a parameter into the function to know what type to convert to. It looks like:
<type> value;
instance.get(value);
This again allowed me to use specialized methods for the types that stringstream can't convert to. However, it felt cumbersome to have to first declare a variable and then pass it into the 'get' method. I then thought about how would look to have the 'get' method simply return the appropriate type and look like:
<type> value = instance.get<type>();
This works fine for many cases, expect for the specialized ones. I have 2 specializations of the 'set' method, but when I try to declare the specializations of the 'get' method using my new scheme, the compiler errors out saying that the two functions only differ by return types. In other words, I have:
template <typename T>
T get()
{
   std::stringstream s;
   s << m_String.c_str();
   double temp = 0.0f;
   s >> temp;
   return static_cast<T>(temp);
}

std::string get() 
{
   return m_String;
}

MyClass get()
{
   MyClass instance;
   instance.set_text(m_String);
   // More happens, but I think you get the picture.
   return MyClass;
}
Is there no way to tell the compiler which version of 'get' (std::string or MyClass) to use? I am able to do it for the template types, why not these other two? (in other words,
unsigned int value = instance.get<unsigned int>(); // works
float value_2 = instance.get<float>(); // works
std::string value_3 = instance.get<std::string>(); // fails!

Share this post


Link to post
Share on other sites
Advertisement
Do this:
template <typename T>
T get()
{
std::stringstream s;
s << m_String.c_str();
double temp = 0.0f;
s >> temp;
return static_cast<T>(temp);
}

template <>
std::string get<std::string>()
{
return m_String;
}

template <>
MyClass get<MyClass>()
{
MyClass instance;
instance.set_text(m_String);
// More happens, but I think you get the picture.
return MyClass;
}

Share this post


Link to post
Share on other sites
@ King Mir: That's exactly what I was looking for, thanks.

@ deathkrush: I'm not sure of the reasoning behind your post. Are you just saying to use the lexical_cast over the stringstream (which I wouldn't argue against), or does using the lexical_cast gain me something else?

@ rozz666: Good article. I'm going to have to keep that one in mind as I'm sure I'll have to come back to it at some point. It's a bit too much for me to fully appreciate at this time since I'm not a wiz with templates.

Share this post


Link to post
Share on other sites
Why not define the usual stream insertion and extraction operators (<< and >>) for your other types? That way you don't have any special cases.

Share this post


Link to post
Share on other sites
@ the_edd: Two reasons.

First, I had a problem with std::string. Going from a stringstream to a string using >> causes the copy of data to stop at a space. I want it to copy the entire string. I'm not sure I could even change that behavior.

Second, when using the MyClass example, more things have to happen than simply the >> operator. This then made two specialization functions and I ran into problems.

Share this post


Link to post
Share on other sites
Quote:

@ deathkrush: I'm not sure of the reasoning behind your post. Are you just saying to use the lexical_cast over the stringstream (which I wouldn't argue against), or does using the lexical_cast gain me something else?


lexical_cast<> uses stringstream, but it includes a whole bunch of added features for specialized casts <template specializations> developed over a period of time by some very smart guys. It also throws an exception when something goes wrong.

It's perfectly OK to use your own function for these types of casts, but if you want a (near) bulletproof approach, use boost::lexical_cast<>.

Quote:

First, I had a problem with std::string. Going from a stringstream to a string using >> causes the copy of data to stop at a space. I want it to copy the entire string. I'm not sure I could even change that behavior.


Use std::getline(std::istream,std::string) to read an entire line at a time, or lookup .rdbuf() to find some ways to read whole files at a time, warts and all. Also it looks like you're over-complicating all the type casts, etc. You could simplify and probably get there easier.

I'm not 100% clear as to what you're trying to do... but you could simplify by trying something like:


template <typename T>
T get()
{
std::stringstream s;
T cache = 0;
if ( !(s << m_String.c_str()) ); // throw exception
if ( !(s >> cache) ); // throw exception
return cache;
}

// In fact, if I'm not mistaken, lexical_cast contains the even simpler form:

template <typename T>
T get()
{
std::stringstream s;
T cache = 0;
if ( !(s << m_String.c_str()) || !(s >> cache) ); // throw exception
return cache;
}

// Which is about as compact as you could get...



--random

[Edited by - random_thinker on October 1, 2007 4:10:05 PM]

Share this post


Link to post
Share on other sites
The first templated version I gave does basically what you have. The reason I extract the stringstream to a double first is I had an issue when I set the precision of the stringstream low and it converted the input to scientific notation. If I try to extract to an int, it would read '7.7e+006' as '7'. If I extract it do a double, then cast to the int, I would get the correct 7700000. Does that explain the extra conversion I'm doing that you were wondering about?

The problem comes back to the >> operator and std::string. If I use your examples and the given type is a std::string, the >> operator will stop at the first white space. I won't be able to get the behavior I want without making a specialization for the std::string case. You mentioned using std::getline(std::istream,std::string), which is fine, but that's not part of the templated example you gave.

Share this post


Link to post
Share on other sites
Quote:

...The reason I extract the stringstream to a double first is I had an issue when I set the precision of the stringstream low and it converted the input to scientific notation...


The number that you are seeing when you send the result to output is not what is actually stored, as this should be according to specified type. If you want to view the number in the type form that you want (as opposed to scientific notation which is default, I believe), use stream formatters when you output it. You could do this as a template and feed the type just as you have done with the 'get()' function.

If you have a space in your string representation of a number then the number conversion probably won't work as you wish when stringstream is used. The other option is to step down closer to machine level and write an fsm-type string parser function.

To use std::getline() you just insert your stream and your string and you can read up to the first newline marker by default, or specify your own marker (like a space). std::getline(,) has some advantages over 'std::cin >>' for example in terms of how it leaves the stream state. Use it something like:


std::string cache;
std::cout >> "Input a number: ";
std::getline(std::cin,cache);
// Cache will contain your input. Accepts up to newline, can also use:
std::getline(std::cin,cache," ");
// To stop at a space.


--random

[Edited by - random_thinker on October 2, 2007 2:02:26 AM]

Share this post


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

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!