switch/case with enum Example:
Code: Select all
struct Dog{ void move(); };
struct Fish{ void move(); };
struct Bird{ void move(); };
enum class Animals{ DOG,BIRD,FISH };
struct Animal
{
Animals type = DOG;
};
class AnimalRepo
{
public:
Dog& get_dog(){ return dog; }
Bird& get_bird(){ return bird; }
Fish& get_fish(){ return fish; }
};
void Movable::Do( const Animal& _animal )
{
switch( _animal.type )
{
case Animals::DOG:
animal_repo.get_dog().move();
break;
case Animals::BIRD:
animal_repo.get_bird().move();
break;
case Animals::FISH:
animal_repo.get_fish().move();
break;
}
}
Code: Select all
struct Dog{};
struct Fish{};
struct Bird{};
struct Animal
{
std::variant<Dog,Fish,Bird> type = Dog();
};
struct MoveVisitor
{
void operator()( Dog& _animal ){ _animal.move(); }
void operator()( Fish& _animal ){ _animal.move(); }
void operator()( Bird& _animal ){ _animal.move(); }
};
void Movable::Do( Animal& _animal )
{
std::visit( MoveVisitor, _animal_type );
}
switch/case with enum Example:
Code: Select all
struct Dog{ void move(); };
struct Fish{ void move(); };
struct Bird{ void move(); };
struct Goat{ void move(); };
enum class Animals{ DOG,BIRD,FISH,GOAT };
struct Animal
{
Animals type = DOG;
};
class AnimalRepo
{
public:
Dog& get_dog(){ return dog; }
Bird& get_bird(){ return bird; }
Fish& get_fish(){ return fish; }
Goat& get_goat(){ return goat; }
};
void Movable::Do( const Animal& _animal )
{
switch( _animal.type )
{
case Animals::DOG:
animal_repo.get_dog().move();
break;
case Animals::BIRD:
animal_repo.get_bird().move();
break;
case Animals::FISH:
animal_repo.get_fish().move();
break;
case Animals::GOAT:
animal_repo.get_goat().move();
}
}
Code: Select all
struct Dog{ void move(); };
struct Fish{ void move(); };
struct Bird{ void move(); };
struct Goat{ void move(); };
struct Animal
{
std::variant<Dog,Fish,Bird,Goat> type = Dog();
};
struct MoveVisitor
{
void operator()( Dog& _animal ){ _animal.move(); }
void operator()( Fish& _animal ){ _animal.move(); }
void operator()( Bird& _animal ){ _animal.move(); }
void operator()( Goat& _animal ){ _animal.move(); }
};
void Movable::Do( Animal& _animal )
{
std::visit( MoveVisitor, _animal_type );
}
In the switch/case version, you must maintain the switch/case cases, with variant you must maintain a list of functions matching the variant types.
The only reason I bring this up is because of an issue I ran into. I had my program compiling and running, then decided to add some system classes, also added some more messages specific to those classes. Well, I was getting errors saying that there was an error with function overloading, I can't recall now what the exact message was. Well, the issue was that I hadn't added the new message handling functions to the older classes. This can both be a relief and discouraging all at the same time. Without VS telling me exactly what is wrong, finding the errors is really difficult. Luckily, the output window had more detailed information than the Errors window.
On the bright side though, this was a compile time error. In this particular case, had it not been a compile time error, no negative side affects would have occurred because the old classes didn't need to handle the new messages.Example wrote: "missing function overloads for <list of types> in class <name of class(es)>" would have been a nice error message.
My workaround is to just define the functions that handle the messages that the current class cares about, and define an empty template function that handles the rest.
This will allow me to add new messages without having to come back to classes that are already complete. This could possibly be a "shot myself in the foot" moment though if I do add messages that the older classes could or should handle and I forget.