Any way to get rid of the cast when calling get_value()?

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
 
S

Sep

Couldn't you make get_value() into a pure virtual function in
OptionBase and rely on the vtable to call the correct function? Or do
you not want to incur the extra overhead?
 
E

Eric Lilja

Sep said:
Couldn't you make get_value() into a pure virtual function in
OptionBase and rely on the vtable to call the correct function? Or do
you not want to incur the extra overhead?

Thanks for your reply, Sep. I'm not concerned about virtual function
overhead just not sure how the declaration of get_value() would look like in
the OptionBase class, i.e., what should its return type be?

/ Eric
 
S

Sep

Sorry, I was a bit hasty in typing that up and forgot that template
functions cannot be virtual.
 
S

Sep

I had thought at first that get_value() had to be a template class but
then thankfully remembered the relaxation of type rule.

To make get_value() into a virtual function, just define it with an
arbitrary non-void type in OptionBase as a pure virtual then override
it in Options with return type T. The return-type of overridden
virtual functions is allowed to differ, making this allowed. Now the
correct function will be called polymorphically when calling
get_value() through a pointer to OptionBase.
 
S

Sep

Nope, that wasn't correct either. Having a bit of a bad day in the
thinking department.
 
D

David Hilsee

Eric Lilja said:
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 */
[...]

When possible, the preferred way to downcast to a subclass from a base is
dynamic_cast, because it will perform a runtime check to see if the cast
succeeded (correct type) or failed (wrong type). The C-style casts that you
are using won't do that.

To answer your question, you could have multiple member functions:

virtual long OptionBase::get_long_value();
virtual std::string OptionBase::get_string_value();

They could either throw exceptions if the type doesn't match, or attempt to
convert between the types to satisfy the request. You'd still need to know
the "correct" or "compatible" type(s) before calling the members, though.
This approach would be fine if the number of option types was small and
unchanging, but it might get ugly if more and more types had to be added
later.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top