Can I have std::vector store these subclasses mixed?

E

Eric Lilja

Hello, in my program I am reading a text file that has format:
option=value
There are several types of options and they expect values of different types
(mostly strings or integers). I made a templated base class Option and had
my different options derive from Option instantiated for their type (i.e.,
class LongOption inherits publicly from Option<long>). I selected this
approach because I wanted a uniform interface for obtaining the type and
value of a given option. I came up with the following class tree for testing
purposes:
template <typename T>
class Option
{
public:
enum OptionType
{
LONG,
STRING
};

Option(const std::string& name)
:
m_name(name),
m_not_set(true) {}

virtual ~Option() {}

T get_value() const
{
if(m_not_set)
throw;

return m_value;
}

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

m_value = value;
}

virtual OptionType get_type() const = 0;

private:
std::string m_name;
bool m_not_set;
T m_value;
};

class LongOption : public Option<long>
{
public:
LongOption(const std::string& name)
:
Option<long>(name) {}

virtual OptionType get_type() const
{
return STRING;
}
};

class StringOption : public Option<std::string>
{
public:
StringOption(const std::string& name)
:
Option<std::string>(name) {}

virtual OptionType get_type() const
{
return LONG;
}
};

That seems to compile, but I cannot do:
std::vector<Option*> options(11);
options[0] = new StringOption("Name=");
The compiler complains:
$ make gccmain
g++ gccmain.cpp -o gccmain
gccmain.cpp: In function `int main()':
gccmain.cpp:6: error: use of class template `template<class T> class Option'
as expression
gccmain.cpp:6: error: parse error before `>' token
gccmain.cpp:8: error: `options' undeclared (first use this function)
gccmain.cpp:8: error: (Each undeclared identifier is reported only once for
each function it appears in.)
make: *** [gccmain] Error 1

Not being able to store my different options in the same container makes
reading the file with options a lot messier...are their any ways to solve
this? I'm not very good at templates so I've certainly made a number of
silly mistakes.

/ Eric
 
R

Rolf Magnus

Eric said:
Hello, in my program I am reading a text file that has format:
option=value
There are several types of options and they expect values of different
types (mostly strings or integers). I made a templated base class Option
and had my different options derive from Option instantiated for their
type (i.e., class LongOption inherits publicly from Option<long>). I
selected this approach because I wanted a uniform interface for obtaining
the type and value of a given option. I came up with the following class
tree for testing purposes:
template <typename T>
class Option
{
public:
enum OptionType
{
LONG,
STRING
};

This enum must be changed whenever you add another derived type. I'd use
typeinfo instead.
Option(const std::string& name)
:
m_name(name),
m_not_set(true) {}

virtual ~Option() {}

T get_value() const
{
if(m_not_set)
throw;

throw what?
return m_value;
}

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

m_value = value;
}

virtual OptionType get_type() const = 0;

private:
std::string m_name;
bool m_not_set;
T m_value;
};

class LongOption : public Option<long>
{
public:
LongOption(const std::string& name)
:
Option<long>(name) {}

virtual OptionType get_type() const
{
return STRING;
}
};

I don't really think you need to derive for each type if you use typeinfo.
I'll present an example of what I mean below.
class StringOption : public Option<std::string>
{
public:
StringOption(const std::string& name)
:
Option<std::string>(name) {}

virtual OptionType get_type() const
{
return LONG;
}
};

That seems to compile, but I cannot do:
std::vector<Option*> options(11);

Option is a template, not a type. Therefore, you cannot create a vector of
Option pointers.
options[0] = new StringOption("Name=");
The compiler complains:
$ make gccmain
g++ gccmain.cpp -o gccmain
gccmain.cpp: In function `int main()':
gccmain.cpp:6: error: use of class template `template<class T> class
Option' as expression
gccmain.cpp:6: error: parse error before `>' token
gccmain.cpp:8: error: `options' undeclared (first use this function)
gccmain.cpp:8: error: (Each undeclared identifier is reported only once
for
each function it appears in.)
make: *** [gccmain] Error 1

Not being able to store my different options in the same container makes
reading the file with options a lot messier...are their any ways to solve
this?

Yes. Make a non-templated base class and derive your Option template from
it. Then store pointers to that base class in your container.
I'm not very good at templates so I've certainly made a number of
silly mistakes.


class OptionBase
{
public:
OptionBase(const std::string& name)
: m_name(name),
m_not_set(true)
{
}
virtual std::typeinfo get_type() const = 0;
virtual ~OptionBase() {}

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

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

T get_value() const
{
if(m_not_set)
throw SomeException();

return m_value;
}

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

m_value = value;
}

virtual std::typeinfo get_type() const
{
return typeid(T);
}

private:
T m_value;
};

//Don't need to derive for each type anymore - a typedef is sufficient
typedef Option<Long> LongOption;
typedef Option<std::string> StringOption;

//...
std::vector<OptionBase*> options(11);
options[0] = new StringOption("Name=");

//And to test for the type:
if (options[0]->get_type() == typeid(std::string))
{
//we have a string
}

Another thing you might be interested in is boost::any. See
http://www.boost.org/doc/html/any.html
 
E

Eric Lilja

Rolf Magnus said:
Eric said:
Hello, in my program I am reading a text file that has format:
option=value
There are several types of options and they expect values of different
types (mostly strings or integers). I made a templated base class Option
and had my different options derive from Option instantiated for their
type (i.e., class LongOption inherits publicly from Option<long>). I
selected this approach because I wanted a uniform interface for obtaining
the type and value of a given option. I came up with the following class
tree for testing purposes:
template <typename T>
class Option
{
public:
enum OptionType
{
LONG,
STRING
};

This enum must be changed whenever you add another derived type. I'd use
typeinfo instead.
Option(const std::string& name)
:
m_name(name),
m_not_set(true) {}

virtual ~Option() {}

T get_value() const
{
if(m_not_set)
throw;

throw what?
return m_value;
}

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

m_value = value;
}

virtual OptionType get_type() const = 0;

private:
std::string m_name;
bool m_not_set;
T m_value;
};

class LongOption : public Option<long>
{
public:
LongOption(const std::string& name)
:
Option<long>(name) {}

virtual OptionType get_type() const
{
return STRING;
}
};

I don't really think you need to derive for each type if you use typeinfo.
I'll present an example of what I mean below.
class StringOption : public Option<std::string>
{
public:
StringOption(const std::string& name)
:
Option<std::string>(name) {}

virtual OptionType get_type() const
{
return LONG;
}
};

That seems to compile, but I cannot do:
std::vector<Option*> options(11);

Option is a template, not a type. Therefore, you cannot create a vector of
Option pointers.
options[0] = new StringOption("Name=");
The compiler complains:
$ make gccmain
g++ gccmain.cpp -o gccmain
gccmain.cpp: In function `int main()':
gccmain.cpp:6: error: use of class template `template<class T> class
Option' as expression
gccmain.cpp:6: error: parse error before `>' token
gccmain.cpp:8: error: `options' undeclared (first use this function)
gccmain.cpp:8: error: (Each undeclared identifier is reported only once
for
each function it appears in.)
make: *** [gccmain] Error 1

Not being able to store my different options in the same container makes
reading the file with options a lot messier...are their any ways to solve
this?

Yes. Make a non-templated base class and derive your Option template from
it. Then store pointers to that base class in your container.
I'm not very good at templates so I've certainly made a number of
silly mistakes.


class OptionBase
{
public:
OptionBase(const std::string& name)
: m_name(name),
m_not_set(true)
{
}
virtual std::typeinfo get_type() const = 0;
virtual ~OptionBase() {}

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

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

T get_value() const
{
if(m_not_set)
throw SomeException();

return m_value;
}

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

m_value = value;
}

virtual std::typeinfo get_type() const
{
return typeid(T);
}

private:
T m_value;
};

//Don't need to derive for each type anymore - a typedef is sufficient
typedef Option<Long> LongOption;
typedef Option<std::string> StringOption;

//...
std::vector<OptionBase*> options(11);
options[0] = new StringOption("Name=");

//And to test for the type:
if (options[0]->get_type() == typeid(std::string))
{
//we have a string
}

Another thing you might be interested in is boost::any. See
http://www.boost.org/doc/html/any.html

Very nice, Rolf! I will try out your code as soon as I've showered & shaved,
looks real nice! Thank you for such a detailed response.

/ Eric
 
E

Eric Lilja

Eric Lilja said:
Rolf Magnus said:
Eric said:
Hello, in my program I am reading a text file that has format:
option=value
There are several types of options and they expect values of different
types (mostly strings or integers). I made a templated base class Option
and had my different options derive from Option instantiated for their
type (i.e., class LongOption inherits publicly from Option<long>). I
selected this approach because I wanted a uniform interface for
obtaining
the type and value of a given option. I came up with the following class
tree for testing purposes:
template <typename T>
class Option
{
public:
enum OptionType
{
LONG,
STRING
};

This enum must be changed whenever you add another derived type. I'd use
typeinfo instead.
Option(const std::string& name)
:
m_name(name),
m_not_set(true) {}

virtual ~Option() {}

T get_value() const
{
if(m_not_set)
throw;

throw what?
return m_value;
}

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

m_value = value;
}

virtual OptionType get_type() const = 0;

private:
std::string m_name;
bool m_not_set;
T m_value;
};

class LongOption : public Option<long>
{
public:
LongOption(const std::string& name)
:
Option<long>(name) {}

virtual OptionType get_type() const
{
return STRING;
}
};

I don't really think you need to derive for each type if you use
typeinfo.
I'll present an example of what I mean below.
class StringOption : public Option<std::string>
{
public:
StringOption(const std::string& name)
:
Option<std::string>(name) {}

virtual OptionType get_type() const
{
return LONG;
}
};

That seems to compile, but I cannot do:
std::vector<Option*> options(11);

Option is a template, not a type. Therefore, you cannot create a vector
of
Option pointers.
options[0] = new StringOption("Name=");
The compiler complains:
$ make gccmain
g++ gccmain.cpp -o gccmain
gccmain.cpp: In function `int main()':
gccmain.cpp:6: error: use of class template `template<class T> class
Option' as expression
gccmain.cpp:6: error: parse error before `>' token
gccmain.cpp:8: error: `options' undeclared (first use this function)
gccmain.cpp:8: error: (Each undeclared identifier is reported only once
for
each function it appears in.)
make: *** [gccmain] Error 1

Not being able to store my different options in the same container makes
reading the file with options a lot messier...are their any ways to
solve
this?

Yes. Make a non-templated base class and derive your Option template from
it. Then store pointers to that base class in your container.
I'm not very good at templates so I've certainly made a number of
silly mistakes.


class OptionBase
{
public:
OptionBase(const std::string& name)
: m_name(name),
m_not_set(true)
{
}
virtual std::typeinfo get_type() const = 0;
virtual ~OptionBase() {}

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

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

T get_value() const
{
if(m_not_set)
throw SomeException();

return m_value;
}

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

m_value = value;
}

virtual std::typeinfo get_type() const
{
return typeid(T);
}

private:
T m_value;
};

//Don't need to derive for each type anymore - a typedef is sufficient
typedef Option<Long> LongOption;
typedef Option<std::string> StringOption;

//...
std::vector<OptionBase*> options(11);
options[0] = new StringOption("Name=");

//And to test for the type:
if (options[0]->get_type() == typeid(std::string))
{
//we have a string
}

Another thing you might be interested in is boost::any. See
http://www.boost.org/doc/html/any.html

Very nice, Rolf! I will try out your code as soon as I've showered &
shaved, looks real nice! Thank you for such a detailed response.

/ Eric

Heh, I couldn't wait to try the code out..I'm having some problems...first I
had to change std::typeinfo to std::type_info, guess that was just a simple
typo on your part (happens all the time for me). But I'm still having
problems with the get_type() function. The code is:

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

virtual ~OptionBase() {}

virtual std::type_info get_type() const = 0;

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

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

virtual std::type_info get_type() const
{
return typeid(T);
}

T get_value() const
{
if(m_not_set)
throw;
return m_value;
}

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

private:
T m_value;
};

typedef Option<std::string> StringOption;

int main()
{
std::vector<OptionBase*> options(11);

options[0] = new StringOption("duh");

return 0;
}

and the error is:
/usr/include/c++/3.3.3/typeinfo: In member function `std::type_info
Option<T>::get_type() const [with T = std::string]':
gccmain.cpp:8: instantiated from here
/usr/include/c++/3.3.3/typeinfo:75: error: `std::type_info::type_info(const
std::type_info&)' is private
option.hpp:38: error: within this context
make: *** [gccmain] Error 1

What am I doing wrong? I must say that I have never used type_info before.

/ Eric
 
E

Eric Lilja

Eric Lilja said:
Eric Lilja said:
Rolf Magnus said:
Eric Lilja wrote:

Hello, in my program I am reading a text file that has format:
option=value
There are several types of options and they expect values of different
types (mostly strings or integers). I made a templated base class
Option
and had my different options derive from Option instantiated for their
type (i.e., class LongOption inherits publicly from Option<long>). I
selected this approach because I wanted a uniform interface for
obtaining
the type and value of a given option. I came up with the following
class
tree for testing purposes:
template <typename T>
class Option
{
public:
enum OptionType
{
LONG,
STRING
};

This enum must be changed whenever you add another derived type. I'd use
typeinfo instead.


Option(const std::string& name)
:
m_name(name),
m_not_set(true) {}

virtual ~Option() {}

T get_value() const
{
if(m_not_set)
throw;

throw what?


return m_value;
}

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

m_value = value;
}

virtual OptionType get_type() const = 0;

private:
std::string m_name;
bool m_not_set;
T m_value;
};

class LongOption : public Option<long>
{
public:
LongOption(const std::string& name)
:
Option<long>(name) {}

virtual OptionType get_type() const
{
return STRING;
}
};

I don't really think you need to derive for each type if you use
typeinfo.
I'll present an example of what I mean below.


class StringOption : public Option<std::string>
{
public:
StringOption(const std::string& name)
:
Option<std::string>(name) {}

virtual OptionType get_type() const
{
return LONG;
}
};

That seems to compile, but I cannot do:
std::vector<Option*> options(11);

Option is a template, not a type. Therefore, you cannot create a vector
of
Option pointers.

options[0] = new StringOption("Name=");
The compiler complains:
$ make gccmain
g++ gccmain.cpp -o gccmain
gccmain.cpp: In function `int main()':
gccmain.cpp:6: error: use of class template `template<class T> class
Option' as expression
gccmain.cpp:6: error: parse error before `>' token
gccmain.cpp:8: error: `options' undeclared (first use this function)
gccmain.cpp:8: error: (Each undeclared identifier is reported only once
for
each function it appears in.)
make: *** [gccmain] Error 1

Not being able to store my different options in the same container
makes
reading the file with options a lot messier...are their any ways to
solve
this?

Yes. Make a non-templated base class and derive your Option template
from
it. Then store pointers to that base class in your container.

I'm not very good at templates so I've certainly made a number of
silly mistakes.


class OptionBase
{
public:
OptionBase(const std::string& name)
: m_name(name),
m_not_set(true)
{
}
virtual std::typeinfo get_type() const = 0;
virtual ~OptionBase() {}

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

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

T get_value() const
{
if(m_not_set)
throw SomeException();

return m_value;
}

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

m_value = value;
}

virtual std::typeinfo get_type() const
{
return typeid(T);
}

private:
T m_value;
};

//Don't need to derive for each type anymore - a typedef is sufficient
typedef Option<Long> LongOption;
typedef Option<std::string> StringOption;

//...
std::vector<OptionBase*> options(11);
options[0] = new StringOption("Name=");

//And to test for the type:
if (options[0]->get_type() == typeid(std::string))
{
//we have a string
}

Another thing you might be interested in is boost::any. See
http://www.boost.org/doc/html/any.html

Very nice, Rolf! I will try out your code as soon as I've showered &
shaved, looks real nice! Thank you for such a detailed response.

/ Eric

Heh, I couldn't wait to try the code out..I'm having some problems...first
I had to change std::typeinfo to std::type_info, guess that was just a
simple typo on your part (happens all the time for me). But I'm still
having problems with the get_type() function. The code is:

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

virtual ~OptionBase() {}

virtual std::type_info get_type() const = 0;

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

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

virtual std::type_info get_type() const
{
return typeid(T);
}

T get_value() const
{
if(m_not_set)
throw;
return m_value;
}

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

private:
T m_value;
};

typedef Option<std::string> StringOption;

int main()
{
std::vector<OptionBase*> options(11);

options[0] = new StringOption("duh");

return 0;
}

and the error is:
/usr/include/c++/3.3.3/typeinfo: In member function `std::type_info
Option<T>::get_type() const [with T = std::string]':
gccmain.cpp:8: instantiated from here
/usr/include/c++/3.3.3/typeinfo:75: error:
`std::type_info::type_info(const
std::type_info&)' is private
option.hpp:38: error: within this context
make: *** [gccmain] Error 1

What am I doing wrong? I must say that I have never used type_info before.

/ Eric

Note: It compiles if I make get_type() a non-virtual member function of the
class Option (i.e., it doesn't appear in class ObjectBase at all). But that
removes the one function that made my OptionBase class an abstract base
class.

/ Eric
 
R

Rolf Magnus

Eric said:
Heh, I couldn't wait to try the code out..I'm having some problems...first
I had to change std::typeinfo to std::type_info, guess that was just a
simple typo on your part (happens all the time for me).

Yes, it was. I haven't used type_info for a while, so I forgot the
underscore :)
and the error is:
/usr/include/c++/3.3.3/typeinfo: In member function `std::type_info
Option<T>::get_type() const [with T = std::string]':
gccmain.cpp:8: instantiated from here
/usr/include/c++/3.3.3/typeinfo:75: error:
`std::type_info::type_info(const
std::type_info&)' is private
option.hpp:38: error: within this context
make: *** [gccmain] Error 1

What am I doing wrong? I must say that I have never used type_info before.

Again, my error. std::type_info cannot be copied. Try returning it by
reference instead:

virtual const std::type_info& get_type() const
{
return typeid(T);
}
 
E

Eric Lilja

Rolf Magnus said:
Eric said:
Heh, I couldn't wait to try the code out..I'm having some
problems...first
I had to change std::typeinfo to std::type_info, guess that was just a
simple typo on your part (happens all the time for me).

Yes, it was. I haven't used type_info for a while, so I forgot the
underscore :)
and the error is:
/usr/include/c++/3.3.3/typeinfo: In member function `std::type_info
Option<T>::get_type() const [with T = std::string]':
gccmain.cpp:8: instantiated from here
/usr/include/c++/3.3.3/typeinfo:75: error:
`std::type_info::type_info(const
std::type_info&)' is private
option.hpp:38: error: within this context
make: *** [gccmain] Error 1

What am I doing wrong? I must say that I have never used type_info
before.

Again, my error. std::type_info cannot be copied. Try returning it by
reference instead:

virtual const std::type_info& get_type() const
{
return typeid(T);
}

Thanks Rolf, I finally made it compile and run without crashing. I had to
make m_not_set a protected (not private) member variable of class OptionBase
and I also removed the value variable from OptionBase. The only thing that
makes the class a bit cumbersome to use is that I have to find the proper
type and cast it to that before being able to call set_value() or
get_value() but I guess there's no way around that.

/ Eric
 

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,708
Messages
2,569,347
Members
44,653
Latest member
Angus30T19

Latest Threads

Top