E
Eric Lilja
Hello, I have a std::vector storing pointers to an abstract base class
OptionBase. OptionBase is not templated but I am deriving a template class
called Option from OptionBase. The class Option has a public member function
called get_value() that is not present in the class OptionBase. My problem
is that when calling get_value() I have to cast to Option<some_type>* and I
was wondering if I can somehow remove the cast so I dont have to remember
exatly what type Option is instantiated with.
Some code to clarify:
typedef Option<string> StringOption;
typedef Option<long> LongOption;
vector<OptionBase*> options(8);
options[0] = new StringOption("Name=");
options[1] = new StringOption("Process=");
options[2] = new LongOption("Level=");
options[3] = new StringOption("Technique=");
options[4] = new StringOption("Knowledge=");
options[5] = new StringOption("Device=");
options[6] = new StringOption("Primary components=");
options[7] = new StringOption("Build components=");
/* Is there a way to remove these casts so I dont have to remember
exactly what type I used to instantiate Option at each element? */
((StringOption*)options[0])->get_value(), /* name */
((StringOption*)options[1])->get_value(), /* process */
((LongOption*)options[2])->get_value(), /* level */
The class definitions:
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);
/* TODO: Turn this into a call to copy_all_past() function. */
std::string str = s.substr(m_name.length(), s.length());
if(str.empty())
throw std::runtime_error("Option lacks value.");
set_value(convert<T>(str));
}
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:
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());
return (str == starts_with);
}
template<typename T>
T convert(const std::string& a)
{
std::istringstream ss(a);
T out;
if(!(ss >> out))
throw runtime_error("Conversion not possible.");
return out;
}
/* We need this specialization so we don't try to convert
a std::string to a std::string. Doing so will loose
all but the first "word" in string containing spaces. */
template<>
std::string convert(const std::string& s)
{
return s;
}
T m_value;
};
Thanks for reading and replying...hope my question made sense
/ Eric
OptionBase. OptionBase is not templated but I am deriving a template class
called Option from OptionBase. The class Option has a public member function
called get_value() that is not present in the class OptionBase. My problem
is that when calling get_value() I have to cast to Option<some_type>* and I
was wondering if I can somehow remove the cast so I dont have to remember
exatly what type Option is instantiated with.
Some code to clarify:
typedef Option<string> StringOption;
typedef Option<long> LongOption;
vector<OptionBase*> options(8);
options[0] = new StringOption("Name=");
options[1] = new StringOption("Process=");
options[2] = new LongOption("Level=");
options[3] = new StringOption("Technique=");
options[4] = new StringOption("Knowledge=");
options[5] = new StringOption("Device=");
options[6] = new StringOption("Primary components=");
options[7] = new StringOption("Build components=");
/* Is there a way to remove these casts so I dont have to remember
exactly what type I used to instantiate Option at each element? */
((StringOption*)options[0])->get_value(), /* name */
((StringOption*)options[1])->get_value(), /* process */
((LongOption*)options[2])->get_value(), /* level */
The class definitions:
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);
/* TODO: Turn this into a call to copy_all_past() function. */
std::string str = s.substr(m_name.length(), s.length());
if(str.empty())
throw std::runtime_error("Option lacks value.");
set_value(convert<T>(str));
}
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:
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());
return (str == starts_with);
}
template<typename T>
T convert(const std::string& a)
{
std::istringstream ss(a);
T out;
if(!(ss >> out))
throw runtime_error("Conversion not possible.");
return out;
}
/* We need this specialization so we don't try to convert
a std::string to a std::string. Doing so will loose
all but the first "word" in string containing spaces. */
template<>
std::string convert(const std::string& s)
{
return s;
}
T m_value;
};
Thanks for reading and replying...hope my question made sense
/ Eric