Virtual template workaround--can't find solution here.

My goal is to have "Wolf::chooseAction() called." in main(). The problems are explained in the comments:
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <iostream>
#include <memory>
#include <unordered_map>
#include <string>

struct LivingBeing {
	std::string name;
	LivingBeing() = default;
	LivingBeing (const std::string& n) : name(n) {}
    virtual ~LivingBeing() = default;
    virtual void chooseAction() = 0;
};

struct Wizard : LivingBeing {
	using LivingBeing::LivingBeing;
	virtual void chooseAction() override {}	
};

struct Monster : virtual LivingBeing {
	using LivingBeing::LivingBeing;
	virtual void chooseAction() override {std::cout << "Monster::chooseAction() called.\n";}
};

struct Wolf : virtual Monster {
	using Monster::Monster;
	virtual void chooseAction() override {std::cout << "Wolf::chooseAction() called.\n";}
};

struct SummonedMonsterStrategy {
	struct Exemplar {};
	LivingBeing* summonedMonster;
	static std::unordered_map<std::string, SummonedMonsterStrategy*> prototypesMap;

	SummonedMonsterStrategy (LivingBeing* monster) : summonedMonster(monster) {}
	SummonedMonsterStrategy (Exemplar, const std::string& name) {insertInPrototypesMap (name, this);}
	virtual ~SummonedMonsterStrategy() = default;
	static const std::unordered_map<std::string, SummonedMonsterStrategy*>& getPrototypesMap() {return prototypesMap;}
	virtual void execute() = 0;
	static void insertInPrototypesMap (const std::string& tag, SummonedMonsterStrategy* strategy) {
		prototypesMap.emplace (tag, strategy);
		std::cout << "{" << tag << ", " << strategy << "} inserted in SummonedMonsterStrategy::prototypesMap." << std::endl;
	}
};
std::unordered_map<std::string, SummonedMonsterStrategy*> SummonedMonsterStrategy::prototypesMap;

struct AttackEnemiesOfSummoner : SummonedMonsterStrategy {
	static const AttackEnemiesOfSummoner prototype;
	using SummonedMonsterStrategy::SummonedMonsterStrategy;
	AttackEnemiesOfSummoner (Exemplar e) : SummonedMonsterStrategy (e, "Attack Enemies Of Summoner") {}
	virtual void execute() override;
};
const AttackEnemiesOfSummoner AttackEnemiesOfSummoner::prototype (Exemplar{});

struct SummonedMonster : virtual Monster {
	LivingBeing* summoner;
	SummonedMonster (LivingBeing* s) : summoner(s) {}
};

template <typename T>
struct Summoned : SummonedMonster, T {  // Adapter Pattern
	std::shared_ptr<SummonedMonsterStrategy> summonedMonsterStrategy;
	Summoned (LivingBeing* summoner, const std::string& name) : LivingBeing(name), SummonedMonster(summoner) {
		summonedMonsterStrategy = std::make_shared<AttackEnemiesOfSummoner>(this);
	}
//	virtual void chooseAction() override {T::chooseAction();}  // What I originally had, which worked, but fails to incorporte 'summonedMonsterStrategy'.
	virtual void chooseAction() override {summonedMonsterStrategy->execute();}  // This I want to use now.
};

void AttackEnemiesOfSummoner::execute() {
//	summonedMonster->chooseAction();  // This creates an infinite loop back to itself.
	dynamic_cast<Monster*>(summonedMonster)->Monster::chooseAction();
/*  Would like to use 'T::chooseAction();' instead but since execute() is virtual it cannot get the template T.
SummonedMonsterStrategy itself can be a template class, but then the problem is
	 const AttackEnemiesOfSummoner AttackEnemiesOfSummoner::prototype (Exemplar{});
will have to change to
         const AttackEnemiesOfSummoner<T> AttackEnemiesOfSummoner<T>::prototype (Exemplar{});
which won't be instantiated unless ALL
Monster subtypes are used for T, and there are too many of them (and too many derived types of SummonedMonsterStrategy too).*/
}

int main() {
	Wizard wizard ("Wizard");
	Summoned<Wolf> summonedWolf (&wizard, "Summoned Wolf");
	summonedWolf.chooseAction();  // Monster::chooseAction() called.
}
//  Want to have "Wolf::chooseAction() called." to be the output though. 

The standard Visitor Pattern as a workaround for virtual template functions doesn't seem to work here, or does it? Any solution to this other than making SummonedMonsterStrategy itself a template class, which runs into severe problems described in my comments? I need to have the 'prototypesMap', and the Adapter Pattern is doing a good job in my program currently.
Last edited on
I guess there is no solution without redesigning, huh? The design was working perfectly until I decided yesterday to generalize
virtual void chooseAction() override {T::chooseAction();}
with
virtual void chooseAction() override {summonedMonsterStrategy->execute();}
to account for different strategy values. During the design, I didn't even have the Strategy Pattern used there, and now I have to redesign after placing it. How was I supposed to predict that???
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
struct SummonedMonster : virtual Monster {
	LivingBeing* summoner;
	SummonedMonster (LivingBeing* s) : summoner(s) {}
	////////////////////////////////////////////////////////////////////////
	virtual void do_choose_action() = 0 ; // **** added ****
	////////////////////////////////////////////////////////////////////////
};

template <typename T>
struct Summoned : SummonedMonster, T {  // Adapter Pattern
	std::shared_ptr<SummonedMonsterStrategy> summonedMonsterStrategy;
	Summoned (LivingBeing* summoner, const std::string& name) : LivingBeing(name), SummonedMonster(summoner) {
		summonedMonsterStrategy = std::make_shared<AttackEnemiesOfSummoner>(this);
	}
	virtual void chooseAction() override {summonedMonsterStrategy->execute();}  // This I want to use now.
	////////////////////////////////////////////////////////////////////////
	virtual void do_choose_action() override {T::chooseAction();} // **** added ****
	////////////////////////////////////////////////////////////////////////
};

void AttackEnemiesOfSummoner::execute() {
    ////////////////////////////////////////////////////////////////////////
	dynamic_cast<SummonedMonster*>(summonedMonster)->do_choose_action() ; // **** modified ****
    ////////////////////////////////////////////////////////////////////////
}

http://coliru.stacked-crooked.com/a/d70de9843514e579
Oh, lord! A simple virtual helper was all that was needed, and I actually gave up too. The tricky part was realizing that they dynamic type was actually known. You are forever in my debt JLBorges, as usual.
Last edited on
Topic archived. No new replies allowed.