How to take care of these repetitions elegantly?

The following gives what I want, but I want to remove the repetitions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <list>

struct InventoryItem {
	virtual ~InventoryItem() {}
};

struct Weapon: public InventoryItem {};
struct Missile: public InventoryItem {};
struct Armor: public InventoryItem {};

struct Menu {
	void insert (const Weapon* weapon) {std::cout << "Menu::insert weapon called" << std::endl;}
	void insert (const Missile* missile) {std::cout << "Menu::insert missile called" << std::endl;}
	void insert (const Armor* armor) {std::cout << "Menu::insert armor called" << std::endl;}
} menu, anotherMenu, yetAnotherMenu;

int main() {
	std::list<Weapon*> weapons = {new Weapon, new Weapon, new Weapon};
	std::list<Armor*> armor = {new Armor, new Armor, new Armor};
	std::list<Missile*> missiles = {new Missile, new Missile, new Missile};
/*	[Won't compile because of ambiguity]
	auto insertInMenus = [&](const InventoryItem* item)->void {
		menu.insert(item);
		// long code that uses anotherMenu.insert(x), yetAnotherMenu.insert(x), etc...
	};  
*/
	for (const Weapon* x: weapons)
	{
		menu.insert(x);
		// long code that uses anotherMenu.insert(x), yetAnotherMenu.insert(x), etc...
	}
	for (const Missile* x: missiles)
	{
		menu.insert(x);
		// long code identical to the above (but uses a different overload of Menu::insert because x is Missile*)
	}
	for (const Armor* x: armor)
	{
		menu.insert(x);
		// long code identical to the above (but uses a different overload of Menu::insert because x is Armor*)
	}
	std::cin.get();
}


My ideal solution was to use the local function
1
2
3
4
	auto insertInMenus = [&](const InventoryItem* item)->void {
		menu.insert(item);
		// long code that uses anotherMenu.insert(x), yetAnotherMenu.insert(x), etc...
	}; 

in each for-loop. That would have taken care of the repetitions nicely. But it won't compile because of ambiguity.

Note: the three overloads of Menu::insert are much more complex than above.
Last edited on
So I tried using virtual methods, which works. But the amount of repetition is just as much. It is only a tad better because now main() looks more elegant and shorter. Still not content with this though.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <list>

struct InventoryItem {
	virtual ~InventoryItem() {}
	virtual void insertInMenus() const = 0;
};

struct Weapon: public InventoryItem {
	virtual void insertInMenus() const;
};

struct Missile: public InventoryItem {
	virtual void insertInMenus() const;
};

struct Armor: public InventoryItem {
	virtual void insertInMenus() const;
};

struct Menu {
	void insert (const Weapon* weapon) {std::cout << "Menu::insert weapon called" << std::endl;}
	void insert (const Missile* missile) {std::cout << "Menu::insert missile called" << std::endl;}
	void insert (const Armor* armor) {std::cout << "Menu::insert armor called" << std::endl;}
} menu, anotherMenu, yetAnotherMenu;

void Weapon::insertInMenus() const {
	menu.insert (this);
	// long code that uses anotherMenu.insert (this), yetAnotherMenu.insert (this), etc...
}

void Missile::insertInMenus() const {
	menu.insert (this);
	// long code identical to the above (but uses a different overload of Menu::insert because this is Missile
}

void Armor::insertInMenus() const {
	menu.insert (this);
	// long code identical to the above (but uses a different overload of Menu::insert because this is Armor
}
	
int main() {
	std::list<Weapon*> weapons = {new Weapon, new Weapon, new Weapon};
	std::list<Armor*> armor = {new Armor, new Armor, new Armor};
	std::list<Missile*> missiles = {new Missile, new Missile, new Missile};
	for (const Weapon* x: weapons)
		x->insertInMenus();
	for (const Missile* x: missiles)
		x->insertInMenus();
	for (const Armor* x: armor)
		x->insertInMenus();
	std::cin.get();
}


How to remove the repetitions and have it appear only once?
Last edited on
Is this the best solution???
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct InventoryItem {
	virtual ~InventoryItem() {}
};

struct Weapon: public InventoryItem {};
struct Missile: public InventoryItem {};
struct Armor: public InventoryItem {};

struct Menu {
	void insert (const InventoryItem* item) {
	    if (typeid (*item) == typeid (Weapon)) {std::cout << "Menu::insert weapon called" << std::endl;}
	    else if (typeid (*item) == typeid (Missile)) {std::cout << "Menu::insert missile called" << std::endl;}
	    else if (typeid (*item) == typeid (Armor)) {std::cout << "Menu::insert armor called" << std::endl;}
	} 
} menu, anotherMenu, yetAnotherMenu;

int main() {
	std::list<Weapon*> weapons = {new Weapon, new Weapon, new Weapon};
	std::list<Armor*> armor = {new Armor, new Armor, new Armor};
	std::list<Missile*> missiles = {new Missile, new Missile, new Missile};
	auto insertInMenus = [&](const InventoryItem* item) {
		menu.insert (item);
		//	And now the long code that was being repeated:
		// ...
		anotherMenu.insert (item);
		// ...
		yetAnotherMenu.insert (item);	
		// ...
	};
	for (const Weapon* x: weapons)
		insertInMenus(x);
	for (const Missile* x: missiles)
		insertInMenus(x);
	for (const Armor* x: armor)
		insertInMenus(x);
	std::cin.get();
}

It gives the correct output. But I've been told that virtual methods is better than using multiple if-statements (that were prevalent in the 80's before virtual methods came around), and downgrades performance due to the constant multiple checking.
Last edited on
If there would be several more entities like Menu: Visitor + CRTP
http://www.oodesign.com/visitor-pattern.html
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Curiously_Recurring_Template_Pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <list>

struct Weapon ;
struct Missile ;
struct Armour ;

struct inventory_visitor { // Visitor
    virtual ~inventory_visitor() = default ;
    virtual void visit( const Weapon* ) = 0 ;
    virtual void visit( const Missile* ) = 0  ;
    virtual void visit( const Armour* ) = 0 ;
};

struct InventoryItem {
    virtual ~InventoryItem() = default ;
    virtual void accept( inventory_visitor& visitor ) const = 0;
};

template < typename DERIVED > struct InventoryItem_impl : InventoryItem { // CRTP
    virtual void accept( inventory_visitor& visitor ) const override {
        visitor.visit( dynamic_cast<const DERIVED*>(this) ) ;
	}
};

struct Weapon: InventoryItem_impl<Weapon> {} ;
struct Missile: InventoryItem_impl<Missile> {} ;
struct Armour: InventoryItem_impl<Armour> {} ;

struct Menu : inventory_visitor
{
    virtual void visit( const Weapon* ) override { std::cout << "Menu::insert weapon\n" ; }
    virtual void visit( const Missile* ) override { std::cout << "Menu::insert missile\n" ; }
    virtual void visit( const Armour* ) override { std::cout << "Menu::insert armor\n"; }
} ;

struct ListBox : inventory_visitor
{
    virtual void visit( const Weapon* ) override { std::cout << "ListBox::append weapon\n" ; }
    virtual void visit( const Missile* ) override { std::cout << "ListBox::append missile\n" ; }
    virtual void visit( const Armour* ) override { std::cout << "ListBox::append armor\n"; }
} ;

struct Arsenal : inventory_visitor
{
    virtual void visit( const Weapon* ) override { std::cout << "Arsenal::store weapon\n" ; }
    virtual void visit( const Missile* ) override { std::cout << "Arsenal::store missile\n" ; }
    virtual void visit( const Armour* ) override { std::cout << "Arsenal::store armor\n"; }
} ;

int main() {

    Menu menu ;
    ListBox lb ;
    Arsenal a ;

    std::list<Weapon*> weapons = {new Weapon, new Weapon };
    std::list<Armour*> armours = {new Armour, new Armour };
    std::list<Missile*> missiles = {new Missile, new Missile, new Missile };

    for( auto p : weapons ) { p->accept(menu) ; p->accept(lb) ; p->accept(a) ; }
    std::cout << '\n' ;

    for( auto p : armours ) { p->accept(menu) ; p->accept(lb) ; p->accept(a) ; }
    std::cout << '\n' ;

    for( auto p : missiles ) { p->accept(menu) ; p->accept(lb) ; p->accept(a) ; }
}

http://coliru.stacked-crooked.com/a/bde8c9eae0d2f142
Topic archived. No new replies allowed.