Connecting Tech Pros Worldwide Forums | Help | Site Map

How do I make my convert() function do nothing for equal types?

Eric Lilja
Guest
 
Posts: n/a
#1: Jul 23 '05
Hello, I've made a templated class Option (a child of the abstract base
class OptionBase) that stores an option name (in the form someoption=) and
the value belonging to that option. The value is of the type the object is
instantiated with. In my test program I have Option<std::string> and
Option<long>. Here's the code for OptionBase and Option along with a small
helper function. In the code are comments describing my problem, look
closely at the read_option() and convert() functions in the Option class.

#ifndef OPTION_HPP
#define OPTION_HPP

#include <stdexcept>
#include <string>
#include <typeinfo>

static bool
starts_with(const std::string& s, const std::string& starts_with)
{
if(s.length() < starts_with.length())
return false;

std::string str = s.substr(0, starts_with.length());

if(str == starts_with)
return true;

return false;
}

class OptionBase
{
public:
OptionBase(const std::string& name)
:
m_name(name),
m_not_set(true) {}

virtual ~OptionBase() {}

virtual void read_option(const std::string&) = 0;

const std::string get_name() const
{
return m_name;
}

protected:
std::string m_name;
bool m_not_set;
};

template<typename T>
class Option : public OptionBase
{
public:
Option(const std::string& name)
:
OptionBase(name) {}

virtual void read_option(const std::string& s)
{
if(!starts_with(s, m_name))
throw std::runtime_error(std::string("Didn't find option ") +
m_name);

std::string str = s.substr(m_name.length(), s.length());

if(str.empty())
throw std::runtime_error("Option lacks value.");

/* Problem: Will try to convert std::string to std::string
which will lead to "Iron Gual Dye" becoming just "Iron". */
T tmp = convert(str);

set_value(tmp);
}

T get_value() const
{
if(m_not_set)
throw std::runtime_error("Called get_value() when no value had been
set.");

return m_value;
}

void set_value(const T& value)
{
m_value = value;
m_not_set = false;
}

private:
T convert(const std::string& s)
{
/* Here I want to simply return s if(typeid(T) == typeid(std::string)
but return (T)s doesn't compile when this class is instantiated
with type long (I'm using long and std::string in my test program).
*/

stringstream ss;
ss << s;

T out;

if(!(ss >> out))
throw runtime_error("Conversion not possible.");

return out;
}

T m_value;
};

#endif /* #ifndef OPTION_HPP */

So the problem is convert(). When I have instantiated my object with
std::string and I call read_option() on that object it will call convert()
and convert() will convert a string containing spaces to a substring up to
the first space. Iron Gual Dye becomes Iron for example. How should I
prevent this from happening? If I have instantiated with std::string I don't
want to call convert (because it messes up strings with spaces) but I have
to because of set_value().

Hope you understand what I mean...I can provide a complete and compilable
example exhibiting the problem should anyone want me to.

/ Eric



Shezan Baig
Guest
 
Posts: n/a
#2: Jul 23 '05

re: How do I make my convert() function do nothing for equal types?



Eric Lilja wrote:[color=blue]
> template<typename T>
> class Option : public OptionBase
> {
> public:
> Option(const std::string& name)
> :
> OptionBase(name) {}
>
> virtual void read_option(const std::string& s)
> {[/color]


[snip]

[color=blue]
> /* Problem: Will try to convert std::string to std::string
> which will lead to "Iron Gual Dye" becoming just "Iron". */
> T tmp = convert(str);
>
> set_value(tmp);
> }
>[/color]

[snip]
[color=blue]
> private:
> T convert(const std::string& s)
> {
> /* Here I want to simply return s if(typeid(T) ==[/color]
typeid(std::string)[color=blue]
> but return (T)s doesn't compile when this class is[/color]
instantiated[color=blue]
> with type long (I'm using long and std::string in my test[/color]
program).[color=blue]
> */
>
> stringstream ss;
> ss << s;
>
> T out;
>
> if(!(ss >> out))
> throw runtime_error("Conversion not possible.");
>
> return out;
> }
>
> T m_value;
> };
>
> #endif /* #ifndef OPTION_HPP */
>
> So the problem is convert(). When I have instantiated my object with
> std::string and I call read_option() on that object it will call[/color]
convert()[color=blue]
> and convert() will convert a string containing spaces to a substring[/color]
up to[color=blue]
> the first space. Iron Gual Dye becomes Iron for example. How should I[/color]
[color=blue]
> prevent this from happening? If I have instantiated with std::string[/color]
I don't[color=blue]
> want to call convert (because it messes up strings with spaces) but I[/color]
have[color=blue]
> to because of set_value().
>
> Hope you understand what I mean...I can provide a complete and[/color]
compilable[color=blue]
> example exhibiting the problem should anyone want me to.
>
> / Eric[/color]


You can create a specialization for Option<std::string> that does not
read from the string stream.

Hope this helps,
-shez-

Eric Lilja
Guest
 
Posts: n/a
#3: Jul 23 '05

re: How do I make my convert() function do nothing for equal types?



"Shezan Baig" wrote:[color=blue]
> Eric Lilja wrote:[color=green]
>> template<typename T>
>> class Option : public OptionBase
>> {
>> public:
>> Option(const std::string& name)
>> :
>> OptionBase(name) {}
>>
>> virtual void read_option(const std::string& s)
>> {[/color]
>
>
> [snip]
>
>[color=green]
>> /* Problem: Will try to convert std::string to std::string
>> which will lead to "Iron Gual Dye" becoming just "Iron". */
>> T tmp = convert(str);
>>
>> set_value(tmp);
>> }
>>[/color]
>
> [snip]
>[color=green]
>> private:
>> T convert(const std::string& s)
>> {
>> /* Here I want to simply return s if(typeid(T) ==[/color]
> typeid(std::string)[color=green]
>> but return (T)s doesn't compile when this class is[/color]
> instantiated[color=green]
>> with type long (I'm using long and std::string in my test[/color]
> program).[color=green]
>> */
>>
>> stringstream ss;
>> ss << s;
>>
>> T out;
>>
>> if(!(ss >> out))
>> throw runtime_error("Conversion not possible.");
>>
>> return out;
>> }
>>
>> T m_value;
>> };
>>
>> #endif /* #ifndef OPTION_HPP */
>>
>> So the problem is convert(). When I have instantiated my object with
>> std::string and I call read_option() on that object it will call[/color]
> convert()[color=green]
>> and convert() will convert a string containing spaces to a substring[/color]
> up to[color=green]
>> the first space. Iron Gual Dye becomes Iron for example. How should I[/color]
>[color=green]
>> prevent this from happening? If I have instantiated with std::string[/color]
> I don't[color=green]
>> want to call convert (because it messes up strings with spaces) but I[/color]
> have[color=green]
>> to because of set_value().
>>
>> Hope you understand what I mean...I can provide a complete and[/color]
> compilable[color=green]
>> example exhibiting the problem should anyone want me to.
>>
>> / Eric[/color]
>
>
> You can create a specialization for Option<std::string> that does not
> read from the string stream.
>
> Hope this helps,
> -shez-
>[/color]

Thanks for your reply. I tried moving the convert() function outside the
class and made a specialization for it for std::string but it always calls
the "wrong" convert. The code:
template<typename T>
T convert(const std::string& s, T)
{
stringstream ss;
ss << s;

T out;

if(!(ss >> out))
throw runtime_error("Conversion not possible.");

return out;
}

template<typename T>
std::string convert(const std::string& s, std::string)
{
return s;
}

But maybe that's not what you meant? My template skills are a bit weak I
must say =/

/ Eric


Eric Lilja
Guest
 
Posts: n/a
#4: Jul 23 '05

re: How do I make my convert() function do nothing for equal types?



"Eric Lilja" wrote:[color=blue]
>
> "Shezan Baig" wrote:[color=green]
>> Eric Lilja wrote:[color=darkred]
>>> template<typename T>
>>> class Option : public OptionBase
>>> {
>>> public:
>>> Option(const std::string& name)
>>> :
>>> OptionBase(name) {}
>>>
>>> virtual void read_option(const std::string& s)
>>> {[/color]
>>
>>
>> [snip]
>>
>>[color=darkred]
>>> /* Problem: Will try to convert std::string to std::string
>>> which will lead to "Iron Gual Dye" becoming just "Iron". */
>>> T tmp = convert(str);
>>>
>>> set_value(tmp);
>>> }
>>>[/color]
>>
>> [snip]
>>[color=darkred]
>>> private:
>>> T convert(const std::string& s)
>>> {
>>> /* Here I want to simply return s if(typeid(T) ==[/color]
>> typeid(std::string)[color=darkred]
>>> but return (T)s doesn't compile when this class is[/color]
>> instantiated[color=darkred]
>>> with type long (I'm using long and std::string in my test[/color]
>> program).[color=darkred]
>>> */
>>>
>>> stringstream ss;
>>> ss << s;
>>>
>>> T out;
>>>
>>> if(!(ss >> out))
>>> throw runtime_error("Conversion not possible.");
>>>
>>> return out;
>>> }
>>>
>>> T m_value;
>>> };
>>>
>>> #endif /* #ifndef OPTION_HPP */
>>>
>>> So the problem is convert(). When I have instantiated my object with
>>> std::string and I call read_option() on that object it will call[/color]
>> convert()[color=darkred]
>>> and convert() will convert a string containing spaces to a substring[/color]
>> up to[color=darkred]
>>> the first space. Iron Gual Dye becomes Iron for example. How should I[/color]
>>[color=darkred]
>>> prevent this from happening? If I have instantiated with std::string[/color]
>> I don't[color=darkred]
>>> want to call convert (because it messes up strings with spaces) but I[/color]
>> have[color=darkred]
>>> to because of set_value().
>>>
>>> Hope you understand what I mean...I can provide a complete and[/color]
>> compilable[color=darkred]
>>> example exhibiting the problem should anyone want me to.
>>>
>>> / Eric[/color]
>>
>>
>> You can create a specialization for Option<std::string> that does not
>> read from the string stream.
>>
>> Hope this helps,
>> -shez-
>>[/color]
>
> Thanks for your reply. I tried moving the convert() function outside the
> class and made a specialization for it for std::string but it always calls
> the "wrong" convert. The code:
> template<typename T>
> T convert(const std::string& s, T)
> {
> stringstream ss;
> ss << s;
>
> T out;
>
> if(!(ss >> out))
> throw runtime_error("Conversion not possible.");
>
> return out;
> }
>
> template<typename T>
> std::string convert(const std::string& s, std::string)
> {
> return s;
> }
>
> But maybe that's not what you meant? My template skills are a bit weak I
> must say =/
>
> / Eric
>[/color]

I solved it, here it is with the convert() function as a stand-alone
function:
template<typename T1, typename T2>
T2 convert(const T1& a, const T2&)
{
stringstream ss;
ss << a;

T2 out;

if(!(ss >> out))
throw runtime_error("Conversion not possible.");

return out;
}

template<>
std::string convert(const std::string& s, const std::string&)
{
return s;
}

Now, should I (can I?) make convert a member of the class Option? And can I
loose the second argument (it's after all not used in the function)?

/ Eric


Shezan Baig
Guest
 
Posts: n/a
#5: Jul 23 '05

re: How do I make my convert() function do nothing for equal types?


Eric Lilja wrote:[color=blue]
> I solved it, here it is with the convert() function as a stand-alone
> function:
> template<typename T1, typename T2>
> T2 convert(const T1& a, const T2&)
> {
> stringstream ss;
> ss << a;
>
> T2 out;
>
> if(!(ss >> out))
> throw runtime_error("Conversion not possible.");
>
> return out;
> }
>
> template<>
> std::string convert(const std::string& s, const std::string&)
> {
> return s;
> }
>
> Now, should I (can I?) make convert a member of the class Option? And[/color]
can I[color=blue]
> loose the second argument (it's after all not used in the function)?
>
> / Eric[/color]


You don't need to second argument. In the option class, declare it
like this:

template<typename RETURN_TYPE>
RETURN_TYPE convert(const std::string& s);

Outside the class, provide two definitions for this function, one
generic and the other specialized for std::string:

template <typename TYPE>
template <typename RETURN_TYPE>
RETURN_TYPE Option<TYPE>::convert(const std::string& s)
{
// ...
}

template <typename TYPE>
template <>
std::string Option<TYPE>::convert<std::string>(const std::string& s)
{
// ...
}


When you call the convert function, you call it like this:

convert<TYPE>(s);

So, if 'TYPE' is 'std::string', it will call the specialised version.

Hope this helps,
-shez-

Closed Thread


Similar C / C++ bytes