U
Unforgiven
I have the following situation:
Given this class:
template<typename T>
class Expression {
/* omitted */
};
This is the base class for a BooleanExpression and an ArithmeticExpression.
Now I have another struct, that contains a BooleanExpression and an
ArithmeticExpression. This expression will be either Expression<int> or
Expression<string>. Which one is determined at runtime. The problem I'm
faced with is how to store them. Basically, I'm leaning toward two different
options.
1. Create a base class BaseExpression, that doesn't really do anything at
all, and have Expression inherit from it, making the other class like this:
struct MatchModifier
{
BaseExpression *test;
BaseExpression *operation;
};
I'll have a flag somewhere that I'll need to have anyway that'll tell me the
type it's going to be, so I could then dynamic_cast them whenever I need to
use them.
2. Just have two pointers, only one of which is used:
struct MatchModifier
{
BooleanExpression<int> *intTest;
BooleanExpression<std::string> *stringTest;
ArithmeticExpression *intOperation;
std::string *stringOperation;
};
As you can see here, there's not actually an
ArithmeticExpression<std::string>, it'll just be a plain string.
From my perspective, option two holds the best cards. It clearly relates the
fact that test is always a BooleanExpression and that operation is always an
ArithmeticExpression (or string constant). It also means I don't need to
create an additional class that has no real purpose, and I won't need to
create a StringConstant class or whatever that inherits from BaseExpression
to wrap the std::string. It's also more performant (I think) as the
expressions will be used often, and for option 1 that means a dynamic_cast
every single time, and option 2 just involves picking one of two pointers.
Option 2 does waste 8 bytes per MatchModifier, but that's not such a big
problem, and I could always use unions if that really becomes a concern.
Option 2 does have the distinct disadvantage that adding additional
Expression types adds more work, but at this time it doesn't seem likely
that that'll happen.
However, somehow my sense of style has a little voice in my head saying that
option 2 is wrong somehow, that there must be a more elegant solution. But
all I can come up with is solution 1.
So, which would you prefer. Or would you perhaps know a better, third
alternative?
Thanks in advance for any input.
Given this class:
template<typename T>
class Expression {
/* omitted */
};
This is the base class for a BooleanExpression and an ArithmeticExpression.
Now I have another struct, that contains a BooleanExpression and an
ArithmeticExpression. This expression will be either Expression<int> or
Expression<string>. Which one is determined at runtime. The problem I'm
faced with is how to store them. Basically, I'm leaning toward two different
options.
1. Create a base class BaseExpression, that doesn't really do anything at
all, and have Expression inherit from it, making the other class like this:
struct MatchModifier
{
BaseExpression *test;
BaseExpression *operation;
};
I'll have a flag somewhere that I'll need to have anyway that'll tell me the
type it's going to be, so I could then dynamic_cast them whenever I need to
use them.
2. Just have two pointers, only one of which is used:
struct MatchModifier
{
BooleanExpression<int> *intTest;
BooleanExpression<std::string> *stringTest;
ArithmeticExpression *intOperation;
std::string *stringOperation;
};
As you can see here, there's not actually an
ArithmeticExpression<std::string>, it'll just be a plain string.
From my perspective, option two holds the best cards. It clearly relates the
fact that test is always a BooleanExpression and that operation is always an
ArithmeticExpression (or string constant). It also means I don't need to
create an additional class that has no real purpose, and I won't need to
create a StringConstant class or whatever that inherits from BaseExpression
to wrap the std::string. It's also more performant (I think) as the
expressions will be used often, and for option 1 that means a dynamic_cast
every single time, and option 2 just involves picking one of two pointers.
Option 2 does waste 8 bytes per MatchModifier, but that's not such a big
problem, and I could always use unions if that really becomes a concern.
Option 2 does have the distinct disadvantage that adding additional
Expression types adds more work, but at this time it doesn't seem likely
that that'll happen.
However, somehow my sense of style has a little voice in my head saying that
option 2 is wrong somehow, that there must be a more elegant solution. But
all I can come up with is solution 1.
So, which would you prefer. Or would you perhaps know a better, third
alternative?
Thanks in advance for any input.