views:

173

answers:

7

I was coding this up in C# and the quickest solution to come to mind used the "as" or "is" keywords. I began wondering how I could implement it neatly in C++(without RTTI)... or even in C# without the aforementioned keywords.

Here is the problem (simplified):

There is a class Command which contains a stream of so called "tokens".

class Command
{
    public List<Token> Toks {get; set;}

    //...
}

A token may (currently) be a "keyword" or a "game object".

class Token
{
    //nothing in here :(
}

class KWToken : Token
{
    public List<string> Synonyms {get; set;}
}

class GOToken : Token
{

}

Later, I'll want to loop through a Command object's list of Tokens and do stuff based on the kind of Token stored.

The thorn in this scenario is the fact that KWToken objects contain a List of associated strings that I require.

Of course, as said before, the simple solution in C# would use the "is" or "as" keyword.

I thought of a few not-so-sexy ways to do it.
Anyone have pretty ideas?

EDIT

(Printing example removed as it seemed to be misleading)
The list of synonyms in the KWToken object will be accessed in many places, through the associated Command object. Not just once for printing them as I might have implied with my example.

+4  A: 

This is very classic polymorphism. Wouldn't the following work?

abstract class Token
{
    public abstract void DoStuff();
}

class KeywordToken : Token
{
    List<string> Synonyms { get; set; }
    public override void DoStuff()
    {
        foreach (string s in Synonyms)
        {
            Console.WriteLine(s);
        }
    }
}

class GameObjectToken : Token
{
    public override void DoStuff()
    {
        // Do something else.
    }
}


// Elsewhere
foreach (var token in cmd.Toks)
{
    token.DoStuff();
}
Domenic
Ah crap. I knew I shouldn't have added that printing example. See, that list of synonyms has to be accessed many places. Not just for printing.
Rao
(Question edited)
Rao
So, just add multiple abstract methods. You say "I'll want to loop through a `Command` object's list of `Token`s and do stuff based on the kind of `Token` stored." That is exactly what polymorphism via abstract methods is meant to do: do something different depending on the object's type.
Domenic
+6  A: 

You could also try using the Visitor Pattern. Though it's probably overkill for your simple scenario.

blech
I'm sort of lagging behind in design pattern knowledge, but I did spend time reading on this pattern since yesterday, and it seems like this is the right (and most elegant) solution. Of course, as you say, it is overkill for this little situation. Gracias señor!
Rao
A: 

If you don't want to use RTTI (if it is possible I would advice you to use it) you could implement it yourselve.

#include <string>

template <class T>
class List
{};

class Token
{
    //nothing in here :(
public:
        enum TokenType
        {
                KWToken,
                GOToken
        };

        virtual TokenType Which() = 0;
};

class KWToken : public Token
{
public:
    List<std::string> Synonyms() {
        //get
        return List<std::string>();
    }
    void Synonyms(List<std::string>& value) {
        //set
    }
    virtual TokenType Which() {return Token::KWToken; }
};

class GOToken : public Token
{
        virtual TokenType Which() {return Token::GOToken; }

};

int main() {
  Token* T = GetToken();
  switch(T->What()) {
  case Token::KWToken:
     //Do fancy stuff
     static_cast<KWToken*>(T)->Synonyms();
     break;
  }
}
David Feurle
I had thought of something similar. But you would still have to use dynamic_cast, right?
Rao
@rao no you could then simply call the What method -> see the edited answer
David Feurle
I mean if T(should be a pointer) in your example is pointing to a KWToken, how would you be able to access the Synonyms member?
Rao
@rao you could use static_cast - this works without RTTI since you know the explicit type of the pointer.
David Feurle
@rao or the c-style () cast operator
David Feurle
static_cast wouldn't work as there may be a variable number of Token derived types in a vector during runtime and in an unknown order :) Thanks for the effort though. My initial solution was the same as yours, but I decided to look for better solutions that did not use casting.
Rao
@rao but if you want to do something special on the specific types of tokens the types need to be available at compile time. Maybee you are not too clear about the problem you are trying to solve
David Feurle
A: 

Microsoft's COM architecture handles this gracefully. The base class IUnknown only contains the minimum necessary functionality to get an interface, and the interface defines what the object can or cannot do. In your case, you could define an interface to the keyword list, and if that interface comes back NULL then the object does not support a keyword list. You don't need to implement COM per se, I'm just using it as an example.

You define the object in terms of what it does, not what it is.

Mark Ransom
+1  A: 

Use LINQ's OfType() to extract just the elements in the list that are of the type you want:

foreach(var kw in command.Toks.OfType<KWToken>())
{
  kw.Synonyms[...];
}
dthorpe
I think this would be considered usage of RTTI.
Brian
+1  A: 

As suggested by others, polymorphism and the Visitor pattern seem like natural fits for this problem.

In C++, it could also be done generically, using a class such as boost::variant.

Your Command class could then be defined roughly like this:

class Command
{
public:
    std::vector<boost::variant<KWToken, GOToken> > Toks;

    //...
};

and we can define a static visitor class defining the operations to perform on each object type:

class my_visitor : public boost::static_visitor<void>
{
public:
    // define the operation on KWTokens
    void operator()(KWToken& tok)  const
    {
      //...
      // since tok is well typed, we can access its Synonyms member without any problems.
      DoStuff(tok.Synonyms)
    }
    // and the operation on GOTokens
    void operator()(GOToken& tok) const
    {
      //...
    }
};

And then, given a variant v containing one of the token types, you can invoke the visitor like this:

boost::apply_visitor( my_visitor(), v );

See http://www.boost.org/doc/libs/1_44_0/doc/html/variant.html for details.

This avoids "traditional" OOP polymorphism, giving you an entirely template-based solution (using static polymorphism).

In this approach, you don't even need the common Token base class, and the concrete Token types don't need to implement any virtual functions.

In C#, you don't really have this option, and the visitor pattern (which is still the right solution) would have to be implemented using dynamic polymorphism.

jalf
+1 :) The visitor pattern seems the way to go in such cases (though my current problem is a small scenario and can make do with RTTI). Thanks for the boost oriented solution.
Rao
A: 

I don't know enough about your scenario but it might be an option to add the methods

virtual bool hasSynonyms() = 0;
virtual List<std::string> getSynomyms() { return List<std::string>; /*empty list */ }

to your Token base class.

I would do this only if there might be other tokens that might have synonyms, otherwise the visitor pattern is the way to go.

Philipp
Nah, only one Token derived type has synonyms.
Rao