Converting an object to a different class

Sandy is of class Person, who converts to Muslim and takes on a new name Fatima, and has all the attributes of Sandy, with new Muslim attributes (e.g. religion == Islam, etc..). At this point, Sandy can be deleted, and Fatima, now of class Muslim will play Sandy's role henceforth. The problem is that due to her new address, all the people who knew Sandy does not know Fatima. Manually changing Sandy's address to Fatima's address for all those people who knew Sandy is clearly not an acceptable method. I looked through all the design patterns and cannot find one to solve this problem. Any suggestions on how to improve the design? Here's my simplified code showing the problem:

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
#include <iostream>
#include <string>
#include <typeinfo>

class Person {
		std::string name;
		Person* bestFriend;
	public:
		Person (const std::string& newName) : name (newName) {}
		virtual ~Person() = default;
		std::string getName() const {return name;}
		void setName (const std::string& newName) {name = newName;}
		Person* getBestFriend() const {return bestFriend;}
		void setBestFriend (Person* newBestFriend) {bestFriend = newBestFriend;}
};

class Muslim: public Person {
	public:
		Muslim (const Person& person) : Person (person) {
                          // religion = Islam; etc... 
                }
};

int main() {
	Person *mary = new Person ("Mary"), *sandy = new Person ("Sandy");
	mary->setBestFriend (sandy);
	std::cout << "sandy = " << sandy << ", " << typeid(*sandy).name() << std::endl;
	std::cout << mary->getName() << "'s best friend is " << mary->getBestFriend()->
		getName() << "." << std::endl;
	Muslim* fatima = new Muslim (static_cast<Muslim>(*sandy));
	fatima->setName ("Fatima");  // should now delete sandy, because fatima takes on every attribue of sandy
	std::cout << "fatima = " << fatima << ", " << typeid(*fatima).name() << std::endl;
	std::cout << mary->getName() << "'s best friend is " << mary->getBestFriend()->
		getName() << "." << std::endl;  // still Sandy, of course
	std::cin.get();
}

Output:
sandy = 0x32658, 6Person
Mary's best friend is Sandy.
fatima = 0x23fec0, 6Muslim
Mary's best friend is Sandy.


Of course, we want to have: Mary's best friend is Fatima,. with mary->getBestFriend()->getReligion() == Islam, etc...
How to redesign the whole thing so that this is automated (assume there are thousands of people who know her)?
Last edited on
> I looked through all the design patterns and cannot find one to solve this problem.

Proxy. http://sourcemaking.com/design_patterns/proxy

I'm not sure how the proxy pattern would work here, or are you implying composition over inheritance? Perhaps I need to study the proxy pattern more, but I did get it working using observer pattern (and keeping inheritance):
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
87
88
89
90
91
92
93
94
95
96
97
#include <iostream>
#include <string>
#include <list>
#include <typeinfo>

class Person;

class ObserverPersonInterface {
	public:
		virtual void registerObserver (Person*) = 0;
		virtual void removeObserver (Person*) = 0;
		virtual void notifyObservers() const = 0;		
};

class AddressChangeData: public ObserverPersonInterface {
	private:
		std::list<Person*> observers;  // should perhaps be a red-black tree for greater efficiency
		const Person *oldAddress;
		Person *newAddress;
	public:
		void addressChange (const Person* oldPerson, Person* newPerson) {
			oldAddress = oldPerson;
			newAddress = newPerson;
			// observers.remove (oldAddress);
			notifyObservers();
		} 
		virtual void registerObserver (Person* person) override {observers.emplace_back (person);}
		virtual void removeObserver (Person* person) override {observers.remove (person);}
	private:
		virtual inline void notifyObservers() const override;
} changeOfAddressData;

class Person {
	private:
		std::string name;
		Person* bestFriend;
	public:
		Person() {changeOfAddressData.registerObserver (this);}
		// every new Person constructed is registered to changeOfAddressData
		Person (const std::string& newName) : name (newName) {changeOfAddressData.registerObserver (this);}
		virtual ~Person() = default;
		std::string getName() const {return name;}
		void setName (const std::string& newName) {name = newName;}
		Person* getBestFriend() const {return bestFriend;}
		void setBestFriend (Person* newBestFriend) {bestFriend = newBestFriend;}
	protected:
		void notifyChangeOfAddress (const Person* oldAddress, Person* newAddress) const {
			changeOfAddressData.addressChange (oldAddress, newAddress);
			delete oldAddress;
		}
};

class Muslim: public Person {
	public:
		Muslim() = default;
		Muslim (const Person& person) : Person (person) {
			changeOfAddressData.registerObserver (this);
			notifyChangeOfAddress (&person, this);
		}
};

inline void AddressChangeData::notifyObservers() const {
	for (Person* x : observers)
	{
		if (x->getBestFriend() == oldAddress)
			x->setBestFriend (newAddress);
	}
}

int main() {
	Person *mary = new Person ("Mary"), *sandy = new Person ("Sandy");
	mary->setBestFriend (sandy);
	sandy->setBestFriend (mary);
	std::cout << "mary = " << mary << ", " << typeid(*mary).name() << std::endl;
	std::cout << "sandy = " << sandy << ", " << typeid(*sandy).name() << std::endl;
	std::cout << mary->getName() << "'s best friend is " << mary->getBestFriend()->
		getName() << "." << std::endl;
	std::cout << sandy->getName() << "'s best friend is " << sandy->getBestFriend()->
		getName() << "." << std::endl;
	Muslim* fatima = new Muslim (static_cast<Muslim>(*sandy));
	// all subscribers of changeOfAddressData notified of sandy's address change, sandy is deleted automatically
	fatima->setName ("Fatima");
	std::cout << "fatima = " << fatima << ", " << typeid(*fatima).name() << std::endl;
	std::cout << mary->getName() << "'s best friend is " << mary->getBestFriend()->
		getName() << "." << std::endl;
	std::cout << fatima->getName() << "'s best friend is " << fatima->getBestFriend()->
		getName() << "." << std::endl;
	Muslim* shazia = new Muslim (static_cast<Muslim>(*mary));
	// all subscribers of changeOfAddressData notified of mary's address change, mary is deleted automatically
	shazia->setName ("Shazia");
	std::cout << "shazia = " << shazia << ", " << typeid(*shazia).name() << std::endl;
	std::cout << shazia->getName() << "'s best friend is " << shazia->getBestFriend()->
		getName() << "." << std::endl;
	std::cout << fatima->getName() << "'s best friend is " << fatima->getBestFriend()->
		getName() << "." << std::endl;
	std::cin.get();
}


Output:
mary = 0x32600, 6Person
sandy = 0x32670, 6Person
Mary's best friend is Sandy.
Sandy's best friend is Mary.
fatima = 0x326a0, 6Muslim
Mary's best friend is Fatima.
Fatima's best friend is Mary.
shazia = 0x32670, 6Muslim
Shazia's best friend is Fatima.
Fatima's best friend is Shazia.


Despite this working, I am considering redesigning to composition over inheritance though. I can see how the above won't work if there are threads being used and addresses are being changed simultaneously.
Last edited on
> I'm not sure how the proxy pattern would work here, or are you implying composition over inheritance?

Yes. A proxy exposes the same interface as the original (a proxy must be substitutable for the original).

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
#include <iostream>

struct person
{
    virtual ~person() = default ;
    virtual std::string name() const = 0 ;
    virtual person* best_friend() const  = 0 ;
    virtual void best_friend( person* p ) = 0 ;
    virtual std::string faith() const = 0 ;

    virtual std::ostream& print( std::ostream& stm ) const
    {
        stm << "person '" << name() << "' (" << faith() << ',' << this << ")" ;
        const auto f = best_friend() ;
        if(f) stm << " with best friend: '" << f->name() << "' (" << f->faith() << ',' << f << ')' ;
        return stm << '\n' ;
    }
};

struct person_identity_proxy : person
{
    virtual std::string name() const override { return actual->name() ; }
    virtual person* best_friend() const override { return actual->best_friend() ; }
    virtual void best_friend( person* p ) override { actual->best_friend(p) ; }
    virtual std::string faith() const override { return actual->faith() ; }
    virtual std::ostream& print( std::ostream& stm ) const override { return actual->print(stm) ; }

    explicit person_identity_proxy( person* p ) : actual(p) {}
    void rebind( person* p ) { actual = p ; }

    private: person* actual ;
};

struct person_impl : person
{
    virtual std::string name() const override { return name_ ; }
    virtual person* best_friend() const override { return best_friend_ ; }
    virtual void best_friend( person* p ) override { best_friend_ = p ; }

    explicit person_impl( std::string name ) : name_(name) {}
    person_impl( std::string name, person* best_friend ) : name_(name), best_friend_(best_friend) {}

    private:
        const std::string name_ ;
        person* best_friend_ = nullptr ;
};

struct wasp : person_impl
{
    using person_impl::person_impl ;
    virtual std::string faith() const override { return "wasp" ; }
};

struct muslim : person_impl
{
    using person_impl::person_impl ;
    virtual std::string faith() const override { return "muslim" ; }
};

struct agnostic : person_impl
{
    using person_impl::person_impl ;
    virtual std::string faith() const override { return "agnostic" ; }
};

int main()
{
    wasp sandy("Sandy") ;
    person_identity_proxy proxy( &sandy ) ;

    wasp mary( "Mary", &proxy ) ;
    mary.print(std::cout) ;

    muslim fatima("fatima") ;
    proxy.rebind( &fatima ) ;
    mary.print(std::cout) ;

    agnostic shazia( "Shazia" ) ;
    proxy.rebind( &shazia ) ;
    mary.print(std::cout) ;
}

http://coliru.stacked-crooked.com/a/18b85a8cc4379c3f
Ok, the proxy method above works when mary's best friend is sandy, but it does not work when sandy's best friend is mary (when sandy turns into the Muslim fatima, fatima does not have any of sandy's former values). So I carried out the following to fix this (and I'll take silence as approval):

All the concrete Person derived classes are given copy constructors to person_impl objects. This takes care of the above problem. Instead of creating proxies explicitly in main() (which forces us to remember which proxy belong to which person), I placed PersonProxy as a data member of Person, which is instantiated automatically in the person_impl constructors. A person's proxy is then obtained by calling Person::get_proxy(). Finally, I add the template function person_impl::convert_to_new_class<NEW_TYPE> to carry out the class conversion all in one go.

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
class PersonProxy;

class Person {
	private:
		PersonProxy* proxy;
	public:
	    virtual ~Person() = default;
	    PersonProxy* get_proxy() const {return proxy;}
	    virtual std::string name() const = 0;
	    virtual void set_name (const std::string& new_name) = 0;
	    virtual Person* best_friend() const = 0;
	    virtual void best_friend (Person* p) = 0;
	    virtual std::string faith() const = 0;
	
	    virtual std::ostream& print (std::ostream& stm = std::cout) const {
	        stm << "Person '" << name() << "' (" << faith() << ", " << this << ")";
	        const auto f = best_friend();
	        if(f) stm << " with best friend: '" << f->name() << "' (" << f->faith() << ", " << f << ")";
	        return stm << std::endl;
	    }
	protected:
	    void set_proxy (PersonProxy* p) {proxy = p;}
};

class PersonProxy : public Person {
    private:
	    Person* actual;
	public:
	    explicit PersonProxy (Person* p) : actual (p) {}
	    void rebind (Person* p) {delete actual;  actual = p;}  // the former 'actual' is deleted, as it is no longer needed

	    virtual std::string name() const override {return actual->name();}
	    virtual void set_name (const std::string& new_name) override {actual->set_name (new_name);}
	    virtual Person* best_friend() const override {return actual->best_friend();}
	    virtual void best_friend (Person* p) override {actual->best_friend(p);}
	    virtual std::string faith() const override {return actual->faith();}
	    virtual std::ostream& print (std::ostream& stm = std::cout) const override {return actual->print(stm);}
};

class person_impl : public Person {
    private:
        std::string name_;
        Person* best_friend_ = nullptr;
	public:
	    explicit person_impl (std::string name) : name_(name) {create_proxy();}
	    person_impl (std::string name, Person* best_friend) : name_(name), best_friend_(best_friend) {create_proxy();}

	    virtual std::string name() const override {return name_;}
		virtual void set_name (const std::string& new_name) override {name_ = new_name;}
	    virtual Person* best_friend() const override {return best_friend_;}
	    virtual void best_friend (Person* p) override {best_friend_ = p;}
	
		template <typename NEW_TYPE>  // The all-important class conversion function
		NEW_TYPE* convert_to_new_class (const std::string& new_name = "") const {
			NEW_TYPE* new_type = new NEW_TYPE (static_cast<NEW_TYPE>(*this));
			if (new_name != "")
				new_type->set_name (new_name);  // else keep the old name
			get_proxy()->rebind (new_type);
			return new_type;
		}	    
	private:
		void create_proxy() {
	    	    PersonProxy* proxy = new PersonProxy (this);
			set_proxy (proxy);		
		}
};

class Hindu : public person_impl {
	public:
	    using person_impl::person_impl;
	    Hindu (const person_impl& p) : person_impl (p) {}  // for converting from any person_impl derived class to Hindu
	    virtual std::string faith() const override {return "Hindu";}
};

class Muslim : public person_impl {
	public:
	    using person_impl::person_impl;
	    Muslim (const person_impl& p) : person_impl (p) {}  // for converting from any person_impl derived class to Muslim
	    virtual std::string faith() const override {return "Muslim";}
};

class Agnostic : public person_impl {
	public:
	    using person_impl::person_impl;
	    Agnostic (const person_impl& p) : person_impl (p) {}  // for converting from any person_impl derived class to Agnostic
	    virtual std::string faith() const override {return "Agnostic";}
};

int main() {
    Hindu* theebika = new Hindu ("Theebika");
    Hindu* nirusha = new Hindu ("Nirusha", theebika->get_proxy());  // Nirusha's best friend is Theebika, a Hindu.
    theebika->best_friend (nirusha->get_proxy());  // Likewise, Theebika's best friend is Nirusha.
    theebika->print();
    nirusha->print();

    Muslim* fatima = theebika->convert_to_new_class<Muslim> ("Fatima");
// Muslim* fatima = new Muslim ("Fatima");  // will not work because fatima does not have the same values as theebika, e.g. her best friend is not nirusha.
    fatima->print();
    nirusha->print();  // Nirusha's best friend is now the Muslim Fatima, formerly a Hindu by the name Theebika.

    Agnostic* sandy = fatima->convert_to_new_class<Agnostic> ("Sandy");
    sandy->print();
    nirusha->print();  // Nirusha's best friend is now the Agnostic Sandy, formerly the Muslim Fatima, who herself was formerly the Hindu Theebika.
    
    Muslim* shazia = nirusha->convert_to_new_class<Muslim> ("Shazia");
    sandy->print();
    shazia->print();

    Agnostic* mary = shazia->convert_to_new_class<Agnostic> ("Mary");
    sandy->print();
    mary->print();

    delete sandy->get_proxy();
    delete mary->get_proxy();  // should Person::proxy be shared_ptr to take care of this?
    delete sandy;  delete mary;
    // delete theebika;  delete fatima;  delete nirusha;  delete shazia; have already been carried out automatically.
    std::cin.get();
}

Output:
1
2
3
4
5
6
7
8
9
10
Person 'Theebika' (Hindu, 0x33fc0) with best friend: 'Nirusha' (Hindu, 0x326e8)
Person 'Nirusha' (Hindu, 0x326d0) with best friend: 'Theebika' (Hindu, 0x32698)
Person 'Fatima' (Muslim, 0x32720) with best friend: 'Nirusha' (Hindu, 0x326e8)
Person 'Nirusha' (Hindu, 0x326d0) with best friend: 'Fatima' (Muslim, 0x32698)
Person 'Sandy' (Agnostic, 0x33fc0) with best friend: 'Nirusha' (Hindu, 0x326e8)
Person 'Nirusha' (Hindu, 0x326d0) with best friend: 'Sandy' (Agnostic, 0x32698)
Person 'Sandy' (Agnostic, 0x33fc0) with best friend: 'Shazia' (Muslim, 0x326e8)
Person 'Shazia' (Muslim, 0x32720) with best friend: 'Sandy' (Agnostic, 0x32698)
Person 'Sandy' (Agnostic, 0x33fc0) with best friend: 'Mary' (Agnostic, 0x326e8)
Person 'Mary' (Agnostic, 0x326d0) with best friend: 'Sandy' (Agnostic, 0x32698)

Last edited on
> NEW_TYPE* new_type = new NEW_TYPE (static_cast<NEW_TYPE>(*this));

This will lead to undefined behaviour.


> void rebind (Person* p) {delete actual; actual = p;} // the former 'actual' is deleted

What if a person happens to be the best friend of more than one other person?
Either make actual a non-owning raw pointer or use a smart_pointer to automatically manage life-time.


If all that is required is allow a person to be proselytized, the type object pattern would yield a simpler solution.
http://gameprogrammingpatterns.com/type-object.html
The strategy pattern would be a little more elaborate, faith as strategy would allow polymorphic faiths.

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
#include <iostream>
#include <string>

struct faith_t
{
    std::string denomination() const { return denomination_ ; }
    std::string sabbath() const { return sabbath_ ; }
    // ... other faith related stuff

    faith_t( std::string denomination, std::string sabbath = "" )
        : denomination_(denomination), sabbath_(sabbath) {}

    private:
        std::string denomination_ ;
        std::string sabbath_ ;
        // ...
};

struct person
{
    std::string name() const { return name_ ; }
    int age() const { return age_ ; }
    const person* best_friend() const { return best_friend_ ; }
    void befriend( const person* p ) { best_friend_ = p ; }
    faith_t faith() const { return faith_ ; }
    void faith( faith_t new_faith, std::string new_name = "" )
    { faith_ = new_faith ; if( !new_name.empty() ) name_ = new_name ; }

    person( std::string name, int age, const faith_t& faith, person* best_friend = nullptr )
        : name_(name), age_(age), faith_(faith), best_friend_(best_friend) {}

    private:
        std::string name_ ;
        int age_ ;
        faith_t faith_ ;
        const person* best_friend_ = nullptr ;
};

std::ostream& operator<< ( std::ostream& stm, const person& per )
{
    stm << per.name() << ' ' << per.age() << " years (" << per.faith().denomination() << ')' ;
    const auto f = per.best_friend() ;
    if(f) stm << "  best friend: " << f->name() << " (" << f->faith().denomination() << ')' ;
    return stm ;
}

int main ()
{
    person mary( "Mary", 28, { "wasp", "Sunday" } ) ;
    person deepika( "Deepika", 23, { "hindu" }, &mary ) ;
    mary.befriend(&deepika) ;
    std::cout << mary << '\n' << deepika << "\n-------------\n" ;

    mary.faith( { "agnostic" }, "Orchid" ) ;
    std::cout << mary << '\n' << deepika << "\n-------------\n" ;

    deepika.faith( { "muslim", "Friday" }, "Fatima" ) ;
    std::cout << mary << '\n' << deepika << "\n-------------\n" ;
}

http://coliru.stacked-crooked.com/a/9f9897c917dbef69

For the original problem - simulate a type change of an object without altering its identity (pointers and references continue to be valid) - an identity proxy comes in handy.

A proxy implementation with smart pointers:

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <iostream>
#include <string>
#include <memory>

struct person
{
    using pointer = std::shared_ptr<person> ;

    virtual ~person() = default ;
    virtual std::string name() const = 0 ;
    virtual int age() const = 0 ;
    virtual pointer best_friend() const  = 0 ;
    virtual void befriend( person::pointer p ) = 0 ;
    virtual std::string faith() const = 0 ;

    virtual std::ostream& print( std::ostream& stm = std::cout ) const
    {
        stm << name() << ' ' << age() << " years (" << faith() << ')' ;
        const auto f = best_friend() ;
        if(f) stm << "  best friend: '" << f->name() << "' (" << f->faith() << ')' ;
        return stm << '\n' ;
    }
};

struct person_impl : person
{
    using pointer = std::shared_ptr<person_impl> ;

    virtual std::string name() const override { return name_ ; }
    virtual int age() const override { return age_ ; }
    virtual person::pointer best_friend() const override { return best_friend_ ; }
    virtual void befriend( person::pointer p ) override { best_friend_ = p ; }

    person_impl( std::string name, int age, person::pointer best_friend = {} )
        : name_(name), age_(age), best_friend_(best_friend) {}

    private:
        const std::string name_ ;
        int age_ ;
        person::pointer best_friend_ ;

        template < typename T > pointer convert( std::string new_name )
        { return std::make_shared<T>( new_name.empty() ? name_ : new_name, age_, best_friend_ ) ; }
        friend struct person_proxy ;
};

struct person_proxy : person
{
    using pointer = std::shared_ptr<person_proxy> ;

    virtual std::string name() const override { return actual->name() ; }
    virtual int age() const override { return actual->age() ; }
    virtual person::pointer best_friend() const override { return actual->best_friend() ; }
    virtual void befriend( person::pointer p ) override { actual->befriend(p) ; }
    virtual std::string faith() const override { return actual->faith() ; }

    explicit person_proxy( person_impl::pointer p ) : actual(p) {}
    void rebind( person_impl::pointer p ) { actual = p ; }

    template< typename T, typename... ARGS > static pointer make( ARGS&&... args )
    { return std::make_shared<person_proxy>( std::make_shared<T>( std::forward<ARGS>(args)... ) ) ; }

    template < typename T > void convert( std::string new_name = "" )
    { rebind( actual->convert<T>(new_name) ) ; }

    private:
        person_impl::pointer actual ;
};

#define DEFINE_PERSON(denomination) \
struct denomination : person_impl \
{ \
    using person_impl::person_impl ; \
    virtual std::string faith() const override { return #denomination ; } \
};

DEFINE_PERSON(wasp)
DEFINE_PERSON(muslim)
DEFINE_PERSON(agnostic)
DEFINE_PERSON(hindu)

int main()
{
    auto mary = person_proxy::make<wasp>( "Mary", 28 ) ;
    auto sandy = person_proxy::make<wasp>( "Sandy", 23, mary ) ;
    mary->befriend(sandy) ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    sandy->convert<muslim>("Fatima") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    mary->convert<agnostic>("Orchid") ;
    sandy->convert<hindu>("Deepika") ;
    mary->print() ;
    sandy->print() ;
}

http://coliru.stacked-crooked.com/a/a6a2159fd42c5e9f
Last edited on
Ok, the design is almost rock-solid, except for template functions in a derived class. The problem is that templates cannot be made virtual, and here every function in the person class must be virtual in order for any derived class function to be called. The lines I added to the above code is commented:
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include <iostream>
#include <string>
#include <typeinfo>
#include <memory>

struct person
{
    virtual ~person() = default ;
    virtual std::string name() const = 0 ;
    virtual int age() const = 0 ;
    virtual std::shared_ptr<person> best_friend() const  = 0 ;
    virtual void befriend( std::shared_ptr<person> p ) = 0 ;
    virtual std::string faith() const = 0 ;
    virtual double get_pocket_change() const = 0;
    virtual void add_pocket_change (double change) = 0;
	
    virtual std::ostream& print( std::ostream& stm = std::cout ) const
    {
        stm << name() << ' ' << age() << " years (" << faith() << ')' ;
        const auto f = best_friend() ;
        if(f) stm << "  best friend: '" << f->name() << "' (" << f->faith() << ')' ;
        stm << std::endl << "pocket change = $" << get_pocket_change() << std::endl;
        return stm << '\n' ;
    }

	// buddhist functions
	virtual std::string get_mala_colour() const {return "[not applicable--does not wear a mala]";}
	template <typename T>  // Problem: templates cannot be made virtual
	void reincarnates() {std::cout << name() << " cannot reincarnate." << std::endl;}
};

struct person_impl : person
{
    virtual std::string name() const override { return name_ ; }
    virtual int age() const override { return age_ ; }
    virtual std::shared_ptr<person> best_friend() const override { return best_friend_ ; }
    virtual void befriend( std::shared_ptr<person> p ) override { best_friend_ = p ; }
    virtual double get_pocket_change() const override {return pocket_change;}
    virtual void add_pocket_change (double change) override { pocket_change += change;}
	
    person_impl( std::string name, int age, std::shared_ptr<person> best_friend = {} )
        : name_(name), age_(age), best_friend_(best_friend) {}

    private:
        const std::string name_ ;
        int age_ ;
        double pocket_change = 0;
    // pocket_change is an always-changing value which should not be in the constructor
        std::shared_ptr<person> best_friend_ ;

        template < typename T >
		std::shared_ptr<person_impl> convert ( std::string new_name ) { 
			return std::make_shared<T>( new_name.empty() ? name_ : new_name, age_, best_friend_ ) ;  // but this will not work, as pocket_change is reinitalized
		}
        friend struct person_proxy ;
};

struct person_proxy : person
{
  private:
    std::shared_ptr<person_impl> actual ;
  public:
    virtual std::string name() const override { return actual->name() ; }
    virtual int age() const override { return actual->age() ; }
    virtual std::shared_ptr<person> best_friend() const override { return actual->best_friend() ; }
    virtual void befriend( std::shared_ptr<person> p ) override { actual->befriend(p) ; }
    virtual std::string faith() const override { return actual->faith() ; }
    virtual double get_pocket_change() const override {return actual->get_pocket_change();}
    virtual void add_pocket_change (double change) override {actual->add_pocket_change (change);}

    explicit person_proxy( std::shared_ptr<person_impl> p ) : actual(p) {}
    void rebind( std::shared_ptr<person_impl> p ) { actual = p ; }

    template< typename T, typename... ARGS >
	static std::shared_ptr<person_proxy> make( ARGS&&... args )
    { return std::make_shared<person_proxy>( std::make_shared<T>( std::forward<ARGS>(args)... ) ) ; }

    template < typename T >
	void convert( std::string new_name = "" ) { 
		rebind( actual->convert<T>(new_name) ) ;
	}

	// buddhist functions
	virtual std::string get_mala_colour() const override { return actual->get_mala_colour();}
	template <typename T>  // Problem: templates cannot be made virtual
	void reincarnates() {actual->reincarnates<T>();}
};

#define DEFINE_PERSON(denomination) \
struct denomination : person_impl \
{ \
    using person_impl::person_impl ; \
    virtual std::string faith() const override { return #denomination ; } \
};

DEFINE_PERSON(wasp)
DEFINE_PERSON(muslim)
DEFINE_PERSON(agnostic)
DEFINE_PERSON(hindu)

struct buddhist : person_impl
{
  private:
	std::string mala_colour = "red";
  public:
    using person_impl::person_impl ;    
    virtual std::string faith() const override { return "buddhist" ; }

	virtual std::string get_mala_colour() const override {return mala_colour;}
	template <typename T>
	void reincarnates() {  // cannot be virtual!
		std::cout << name() << " has reincarnated to a " << typeid(T).name() << std::endl;
	}	
};

struct dog {};

int main()
{
    std::shared_ptr<person_proxy> mary = person_proxy::make<wasp>( "Mary", 28 ) ;
    std::shared_ptr<person_proxy> sandy = person_proxy::make<wasp>( "Sandy", 23, mary ) ;
    mary->befriend(sandy) ;
    mary->add_pocket_change (12.38);
    sandy->add_pocket_change (2.55);
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    sandy->convert<muslim>("Fatima") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    mary->convert<agnostic>("Orchid") ;
    sandy->convert<hindu>("Deepika") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;
    
    mary->convert<buddhist>("Priya");
    mary->print() ;
    sandy->print() ;
    std::cout << sandy->best_friend()->name() << ", a " <<
	sandy->best_friend()->faith() << ", is wearing a " << 
	sandy->best_friend()->get_mala_colour() << " mala on her wrist." << std::endl;
    sandy->best_friend()->reincarnates<dog>();  // problem line

    std::cin.get();
}

Output:
Mary 28 years (wasp) best friend: 'Sandy' (wasp)
pocket change = $12.38
Sandy 23 years (wasp) best friend: 'Mary' (wasp)
pocket change = $2.55
------------------
Mary 28 years (wasp) best friend: 'Fatima' (muslim)
pocket change = $12.38
Fatima 23 years (muslim) best friend: 'Mary' (wasp)
pocket change = $0 // oops! (but my former method doesn't have this error)
------------------
Orchid 28 years (agnostic) best friend: 'Deepika' (hindu)
pocket change = $0 // oops!
Deepika 23 years (hindu) best friend: 'Orchid' (agnostic)
pocket change = $0
------------------
Priya 28 years (buddhist) best friend: 'Deepika' (hindu)
pocket change = $0
Deepika 23 years (hindu) best friend: 'Priya' (buddhist)
pocket change = $0
Priya, a buddhist, is wearing a red mala on her wrist. // 'red' is correct
Priya cannot reincarnate. // incorrect


The last line, which is generated by
sandy->best_friend()->reincarnates<dog>();
is supposed to say: Priya has reincarnated to a Dog.
But template <typename T> void reincarnates() must be virtual to get that override called. Perhaps the visitor pattern can fix this somehow (reincarnates takes an argument, the visitor, which has template T, so then reincarnates with its template gone can be made virtual?)
Last edited on
Ok, I think I fixed the virtual template problem, using the visitor pattern (not sure if it is the best way 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
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include <iostream>
#include <string>
#include <typeinfo>
#include <memory>

struct ReincarnationVisitor;  // important class

struct person
{
    virtual ~person() = default ;
    virtual std::string name() const = 0 ;
    virtual int age() const = 0 ;
    virtual std::shared_ptr<person> best_friend() const  = 0 ;
    virtual void befriend( std::shared_ptr<person> p ) = 0 ;
    virtual std::string faith() const = 0 ;
    virtual double get_pocket_change() const = 0;
	virtual void add_pocket_change (double change) = 0;
	
    virtual std::ostream& print( std::ostream& stm = std::cout ) const
    {
        stm << name() << ' ' << age() << " years (" << faith() << ')' ;
        const auto f = best_friend() ;
        if(f) stm << "  best friend: '" << f->name() << "' (" << f->faith() << ')' ;
        stm << std::endl << "pocket change = $" << get_pocket_change() << std::endl;
        return stm << '\n' ;
    }

	// buddhist functions
	virtual std::string get_mala_colour() const {return "[not applicable--does not wear a mala]";}
//	template <typename T>  // Problem: templates cannot be made virtual
//	void reincarnates() {std::cout << name() << " cannot reincarnate." << std::endl;}
	virtual void reincarnates (const ReincarnationVisitor*) {std::cout << name() << " cannot reincarnate." << std::endl;}
};

struct person_impl : person
{
    virtual std::string name() const override { return name_ ; }
    virtual int age() const override { return age_ ; }
    virtual std::shared_ptr<person> best_friend() const override { return best_friend_ ; }
    virtual void befriend( std::shared_ptr<person> p ) override { best_friend_ = p ; }
    virtual double get_pocket_change() const override {return pocket_change;}
	virtual void add_pocket_change (double change) override { pocket_change += change;}
	
    person_impl( std::string name, int age, std::shared_ptr<person> best_friend = {} )
        : name_(name), age_(age), best_friend_(best_friend) {}

    private:
        const std::string name_ ;
        int age_ ;
        double pocket_change = 0; 
        // pocket_change is an always-changing value which should not be passed in the constructor
        std::shared_ptr<person> best_friend_ ;

        template < typename T >
		std::shared_ptr<person_impl> convert ( std::string new_name ) { 
			return std::make_shared<T>( new_name.empty() ? name_ : new_name, age_, best_friend_ ) ;  // but this will not work, as pocket_change is reinitalized
		}
        friend struct person_proxy ;
};

struct person_proxy : person
{
  private:
    std::shared_ptr<person_impl> actual ;
  public:
    virtual std::string name() const override { return actual->name() ; }
    virtual int age() const override { return actual->age() ; }
    virtual std::shared_ptr<person> best_friend() const override { return actual->best_friend() ; }
    virtual void befriend( std::shared_ptr<person> p ) override { actual->befriend(p) ; }
    virtual std::string faith() const override { return actual->faith() ; }
    virtual double get_pocket_change() const override {return actual->get_pocket_change();}
	virtual void add_pocket_change (double change) override {actual->add_pocket_change (change);}

    explicit person_proxy( std::shared_ptr<person_impl> p ) : actual(p) {}
    void rebind( std::shared_ptr<person_impl> p ) { actual = p ; }

    template< typename T, typename... ARGS >
	static std::shared_ptr<person_proxy> make( ARGS&&... args )
    { return std::make_shared<person_proxy>( std::make_shared<T>( std::forward<ARGS>(args)... ) ) ; }

    template < typename T >
	void convert( std::string new_name = "" ) {
		rebind( actual->convert<T>(new_name) ) ;
	}

	// buddhist functions
	virtual std::string get_mala_colour() const override { return actual->get_mala_colour();}
//	template <typename T>  // Problem: templates cannot be made virtual
//	void reincarnates() {actual->reincarnates<T>();}
	virtual void reincarnates (const ReincarnationVisitor* r) override {actual->reincarnates(r);}
};

#define DEFINE_PERSON(denomination) \
struct denomination : person_impl \
{ \
    using person_impl::person_impl ; \
    virtual std::string faith() const override { return #denomination ; } \
};

DEFINE_PERSON(wasp)
DEFINE_PERSON(muslim)
DEFINE_PERSON(agnostic)
DEFINE_PERSON(hindu)

struct ReincarnationVisitor {
	virtual void visit (person_impl*) const = 0;
};

template <typename T>
struct Reincarnation : ReincarnationVisitor {
	virtual void visit (person_impl* p) const {
	    std::cout << p->name() << " has reincarnated to a " << typeid(T).name() << "." << std::endl;
	}
};

struct buddhist : person_impl
{
  private:
	std::string mala_colour = "red";
  public:
    using person_impl::person_impl ;    
    virtual std::string faith() const override { return "buddhist" ; }

	virtual std::string get_mala_colour() const override {return mala_colour;}
//	template <typename T>
//	void reincarnates() {  // cannot be virtual!
//		std::cout << name() << " has reincarnated to a " << typeid(T).name() << std::endl;
//	}
	virtual void reincarnates (const ReincarnationVisitor* r) override {r->visit(this);}  // The fix!
};

struct dog {};

int main()
{
    std::shared_ptr<person_proxy> mary = person_proxy::make<wasp>( "Mary", 28 ) ;
    std::shared_ptr<person_proxy> sandy = person_proxy::make<wasp>( "Sandy", 23, mary ) ;
    mary->befriend(sandy) ;
    mary->add_pocket_change (12.38);
    sandy->add_pocket_change (2.55);
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    sandy->convert<muslim>("Fatima") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    mary->convert<agnostic>("Orchid") ;
    sandy->convert<hindu>("Deepika") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;
    
    mary->convert<buddhist>("Priya");
    mary->print() ;
    sandy->print() ;
    std::cout << sandy->best_friend()->name() << ", a " <<
	sandy->best_friend()->faith() << ", is wearing a " << 
		andy->best_friend()->get_mala_colour() << " mala on her wrist." << std::endl;
    sandy->best_friend()->reincarnates (new Reincarnation<dog>);

    std::cin.get();
}

Output:
Mary 28 years (wasp) best friend: 'Sandy' (wasp)
pocket change = $12.38
Sandy 23 years (wasp) best friend: 'Mary' (wasp)
pocket change = $2.55
------------------
Mary 28 years (wasp) best friend: 'Fatima' (muslim)
pocket change = $12.38
Fatima 23 years (muslim) best friend: 'Mary' (wasp)
pocket change = $0
------------------
Orchid 28 years (agnostic) best friend: 'Deepika' (hindu)
pocket change = $0
Deepika 23 years (hindu) best friend: 'Orchid' (agnostic)
pocket change = $0
------------------
Priya 28 years (buddhist) best friend: 'Deepika' (hindu)
pocket change = $0
Deepika 23 years (hindu) best friend: 'Priya' (buddhist)
pocket change = $0

Priya, a buddhist, is wearing a red mala on her wrist.
Priya has reincarnated to a dog. // correct!

(And I guess Deepika's best friend is the dog now, but that is beside the point ;) )
Last edited on
Two refinements:
Major: generalize visitation (can add visitors to extend functionality for any derived class of person)
Minor: transfer of pocket_change

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#include <iostream>
#include <string>
#include <typeinfo>
#include <memory>

struct person_visitor ;  // important class

struct person
{
    virtual ~person() = default ;
    virtual std::string name() const = 0 ;
    virtual int age() const = 0 ;
    virtual std::shared_ptr<person> best_friend() const  = 0 ;
    virtual void befriend( std::shared_ptr<person> p ) = 0 ;
    virtual std::string faith() const = 0 ;
    virtual double get_pocket_change() const = 0;
    virtual void add_pocket_change (double change) = 0;

    virtual std::ostream& print( std::ostream& stm = std::cout ) const
    {
        stm << name() << ' ' << age() << " years (" << faith() << ')' ;
        const auto f = best_friend() ;
        if(f) stm << "  best friend: '" << f->name() << "' (" << f->faith() << ')' ;
        stm << std::endl << "pocket change = $" << get_pocket_change() << std::endl;
        return stm << '\n' ;
    }

	virtual bool accept( person_visitor* ) = 0 ;

	// *** removed budhist-specific stuff
};

struct person_visitor {
    virtual~person_visitor() = default ;
	virtual bool visit ( person* p ) = 0 ; // *** changed person_impl* => person*
};

struct person_impl : person
{
    virtual std::string name() const override { return name_ ; }
    virtual int age() const override { return age_ ; }
    virtual std::shared_ptr<person> best_friend() const override { return best_friend_ ; }
    virtual void befriend( std::shared_ptr<person> p ) override { best_friend_ = p ; }
    virtual double get_pocket_change() const override {return pocket_change;}
	virtual void add_pocket_change (double change) override { pocket_change += change;}

    person_impl( std::string name, int age, std::shared_ptr<person> best_friend = {} )
        : name_(name), age_(age), best_friend_(best_friend) {}

    virtual bool accept( person_visitor* v ) override { return v->visit(this) ; }

    private:
        const std::string name_ ;
        int age_ ;
        double pocket_change = 0;  
        std::shared_ptr<person> best_friend_ ;

        template < typename T >
		std::shared_ptr<person_impl> convert ( std::string new_name ) {
		    auto p = std::make_shared<T>( new_name.empty() ? name_ : new_name, age_, best_friend_ ) ;
		    p->add_pocket_change(pocket_change) ; // *** added
			return  p ;
		}
        friend struct person_proxy ;
};

struct person_proxy : person
{
  private:
    std::shared_ptr<person_impl> actual ;
  public:
    virtual std::string name() const override { return actual->name() ; }
    virtual int age() const override { return actual->age() ; }
    virtual std::shared_ptr<person> best_friend() const override { return actual->best_friend() ; }
    virtual void befriend( std::shared_ptr<person> p ) override { actual->befriend(p) ; }
    virtual std::string faith() const override { return actual->faith() ; }
    virtual double get_pocket_change() const override {return actual->get_pocket_change();}
	virtual void add_pocket_change (double change) override {actual->add_pocket_change (change);}
	virtual bool accept( person_visitor* v ) { return actual->accept(v) ; } // *** added

    explicit person_proxy( std::shared_ptr<person_impl> p ) : actual(p) {}
    void rebind( std::shared_ptr<person_impl> p ) { actual = p ; }

    template< typename T, typename... ARGS >
	static std::shared_ptr<person_proxy> make( ARGS&&... args )
    { return std::make_shared<person_proxy>( std::make_shared<T>( std::forward<ARGS>(args)... ) ) ; }

    template < typename T >
	void convert( std::string new_name = "" ) {
		rebind( actual->convert<T>(new_name) ) ;
	}
};

#define DEFINE_PERSON(denomination) \
struct denomination : person_impl \
{ \
    using person_impl::person_impl ; \
    virtual std::string faith() const override { return #denomination ; } \
};

DEFINE_PERSON(wasp)
DEFINE_PERSON(muslim)
DEFINE_PERSON(agnostic)
DEFINE_PERSON(hindu)

struct buddhist : person_impl
{
  private:
	std::string mala_colour = "red";
  public:
    using person_impl::person_impl ;
    virtual std::string faith() const override { return "buddhist" ; }

	virtual std::string get_mala_colour() const {return mala_colour;}
};

struct mala_colour_visitor : person_visitor {
	virtual bool visit( person* p ) override
	{
	    auto b = dynamic_cast<buddhist*>(p) ;
	    if(b)
        {
            mala_colour = b->get_mala_colour() ;
            return true ;
        }
        else return false ;
	}
	std::string mala_colour ; // *** to hold the result
};

template < typename T > struct reincarnation_visitor : person_visitor {
	virtual bool visit( person* p ) override
	{
	    auto b = dynamic_cast<buddhist*>(p) ;
	    if(b)
        {
            std::cout << p->name() << " has reincarnated to a " << typeid(T).name() << ".\n" ;
            return true ;
        }
        else return false ;
	}
};

struct dog {};

int main()
{
    std::shared_ptr<person_proxy> mary = person_proxy::make<wasp>( "Mary", 28 ) ;
    std::shared_ptr<person_proxy> sandy = person_proxy::make<wasp>( "Sandy", 23, mary ) ;
    mary->befriend(sandy) ;
    mary->add_pocket_change (12.38);
    sandy->add_pocket_change (2.55);
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    sandy->convert<muslim>("Fatima") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    mary->convert<agnostic>("Orchid") ;
    sandy->convert<hindu>("Deepika") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    mary->convert<buddhist>("Priya");
    mary->print() ;
    sandy->print() ;

    mala_colour_visitor mcv ;
    if( sandy->best_friend()->accept(&mcv) )
    {
        std::cout << sandy->best_friend()->name() << ", a "
                   << sandy->best_friend()->faith() << ", is wearing a "
                   << mcv.mala_colour << " mala on her wrist.\n" ;
    }


	reincarnation_visitor<dog> rv ;
    sandy->best_friend()->accept(&rv);
}

g++-4.9 -std=c++11 -O2 -Wall -Wextra -pedantic-errors main.cpp && ./a.out
Mary 28 years (wasp)  best friend: 'Sandy' (wasp)
pocket change = $12.38

Sandy 23 years (wasp)  best friend: 'Mary' (wasp)
pocket change = $2.55

------------------
Mary 28 years (wasp)  best friend: 'Fatima' (muslim)
pocket change = $12.38

Fatima 23 years (muslim)  best friend: 'Mary' (wasp)
pocket change = $2.55

------------------
Orchid 28 years (agnostic)  best friend: 'Deepika' (hindu)
pocket change = $12.38

Deepika 23 years (hindu)  best friend: 'Orchid' (agnostic)
pocket change = $2.55

------------------
Priya 28 years (buddhist)  best friend: 'Deepika' (hindu)
pocket change = $12.38

Deepika 23 years (hindu)  best friend: 'Priya' (buddhist)
pocket change = $2.55

Priya, a buddhist, is wearing a red mala on her wrist.
Priya has reincarnated to a 3dog.

http://coliru.stacked-crooked.com/a/6dc9355bfa8719a6
I would also like to add that the rvalue reference overloads:
 
virtual bool accept (PersonVisitor&&) = 0;

in person,
 
virtual bool accept (PersonVisitor&& visitor) override {return visitor.visit(this);}

in person_impl, and
 
virtual bool accept (PersonVisitor&& visitor) override {return actual->accept(std::forward<PersonVisitor>(visitor));}

in person_proxy (not sure if using std::move anywhere helps much though), would allow in main() the short and sweet
 
sandy->best_friend()->accept( reincarnation_visitor<dog>() );

to be called (tested and works), since the reincarnation_visitor<dog> object will not be used again (unlike the mala_colour_visitor object).
I also put in an lvalue reference overload too (it doesn't hurt, I guess).
Last edited on
I also changed
1
2
3
4
5
6
7
8
9
10
11
12
13
struct mala_colour_visitor : person_visitor {
	virtual bool visit( person* p ) override
	{
	    auto b = dynamic_cast<buddhist*>(p) ;
	    if(b)
        {
            mala_colour = b->get_mala_colour() ;
            return true ;
        }
        else return false ;
	}
	std::string mala_colour ; 
};

to the simpler
1
2
3
4
5
6
7
8
struct buddhist_person_visitor : person_visitor { 
	virtual bool visit( person* p ) override
	{
	    buddhist_visitor = dynamic_cast<buddhist*>(p) ;
	    return buddhist_visitor;
	}
	buddhist* buddhist_visitor ;
};

which can now return all data members of buddhist, and handle any function of buddhist too. It looks like the visitor is itself a proxy!
So here is the new code now (changes indicated by comments). The output is essentially the same as before.
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#include <iostream>
#include <string>
#include <typeinfo>
#include <memory>

struct person_visitor ;

struct person
{
    virtual ~person() = default ;
    virtual std::string name() const = 0 ;
    virtual int age() const = 0 ;
    virtual std::shared_ptr<person> best_friend() const  = 0 ;
    virtual void befriend( std::shared_ptr<person> p ) = 0 ;
    virtual std::string faith() const = 0 ;
    virtual double get_pocket_change() const = 0;
    virtual void add_pocket_change (double change) = 0;

    virtual std::ostream& print( std::ostream& stm = std::cout ) const
    {
        stm << name() << ' ' << age() << " years (" << faith() << ')' ;
        const auto f = best_friend() ;
        if(f) stm << "  best friend: '" << f->name() << "' (" << f->faith() << ')' ;
        stm << std::endl << "pocket change = $" << get_pocket_change() << std::endl;
        return stm << '\n' ;
    }

	virtual bool accept( person_visitor* ) = 0 ;
};

struct person_visitor {
    virtual ~person_visitor() = default ;
	virtual bool visit ( person* p ) = 0 ;
};

struct person_impl : person
{
    virtual std::string name() const override { return name_ ; }
    virtual int age() const override { return age_ ; }
    virtual std::shared_ptr<person> best_friend() const override { return best_friend_ ; }
    virtual void befriend( std::shared_ptr<person> p ) override { best_friend_ = p ; }
    virtual double get_pocket_change() const override {return pocket_change;}
	virtual void add_pocket_change (double change) override { pocket_change += change;}

    person_impl( std::string name, int age, std::shared_ptr<person> best_friend = {} )
        : name_(name), age_(age), best_friend_(best_friend) {}

    virtual bool accept( person_visitor* v ) override { return v->visit(this) ; }
	
    private:
        const std::string name_ ;
        int age_ ;
        double pocket_change = 0;  
        std::shared_ptr<person> best_friend_ ;

        template < typename T >
		std::shared_ptr<person_impl> convert ( std::string new_name ) {
		    auto p = std::make_shared<T>( new_name.empty() ? name_ : new_name, age_, best_friend_ ) ;
		    p->add_pocket_change(pocket_change) ;
			return  p ;
		}
        friend struct person_proxy ;
};

struct person_proxy : person
{
  private:
    std::shared_ptr<person_impl> actual ;
  public:
    virtual std::string name() const override { return actual->name() ; }
    virtual int age() const override { return actual->age() ; }
    virtual std::shared_ptr<person> best_friend() const override { return actual->best_friend() ; }
    virtual void befriend( std::shared_ptr<person> p ) override { actual->befriend(p) ; }
    virtual std::string faith() const override { return actual->faith() ; }
    virtual double get_pocket_change() const override {return actual->get_pocket_change();}
    virtual void add_pocket_change (double change) override {actual->add_pocket_change (change);}
    virtual bool accept( person_visitor* v ) { return actual->accept(v) ; }
	
    explicit person_proxy( std::shared_ptr<person_impl> p ) : actual(p) {}
    void rebind( std::shared_ptr<person_impl> p ) { actual = p ; }

    template< typename T, typename... ARGS >
	static std::shared_ptr<person_proxy> make( ARGS&&... args )
    { return std::make_shared<person_proxy>( std::make_shared<T>( std::forward<ARGS>(args)... ) ) ; }

    template < typename T >
	void convert( std::string new_name = "" ) {
		rebind( actual->convert<T>(new_name) ) ;
	}
};

#define DEFINE_PERSON(denomination) \
struct denomination : person_impl \
{ \
    using person_impl::person_impl ; \
    virtual std::string faith() const override { return #denomination ; } \
};

DEFINE_PERSON(wasp)
DEFINE_PERSON(muslim)
DEFINE_PERSON(agnostic)
DEFINE_PERSON(hindu)

struct buddhist : person_impl
{
  private:
	std::string mala_colour = "red";
  public:
    using person_impl::person_impl ;
    virtual std::string faith() const override { return "buddhist" ; }

	virtual std::string get_mala_colour() const {return mala_colour;}
	template <typename T> 
	void reincarnates() {std::cout << name() << " has reincarnated to a " << typeid(T).name() << ".\n" ;}
};

struct buddhist_person_visitor : person_visitor { // *** changed
	virtual bool visit( person* p ) override
	{
	    buddhist_visitor = dynamic_cast<buddhist*>(p) ;
	    return buddhist_visitor;
	}
	buddhist* buddhist_visitor ;
};

//  struct reincarnation_visitor not needed anymore

struct dog {};
struct cat {};

int main()
{
    std::shared_ptr<person_proxy> mary = person_proxy::make<wasp>( "Mary", 28 ) ;
    std::shared_ptr<person_proxy> sandy = person_proxy::make<wasp>( "Sandy", 23, mary ) ;
    mary->befriend(sandy) ;
    mary->add_pocket_change (12.38);
    sandy->add_pocket_change (2.55);
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    sandy->convert<muslim>("Fatima") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    mary->convert<agnostic>("Orchid") ;
    sandy->convert<hindu>("Deepika") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    mary->convert<buddhist>("Priya");
    mary->print() ;
    sandy->print() ;

    if (true)
    {
        buddhist_person_visitor bv ;
        if( sandy->best_friend()->accept(&bv) )
        {
        std::cout << sandy->best_friend()->name() << ", a " << sandy->best_friend()->faith() << ", is wearing a "
            << bv.buddhist_visitor->get_mala_colour() << " mala on her wrist.\n" ;  // *** changed
        bv.buddhist_visitor->reincarnates<dog>();  // *** added (we don't need reincarnation_visitor anymore actually)
        }
    }
	
    std::cout << mary->name() << " was not deleted." << std::endl;
    // to show that mary was not deleted when bv went out of scope
    std::cin.get();
}

And let's put struct buddhist_person_visitor inside buddhist and simply call it visitor. The name buddhist::vistor seems more encapsulated to me.
Last edited on
I hope this is not too daring, but I simplified even further by letting person::accept return person* instead of bool and the visit function is now super-short. This allows a smooth transition from sandy->best_friend() to the desired type, which we can then treat like a normal person subtype. I can't really see what problem can arise from this. But I need to know if I overlooked anything before I carry out this redesign:
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include <iostream>
#include <string>
#include <typeinfo>
#include <memory>

struct person_visitor ;

struct person
{
    virtual ~person() = default ;
    virtual std::string name() const = 0 ;
    virtual int age() const = 0 ;
    virtual std::shared_ptr<person> best_friend() const  = 0 ;
    virtual void befriend( std::shared_ptr<person> p ) = 0 ;
    virtual std::string faith() const = 0 ;
    virtual double get_pocket_change() const = 0;
    virtual void add_pocket_change (double change) = 0;

    virtual std::ostream& print( std::ostream& stm = std::cout ) const
    {
        stm << name() << ' ' << age() << " years (" << faith() << ')' ;
        const auto f = best_friend() ;
        if(f) stm << "  best friend: '" << f->name() << "' (" << f->faith() << ')' ;
        stm << std::endl << "pocket change = $" << get_pocket_change() << std::endl;
        return stm << '\n' ;
    }

	virtual person* accept( const person_visitor& ) = 0 ;  //*** changed
	virtual person* accept( person_visitor&& ) = 0 ;
};

struct person_visitor {
    virtual ~person_visitor() = default ;
	virtual person* visit ( person* p ) const = 0 ;
};

struct person_impl : person
{
    virtual std::string name() const override { return name_ ; }
    virtual int age() const override { return age_ ; }
    virtual std::shared_ptr<person> best_friend() const override { return best_friend_ ; }
    virtual void befriend( std::shared_ptr<person> p ) override { best_friend_ = p ; }
    virtual double get_pocket_change() const override {return pocket_change;}
	virtual void add_pocket_change (double change) override { pocket_change += change;}

    person_impl( std::string name, int age, std::shared_ptr<person> best_friend = {} )
        : name_(name), age_(age), best_friend_(best_friend) {}

    virtual person* accept( const person_visitor& v ) override { return v.visit(this) ; }
	virtual person* accept( person_visitor&& v ) override { return v.visit(this) ; }
	
    private:
        const std::string name_ ;
        int age_ ;
        double pocket_change = 0;  
        std::shared_ptr<person> best_friend_ ;

        template < typename T >
		std::shared_ptr<person_impl> convert ( std::string new_name ) {
		    auto p = std::make_shared<T>( new_name.empty() ? name_ : new_name, age_, best_friend_ ) ;
		    p->add_pocket_change(pocket_change) ;
			return  p ;
		}
        friend struct person_proxy ;
};

struct person_proxy : person
{
  private:
    std::shared_ptr<person_impl> actual ;
  public:
    virtual std::string name() const override { return actual->name() ; }
    virtual int age() const override { return actual->age() ; }
    virtual std::shared_ptr<person> best_friend() const override { return actual->best_friend() ; }
    virtual void befriend( std::shared_ptr<person> p ) override { actual->befriend(p) ; }
    virtual std::string faith() const override { return actual->faith() ; }
    virtual double get_pocket_change() const override {return actual->get_pocket_change();}
	virtual void add_pocket_change (double change) override {actual->add_pocket_change (change);}
	virtual person* accept( const person_visitor& v ) { return actual->accept(v) ; }
	virtual person* accept( person_visitor&& v ) { return actual->accept(v) ; }
	
    explicit person_proxy( std::shared_ptr<person_impl> p ) : actual(p) {}
    void rebind( std::shared_ptr<person_impl> p ) { actual = p ; }

    template< typename T, typename... ARGS >
	static std::shared_ptr<person_proxy> make( ARGS&&... args )
    { return std::make_shared<person_proxy>( std::make_shared<T>( std::forward<ARGS>(args)... ) ) ; }

    template < typename T >
	void convert( std::string new_name = "" ) {
		rebind( actual->convert<T>(new_name) ) ;
	}
};

#define DEFINE_PERSON(denomination) \
struct denomination : person_impl \
{ \
    using person_impl::person_impl ; \
    virtual std::string faith() const override { return #denomination ; } \
};

DEFINE_PERSON(wasp)
DEFINE_PERSON(muslim)
DEFINE_PERSON(agnostic)
DEFINE_PERSON(hindu)

struct buddhist : person_impl
{
  public:
	struct visitor : person_visitor  // *** changed
	{
		virtual person* visit( person* p ) const override {return p;}
		// Can it really be this simple???
	};
  private:
	std::string mala_colour = "red", buddhist_rank = "monk";
  public:
    using person_impl::person_impl ;
    virtual std::string faith() const override { return "buddhist" ; }

	std::string get_mala_colour() const {return mala_colour;}
	void set_mala_colour (const std::string& colour) {mala_colour = colour;}
	std::string get_buddhist_rank() const {return buddhist_rank;}
	void set_buddhist_rank (const std::string& rank) {buddhist_rank = rank;}
	template <typename T> 
	void reincarnates() {std::cout << name() << " has reincarnated to a " << typeid(T).name() << ".\n" ;}
};

struct dog {};

int main()
{
    std::shared_ptr<person_proxy> mary = person_proxy::make<wasp>( "Mary", 28 ) ;
    std::shared_ptr<person_proxy> sandy = person_proxy::make<wasp>( "Sandy", 23, mary ) ;
    mary->befriend(sandy) ;
    mary->add_pocket_change (12.38);
    sandy->add_pocket_change (2.55);
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    sandy->convert<muslim>("Fatima") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    mary->convert<agnostic>("Orchid") ;
    sandy->convert<hindu>("Deepika") ;
    mary->print() ;
    sandy->print() ;
    std::cout << "------------------\n" ;

    mary->convert<buddhist>("Priya");
    mary->print() ;
    sandy->print() ;

	// The main change
    buddhist* sandy_best_friend = dynamic_cast<buddhist*>(sandy->best_friend()->accept(buddhist::visitor()));
//	buddhist* sandy_best_friend = dynamic_cast<buddhist*>(sandy->best_friend().get()); yields nullptr
    if (sandy_best_friend)
    {
	    std::cout << sandy_best_friend->name() << ", a " << sandy_best_friend->faith() << ", is wearing a "
	                   << sandy_best_friend->get_mala_colour() << " mala on her wrist.\n" ;
	    sandy_best_friend->set_mala_colour ("gold");
	    std::cout << sandy_best_friend->name() << "'s mala is now " << sandy_best_friend->get_mala_colour() << ".\n";
	    const std::string old_rank = sandy_best_friend->get_buddhist_rank();
	    sandy_best_friend->set_buddhist_rank ("Dalai Lama");
	    std::cout << sandy_best_friend->name() << "'s rank has changed from " << old_rank << " to " << 
	    	sandy_best_friend->get_buddhist_rank() << "." << std::endl;
	    sandy_best_friend->reincarnates<dog>();
    }
    std::cin.get();
}

Output:
Mary 28 years (wasp) best friend: 'Sandy' (wasp)
pocket change = $12.38

Sandy 23 years (wasp) best friend: 'Mary' (wasp)
pocket change = $2.55

------------------
Mary 28 years (wasp) best friend: 'Fatima' (muslim)
pocket change = $12.38

Fatima 23 years (muslim) best friend: 'Mary' (wasp)
pocket change = $2.55

------------------
Orchid 28 years (agnostic) best friend: 'Deepika' (hindu)
pocket change = $12.38

Deepika 23 years (hindu) best friend: 'Orchid' (agnostic)
pocket change = $2.55

------------------
Priya 28 years (buddhist) best friend: 'Deepika' (hindu)
pocket change = $12.38

Deepika 23 years (hindu) best friend: 'Priya' (buddhist)
pocket change = $2.55

Priya, a buddhist, is wearing a red mala on her wrist.
Priya's mala is now gold.
Priya's rank has changed from monk to Dalai Lama.
Priya has reincarnated to a 3dog.
Last edited on
Ok, I just realized that this new version of accept and visit will not work if we don't know sandy->best_friend()'s type, in which case it is better to use the other version of accept and visit. Then have both versions for both scenarios.
In person_impl:
1
2
3
    virtual bool accept_unknown_type( person_visitor& v ) override { return v.visit(this) ; }
    virtual person* accept_known_type( const person_visitor& v ) override { return v.return_person(this) ; }
    virtual person* accept_known_type( person_visitor&& v ) override { return v.return_person(this) ; }

Then
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
struct buddhist : person_impl
{
  public:
	struct visitor : person_visitor  // *** changed again
	{
		virtual bool visit( person* p ) override
		{
		    buddhist_visitor = dynamic_cast<buddhist*>(p) ;
		    return buddhist_visitor;
		}
		virtual person* return_person( person* p ) const override {return p;}
		buddhist* buddhist_visitor ;
	};
  private:
	std::string mala_colour = "red", buddhist_rank = "monk";
  public:
    using person_impl::person_impl ;
    virtual std::string faith() const override { return "buddhist" ; }

	std::string get_mala_colour() const {return mala_colour;}
	void set_mala_colour (const std::string& colour) {mala_colour = colour;}
	std::string get_buddhist_rank() const {return buddhist_rank;}
	void set_buddhist_rank (const std::string& rank) {buddhist_rank = rank;}
	template <typename T> 
	void reincarnates() {std::cout << name() << " has reincarnated to a " << typeid(T).name() << ".\n" ;}
};

And finally, in main():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Suppose sandy->best_friend()'s type is known
buddhist* sandy_best_friend = dynamic_cast<buddhist*>(sandy->best_friend()->accept_known_type(buddhist::visitor()));
std::cout << sandy_best_friend->name() << ", a " << sandy_best_friend->faith() << ", is wearing a "
               << sandy_best_friend->get_mala_colour() << " mala on her wrist.\n" ;
sandy_best_friend->set_mala_colour ("gold");
std::cout << sandy_best_friend->name() << "'s mala is now " << sandy_best_friend->get_mala_colour() << ".\n";
const std::string old_rank = sandy_best_friend->get_buddhist_rank();
sandy_best_friend->set_buddhist_rank ("Dalai Lama");
std::cout << sandy_best_friend->name() << "'s rank has changed from " << old_rank << " to " << 
	sandy_best_friend->get_buddhist_rank() << "." << std::endl;
sandy_best_friend->reincarnates<dog>();

// Now, let's suppose sandy->best_friend()'s type is unknown.
buddhist::visitor bv;
muslim::visitor mv;
hindu::visitor hv;
if (sandy->best_friend()->accept_unknown_type(bv)) 
	bv.buddhist_visitor->reincarnates<cat>();
else if (sandy->best_friend()->accept_unknown_type(mv))
	mv.muslim_visitor->commit_haram();
else if (sandy->best_friend()->accept_unknown_type(hv))
	hv.hindu_visitor->pray_to_ganesh();

Output:
Mary 28 years (wasp) best friend: 'Sandy' (wasp)
pocket change = $12.38

Sandy 23 years (wasp) best friend: 'Mary' (wasp)
pocket change = $2.55

------------------
Mary 28 years (wasp) best friend: 'Fatima' (muslim)
pocket change = $12.38

Fatima 23 years (muslim) best friend: 'Mary' (wasp)
pocket change = $2.55

------------------
Orchid 28 years (agnostic) best friend: 'Deepika' (hindu)
pocket change = $12.38

Deepika 23 years (hindu) best friend: 'Orchid' (agnostic)
pocket change = $2.55

------------------
Priya 28 years (buddhist) best friend: 'Deepika' (hindu)
pocket change = $12.38

Deepika 23 years (hindu) best friend: 'Priya' (buddhist)
pocket change = $2.55

Priya, a buddhist, is wearing a red mala on her wrist.
Priya's mala is now gold.
Priya's rank has changed from monk to Dalai Lama.
Priya has reincarnated to a 3dog.
Priya has reincarnated to a 3cat.


Sorry for all these posts, but revising a design can take forever, but it must be firmly solid before coding the actual program (I learned this the hard way from failed designs in previous programs).
Last edited on
The visitor pattern is used to allow "external polymorphism" - to allow users to add their own new polymorphic operations non-intrusively, from the outside.

For a dynamic down-cast, a visitor is not required: we have dynamic_cast<>() / std::dynamic_pointer_cast<>().

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <iostream>
#include <string>
#include <typeinfo>
#include <memory>

struct person
{
    virtual ~person() = default ;
    virtual std::string name() const = 0 ;
    virtual int age() const = 0 ;
    virtual std::shared_ptr<person> best_friend() const  = 0 ;
    virtual void befriend( std::shared_ptr<person> p ) = 0 ;
    virtual std::string faith() const = 0 ;
    virtual double get_pocket_change() const = 0;
    virtual void add_pocket_change (double change) = 0;

    virtual std::ostream& print( std::ostream& stm = std::cout ) const
    {
        stm << name() << ' ' << age() << " years (" << faith() << ')' ;
        const auto f = best_friend() ;
        if(f) stm << "  best friend: '" << f->name() << "' (" << f->faith() << ')' ;
        stm << std::endl << "pocket change = $" << get_pocket_change() << std::endl;
        return stm << '\n' ;
    }
};

struct person_impl : person
{
    virtual std::string name() const override { return name_ ; }
    virtual int age() const override { return age_ ; }
    virtual std::shared_ptr<person> best_friend() const override { return best_friend_ ; }
    virtual void befriend( std::shared_ptr<person> p ) override { best_friend_ = p ; }
    virtual double get_pocket_change() const override {return pocket_change;}
    virtual void add_pocket_change (double change) override { pocket_change += change;}

    person_impl( std::string name, int age, std::shared_ptr<person> best_friend = {} )
        : name_(name), age_(age), best_friend_(best_friend) {}

    private:
        const std::string name_ ;
        int age_ ;
        double pocket_change = 0;
        std::shared_ptr<person> best_friend_ ;

        template < typename T > std::shared_ptr<person_impl> convert ( std::string new_name ) {
            auto p = std::make_shared<T>( new_name.empty() ? name_ : new_name, age_, best_friend_ ) ;
            p->add_pocket_change(pocket_change) ;
            return  p ;
        }
        friend struct person_proxy ;
};

struct person_proxy : person
{
  private:
    std::shared_ptr<person_impl> actual ;
  public:
    virtual std::string name() const override { return actual->name() ; }
    virtual int age() const override { return actual->age() ; }
    virtual std::shared_ptr<person> best_friend() const override { return actual->best_friend() ; }
    virtual void befriend( std::shared_ptr<person> p ) override { actual->befriend(p) ; }
    virtual std::string faith() const override { return actual->faith() ; }
    virtual double get_pocket_change() const override {return actual->get_pocket_change();}
	virtual void add_pocket_change (double change) override {actual->add_pocket_change (change);}

    explicit person_proxy( std::shared_ptr<person_impl> p ) : actual(p) {}
    void rebind( std::shared_ptr<person_impl> p ) { actual = p ; }

    template < typename T > static std::shared_ptr<T> cast( std::shared_ptr<person> p )
    {
        auto proxy = std::dynamic_pointer_cast<person_proxy>(p) ;
        return std::dynamic_pointer_cast<T>( proxy ? proxy->actual : p ) ;
    }

    template< typename T, typename... ARGS >
	static std::shared_ptr<person_proxy> make( ARGS&&... args )
    { return std::make_shared<person_proxy>( std::make_shared<T>( std::forward<ARGS>(args)... ) ) ; }

    template < typename T >
    void convert( std::string new_name = "" ) { rebind( actual->convert<T>(new_name) ) ; }
};

#define DEFINE_PERSON(denomination) \
struct denomination : person_impl \
{ \
    using person_impl::person_impl ; \
    virtual std::string faith() const override { return #denomination ; } \
};

DEFINE_PERSON(wasp)
DEFINE_PERSON(muslim)
DEFINE_PERSON(agnostic)
DEFINE_PERSON(hindu)

struct buddhist : person_impl
{
  private:
	std::string mala_colour = "red", buddhist_rank = "monk";
  public:
    using person_impl::person_impl ;
    virtual std::string faith() const override { return "buddhist" ; }

	std::string get_mala_colour() const {return mala_colour;}
	void set_mala_colour (const std::string& colour) {mala_colour = colour;}
	std::string get_buddhist_rank() const {return buddhist_rank;}
	void set_buddhist_rank (const std::string& rank) {buddhist_rank = rank;}
	template <typename T>
	void reincarnates() {std::cout << name() << " has reincarnated to a " << typeid(T).name() << ".\n" ;}
};

struct dog {};

int main()
{
    std::shared_ptr<person_proxy> priya = person_proxy::make<buddhist>( "Priya", 28 ) ;
    std::shared_ptr<person_proxy> sandy = person_proxy::make<wasp>( "Sandy", 23, priya ) ;
    priya->befriend(sandy) ;
    sandy->print() ;

    auto sandy_best_friend = person_proxy::cast<buddhist>( sandy->best_friend() ) ;
    if(sandy_best_friend)
    {
	    std::cout << sandy_best_friend->name() << ", a " << sandy_best_friend->faith() << ", is wearing a "
	                   << sandy_best_friend->get_mala_colour() << " mala on her wrist.\n" ;
	    sandy_best_friend->set_mala_colour ("gold");
	    std::cout << sandy_best_friend->name() << "'s mala is now " << sandy_best_friend->get_mala_colour() << ".\n";
	    const std::string old_rank = sandy_best_friend->get_buddhist_rank();
	    sandy_best_friend->set_buddhist_rank ("Dalai Lama");
	    std::cout << sandy_best_friend->name() << "'s rank has changed from " << old_rank << " to " <<
	    	sandy_best_friend->get_buddhist_rank() << "." << std::endl;
	    sandy_best_friend->reincarnates<dog>();
    }
}

http://coliru.stacked-crooked.com/a/69394803c4e83bb1
Typical use of the (acyclic) visitor 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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <iostream>
#include <string>
#include <typeinfo>
#include <memory>

struct person
{
    struct visitor { virtual ~visitor() = default ; } ;

    virtual ~person() = default ;
    virtual std::string name() const = 0 ;
    virtual int age() const = 0 ;
    virtual std::shared_ptr<person> best_friend() const  = 0 ;
    virtual void befriend( std::shared_ptr<person> p ) = 0 ;
    virtual std::string faith() const = 0 ;
    virtual double get_pocket_change() const = 0;
    virtual void add_pocket_change (double change) = 0;

    virtual std::ostream& print( std::ostream& stm = std::cout ) const
    {
        stm << name() << ' ' << age() << " years (" << faith() << ')' ;
        const auto f = best_friend() ;
        if(f) stm << "  best friend: '" << f->name() << "' (" << f->faith() << ')' ;
        stm << std::endl << "pocket change = $" << get_pocket_change() << std::endl;
        return stm << '\n' ;
    }

    virtual bool accept( person::visitor& ) = 0 ;
	bool accept( person::visitor&& v ) { return accept(v) ; } // forward to virtual
};

struct person_impl : person
{
    virtual std::string name() const override { return name_ ; }
    virtual int age() const override { return age_ ; }
    virtual std::shared_ptr<person> best_friend() const override { return best_friend_ ; }
    virtual void befriend( std::shared_ptr<person> p ) override { best_friend_ = p ; }
    virtual double get_pocket_change() const override {return pocket_change;}
	virtual void add_pocket_change (double change) override { pocket_change += change;}

    person_impl( std::string name, int age, std::shared_ptr<person> best_friend = {} )
        : name_(name), age_(age), best_friend_(best_friend) {}

    private:
        const std::string name_ ;
        int age_ ;
        double pocket_change = 0;
        std::shared_ptr<person> best_friend_ ;

        template < typename T > std::shared_ptr<person_impl> convert ( std::string new_name ) {
            auto p = std::make_shared<T>( new_name.empty() ? name_ : new_name, age_, best_friend_ ) ;
            p->add_pocket_change(pocket_change) ;
            return  p ;
		}
        friend struct person_proxy ;
};

struct person_proxy : person
{
  private:
    std::shared_ptr<person_impl> actual ;
  public:
    virtual std::string name() const override { return actual->name() ; }
    virtual int age() const override { return actual->age() ; }
    virtual std::shared_ptr<person> best_friend() const override { return actual->best_friend() ; }
    virtual void befriend( std::shared_ptr<person> p ) override { actual->befriend(p) ; }
    virtual std::string faith() const override { return actual->faith() ; }
    virtual double get_pocket_change() const override {return actual->get_pocket_change();}
	virtual void add_pocket_change (double change) override {actual->add_pocket_change (change);}
	virtual bool accept( person::visitor& v ) override { return actual->accept(v) ; }

    explicit person_proxy( std::shared_ptr<person_impl> p ) : actual(p) {}
    void rebind( std::shared_ptr<person_impl> p ) { actual = p ; }

    template < typename T > static std::shared_ptr<T> cast( std::shared_ptr<person> p )
    {
        auto proxy = std::dynamic_pointer_cast<person_proxy>(p) ;
        return std::dynamic_pointer_cast<T>( proxy ? proxy->actual : p ) ;
    }

    template< typename T, typename... ARGS >
	static std::shared_ptr<person_proxy> make( ARGS&&... args )
    { return std::make_shared<person_proxy>( std::make_shared<T>( std::forward<ARGS>(args)... ) ) ; }

    template < typename T >
    void convert( std::string new_name = "" ) { rebind( actual->convert<T>(new_name) ) ; }
};

#define DEFINE_PERSON(denomination) \
struct denomination : person_impl \
{ \
    using person_impl::person_impl ; \
    virtual std::string faith() const override { return #denomination ; } \
    \
    struct visitor { virtual ~visitor() = default ; virtual bool visit( denomination* ) = 0 ; }; \
    virtual bool accept( person::visitor& pv ) override \
    { auto v = dynamic_cast<visitor*>(&pv) ;  return v ? v->visit(this) : false ; } \
};

DEFINE_PERSON(wasp)
DEFINE_PERSON(muslim)
DEFINE_PERSON(agnostic)

template < typename T > struct counting_visitor : person::visitor, T::visitor
{
    virtual bool visit( T* ) override { return ++cnt ; }
    operator int() const { return cnt ; }
    int cnt = 0 ;
};

int main()
{
    auto mary = person_proxy::make<wasp>( "Mary", 28 ) ;
    auto sandy = person_proxy::make<wasp>( "Sandy", 23, mary ) ;
    mary->befriend(sandy) ;
    auto fatima = person_proxy::make<muslim>( "Fatima", 20, sandy ) ;
    auto agnes = person_proxy::make<wasp>( "agnes", 24, fatima ) ;
    auto shazia = person_proxy::make<muslim>( "Shazia", 24, agnes ) ;
    std::shared_ptr<person> persons[] = { mary, sandy, fatima, agnes, shazia,
                                          person_proxy::make<agnostic>( "Agnes", 22 ),
                                          person_proxy::make<wasp>( "Lilly", 26 ) } ;

    struct a_new_polymorphic_operation : person::visitor, wasp::visitor, muslim::visitor, agnostic::visitor
    {
        virtual bool visit( wasp* w ) override
        { std::cout << w->name() << ": implementation for wasps\n" ; return true ; }
        virtual bool visit( muslim* m ) override
        { std::cout << m->name() << ": implementation for muslims\n" ; return true ; }
        virtual bool visit( agnostic* a ) override
        { std::cout << a->name() << ": implementation for agnostics\n" ; return true ; }
    };
    for( auto& p : persons ) p->accept( a_new_polymorphic_operation{} ) ;
    std::cout << "--------------------------\n" ;

    counting_visitor<wasp> wasp_counter ;
    for( auto& p : persons ) p->accept(wasp_counter) ;
    std::cout << "#wasps: " << wasp_counter << '\n' ;
    std::cout << "--------------------------\n" ;

    struct print_sabbath_visitor : person::visitor, wasp::visitor, muslim::visitor // ignore agnostics
    {
        virtual bool visit( wasp* w ) override { std::cout << w->name() << ": Sunday\n" ; return true ; }
        virtual bool visit( muslim* m ) override { std::cout << m->name() << ": Friday\n" ; return true ; }
    };
    for( auto& p : persons ) p->accept( print_sabbath_visitor{} ) ;
}

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