f



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

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 


0
3/12/2005 8:20:19 PM
comp.lang.c++ 49423 articles. 6 followers. Post Follow

6 Replies
163 Views

Similar Articles

[PageSpeed] 41

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?

0
Sep102 (13)
3/12/2005 8:28:52 PM
"Sep" wrote:
> 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 


0
3/12/2005 9:20:12 PM
Sorry, I was a bit hasty in typing that up and forgot that template
functions cannot be virtual.

0
Sep102 (13)
3/12/2005 9:51:57 PM
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.

0
Sep102 (13)
3/12/2005 10:08:00 PM
Nope, that wasn't correct either.  Having a bit of a bad day in the
thinking department.

0
Sep102 (13)
3/12/2005 10:18:38 PM
"Eric Lilja" <ericliljaNoSpam@yahoo.com> wrote in message
news:d0vita$rch$1@news.island.liu.se...
> 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.

-- 
David Hilsee


0
3/13/2005 12:17:35 AM
Reply: