Encapsulating speed types of different animals

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
#include <iostream>
#define show(variable) std::cout << #variable << " = " << variable << std::endl;

class LivingBeing {
protected:
	struct Speed {  // This design is not good.  Many speed types do not pertain to many types of beings.  How best to redesign?  
		int walkingSpeed, runningSpeed, flyingSpeed, swimmingSpeed, climbingSpeed, crawlingSpeed;
		void increase() {walkingSpeed *= 2;  runningSpeed *= 2;  flyingSpeed *= 2;  swimmingSpeed *= 2;  climbingSpeed *= 2;  crawlingSpeed *= 2;}
		void save (std::ostream& os) const {os << walkingSpeed << ' ' << runningSpeed << ' ' << flyingSpeed << ' ' << swimmingSpeed << ' ' << climbingSpeed << ' ' << crawlingSpeed << std::endl;}
		void load (std::istream& is) {is >> std::skipws >> walkingSpeed >> runningSpeed >> flyingSpeed >> swimmingSpeed >> climbingSpeed >> crawlingSpeed;}
		friend std::ostream& operator << (std::ostream&, const Speed&);
	};
private:
	Speed speed;
public:
	LivingBeing (const Speed& s) : speed(s) {}
	Speed getSpeed() const {return speed;}
	virtual int normalSpeed() const = 0;
	void increaseSpeed() {speed.increase();}
	virtual void saveSpeed (std::ostream& os) const {speed.save(os);}
	virtual void loadSpeed (std::istream& is) {speed.load(is);}
};

struct Human : LivingBeing {
	Human() : LivingBeing ({30,100,0,20,2,3}) {}
	virtual int normalSpeed() const override {return getSpeed().walkingSpeed;}
};

struct Snail : LivingBeing {
	Snail() : LivingBeing ({0,0,0,0,0,1}) {}
	virtual int normalSpeed() const override {return getSpeed().crawlingSpeed;}
};

struct Bird : LivingBeing {
	Bird() : LivingBeing ({2,0,150,0,0,0}) {}
	virtual int normalSpeed() const override {return getSpeed().flyingSpeed;}
};


int main() {
	Human* human = new Human;  Snail* snail = new Snail;  Bird* bird = new Bird;
	LivingBeing* beings[] = {human, snail, bird};
	for (LivingBeing* x : beings) {
		show(x->normalSpeed())
		x->increaseSpeed();
		show(x->normalSpeed())	
	}
}


Though the above works, memory is being wasted for speed types that do not pertain to many animals (a snail only crawls, a human never flies, etc...). Saving and loading their files will involve a lot of useless zeros. The problem seems simple, but I can't think of a good redesign to encapsulate speed properly for each of the many, many types of animals. Note that main() works with speed from the base class LivingBeing.

Should there be polymorphic Speed types within each Animal subtype? Then there will be a lot of identical Speed subtypes (e.g. many animals can only swim).

Something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class AnimalsThatOnlySwimOrCrawl : LivingBeing {
	protected:
		struct Speed : LivingBeing::Speed {
			int swimmingSpeed, crawlingSpeed;
			virtual void increase() override {swimmingSpeed *= 2;  crawlingSpeed *= 2;}
			virtual void save (std::ostream& os) const override {os << swimmingSpeed << ' ' << crawlingSpeed << std::endl;}
			virtual void load (std::istream& is) override {is >> std::skipws >> swimmingSpeed >> crawlingSpeed;}
			friend std::ostream& operator << (std::ostream&, const Speed&);
		};
	private:
		Speed speed;
		virtual int normalSpeed() const override {return speed.swimmingSpeed;}
};

struct GoldFish : AnimalsThatOnlySwimOrCrawl {};
struct Mackerel : AnimalsThatOnlySwimOrCrawl {};
???
Last edited on
This is one idea:

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

struct moves
{
    explicit constexpr moves( int s ) : speed_(s) {}
    int speed_ = 0 ;
    constexpr int speed() const { return speed_ ; }
    /* ... */
};

struct walks : moves { explicit constexpr walks( int w = 2 ) : moves(w) {} ; /* ... */ };
struct runs : moves { explicit constexpr runs( int r = 6 ) : moves(r) {} ; /* ... */ };
struct flies : moves { explicit constexpr flies( int f = 20 ) : moves(f) {} ; /* ... */ };
struct swims : moves { explicit constexpr swims( int s = 1 ) : moves(s) {} ; /* ... */ };

struct animal { virtual ~animal() = default ; virtual int normal_speed() const = 0 ;  /* ... */ };

template < typename T > struct usually : virtual animal, T
{
    usually() = default ;
    explicit usually( int s ) : T(s) {} ;
    virtual int normal_speed() const override { return T::speed() ; }
};
template < typename T > using only = usually<T> ;

struct human : usually<walks>, runs, swims { /* ... */ };
struct cheetah : usually<runs>, swims, walks { /* ... */ };
struct bluejay : usually<flies>, walks { /* ... */ };
struct trout : only<swims> { /* ... */ };

int main()
{
    human h ;
    std::cout << h.normal_speed() << ' ' << h.runs::speed() << '\n' ;

    bluejay b ;
    std::cout << b.normal_speed() << ' ' << b.walks::speed() << '\n' ;

    cheetah c ;
    trout t ;

    std::reference_wrapper< const animal > animals[] = { h, b, c, t } ;
    for( const auto& w : animals ) std::cout << w.get().normal_speed() << ' ' ;
}

http://coliru.stacked-crooked.com/a/d4232ba238ef6fba
You can use something like "movement types": vector of pairs <movement type / speed>
This will make whole inheritance unneeded if you do something like:
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
#include<iostream>
#include<utility>
#include<unordered_map>
#include<vector>


class LivingBeing
{
public:
    enum class MoveType {Walk, Run, Fly, Swim, Climb, Crawl};
    using speed_t = std::pair<const MoveType, int>;

    speed_t normal_movement() const
    { return movement[0]; }

    std::string who() const
    { return species; }

    static LivingBeing create_entity(std::string which);

protected:
    LivingBeing(std::string species_, std::vector<speed_t> moves) :
        movement(std::move(moves)), species(std::move(species_)) {}
private:
    //First  element (0th index) is main movement type
    std::vector<speed_t> movement;
    std::string species;
};


LivingBeing LivingBeing::create_entity(std::string which)
{
    //Prototype pattern
    static std::unordered_map<std::string, LivingBeing> prototypes {
        {"human", {"Human", {{MoveType::Walk, 30}, {MoveType::Run, 100},
                    {MoveType::Swim, 20}, {MoveType::Climb, 2}, {MoveType::Crawl, 3}}}},
        {"snail", {"Snail", {{MoveType::Crawl, 1}}}},
        {"bird", {"Bird", {{MoveType::Fly, 150}, {MoveType::Walk, 2}}}}
    };
    return prototypes.at(which);
}

const std::string& MoveType_to_string(LivingBeing::MoveType x)
{
    static const std::string moves[] = {
        "walking", "running", "flying", "swimming", "climbing", "crawling"
    };
    return moves[static_cast<int>(x)];
}

int main()
{
    std::vector<LivingBeing> creatures = {
        LivingBeing::create_entity("human"),
        LivingBeing::create_entity("snail"),
        LivingBeing::create_entity("bird"),
        LivingBeing::create_entity("snail"),
        LivingBeing::create_entity("human"),
    };
    for(const auto& entity: creatures)
        std::cout << "I am " << entity.who() <<
                     " who is " << MoveType_to_string(entity.normal_movement().first) <<
                     " at speed " << entity.normal_movement().second << '\n';
}
I am Human who is walking at speed 30
I am Snail who is crawling at speed 1
I am Bird who is flying at speed 150
I am Snail who is crawling at speed 1
I am Human who is walking at speed 30
Last edited on
I like both ideas, but both have pros and cons. MiiniPaa's solution avoids inheritance, but appears to be O(N) time in retrieving specific speeds:
1
2
3
4
int LivingBeing::speed (LivingBeing::MoveType m) const {
	const auto it = std::find_if (movement.begin(), movement.end(), [m](const auto& x)-> bool {return x.first == m;});
	return (it != movement.end()) ? it->second : 0;
}

JLBorges' solution thus appears to perform faster, but the saveSpeed (std::ostream&) and loadSpeed (std::istream&) functions apparently cannot be implemented in the base class (nor can retrieving or printing all speeds, or doubling all speeds, etc...), and need to be defined as overrides in each animal subtype, e.g.
1
2
3
4
5
6
7
8
9
10
11
12
13
void moves::doubleSpeed() {_speed *= 2;}

void human::doubleSpeed() {  // virtual override
	usually<walks>::doubleSpeed();  runs::doubleSpeed();  swims::doubleSpeed();
}

void human::save (std::ostream& os) const {  // virtual override	
    os << usually<walks>::speed() << ' ' << runs::speed() << ' ' << swims::speed() << '\n';
}

void human::load (std::istream& is) {  // virtual override	
	is >> std::skipws >> usually<walks>::_speed >> runs::_speed >> swims::_speed;
}

This is reminiscent of my first idea of using polymorphic Speed classes. MiiniPaa's solution can have them defined in the base class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void LivingBeing::doubleSpeed() {for (auto& x : movement) x.second *= 2;}

void LivingBeing::saveSpeed (std::ostream& os) const {
	os << movement.size() << std::endl;
	for (const auto& x : movement)
		os << x.first << ' ' << x.second << ' ';
	os << std::endl;
}

void LivingBeing::loadSpeed (std::istream& is) {
	int numSpeeds, moveType, value;
	is >> numSpeeds;
	for (int i = 0;  i < numSpeeds;  i++) {
		is >> std::skipws >> moveType >> value;
		movement.emplace_back (static_cast<MoveType>(moveType), value);
	}
}

Of course, that's just a convenience and maintainability issue rather than a performance issue. My gut tells me that there's a solution that has the best of both worlds. Perhaps use variadic templates? For example
1
2
3
4
5
6
7
8
9
template <typename... TYPES> class SpeedTypes;

template <typename FIRST, typename... REST> 
class SpeedTypes<FIRST, REST...> : public FIRST, public SpeedTypes<REST...> {};

template <> struct SpeedTypes<> {};

struct human : SpeedTypes<usually<walks>, runs, swims> {  // etc...
struct cheetah : SpeedTypes<usually<runs>, swims, walks> {  // etc... 

and then SpeedTypes<TYPES...> used in the base class to implement those functions? Or if that makes no sense, then something along those lines???
Last edited on
appears to be O(N) time in retrieving specific speeds
Well, you can save it in hash map, but it will be terrible overhead in your case. As long as you do not have more than ten speed types in specific entity, loss of perfomance is neglible.

but the saveSpeed (std::ostream&) and loadSpeed (std::istream&) functions apparently cannot be implemented in the base class
You might be able to create generic save/doubleSpeed functions using http://en.cppreference.com/w/cpp/types/is_base_of (and double dispatch if you want to use these classes through pointer to some common base)
Additionally C++1z might support compile-time reflection which would be helpful in such cases

I guess there is no solution that has the best of both worlds, huh?
Yes. Art of programming is choosing right solution for given problem. Depending on your existing code and your intent to use this class, one or both solution suggested might be unusable for you as-is.
Last edited on
Done! Using variadic templates, I've modified JLBorges' high-performance solution so that any function of all speeds can be implemented in the base class:

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

struct Speed {
    int speed = 0;
    constexpr int getSpeed() const {return speed;}
    explicit constexpr Speed (int s) : speed(s) {}
    void dubble() {speed *= 2;}
};

struct Walks : Speed { explicit constexpr Walks( int w = 2 ) : Speed(w) {} ; };
struct Runs : Speed { explicit constexpr Runs( int r = 6 ) : Speed(r) {} ; };
struct Flies : Speed { explicit constexpr Flies( int f = 20 ) : Speed(f) {} ; };
struct Swims : Speed { explicit constexpr Swims( int s = 2) : Speed(s) {} ; };

template <typename...> class SpeedTypes;

template<typename...> struct DoubleSpeed;

template <typename FIRST, typename... REST>
struct DoubleSpeed<FIRST, REST...> {
	void operator() (SpeedTypes<FIRST, REST...>* s) const {
		s->FIRST::dubble();
		DoubleSpeed<REST...>()(s);
	}
};

template <>
struct DoubleSpeed<> {
	void operator() (SpeedTypes<>*) const {}
};

template<typename...> struct SaveSpeed;

template <typename FIRST, typename... REST>
struct SaveSpeed<FIRST, REST...> {
	void operator() (std::ostream& os, const SpeedTypes<FIRST, REST...>* s) const {
		os << s->FIRST::getSpeed() << ' ';
		SaveSpeed<REST...>()(os, s);
	}
};

template <typename LAST>
struct SaveSpeed<LAST> {
	void operator() (std::ostream& os, const SpeedTypes<LAST>* s) const {
		os << s->LAST::getSpeed() << std::endl;
	}
};

template<typename...> struct LoadSpeed;

template <typename FIRST, typename... REST>
struct LoadSpeed<FIRST, REST...> {
	void operator() (std::istream& is, SpeedTypes<FIRST, REST...>* s) const {
		is >> std::skipws >> s->FIRST::speed;
		LoadSpeed<REST...>()(is, s);
	}
};

template <>
struct LoadSpeed<> {
	void operator() (std::istream&, SpeedTypes<>*) const {}
};

struct LivingBeing {
	virtual ~LivingBeing() = default;
	virtual int normal_speed() const = 0;
	virtual void doubleSpeed() = 0;
        virtual void saveSpeed (std::ostream&) const = 0;
        virtual void loadSpeed (std::istream&) = 0;
//    private:
//	template <typename... T> friend SpeedTypes<T...>;  // How to declare SpeedTypes<T...> friend so that below can be private?
	template <typename... T> void doubleSpeedBase (SpeedTypes<T...>* s) {DoubleSpeed<T...>()(s);}
	template <typename... T> void saveSpeedBase (std::ostream& os, const SpeedTypes<T...>* s) const {SaveSpeed<T...>()(os, s);}
	template <typename... T> void loadSpeedBase (std::istream& is, SpeedTypes<T...>* s) const {LoadSpeed<T...>()(is, s);}
};

template <typename FIRST, typename... REST> 
struct SpeedTypes<FIRST, REST...> : virtual public LivingBeing, virtual public FIRST, public SpeedTypes<REST...> {
	virtual void doubleSpeed() override {doubleSpeedBase<FIRST, REST...>(this);}  // The key overrides!
	virtual void saveSpeed (std::ostream& os) const override {saveSpeedBase<FIRST, REST...>(os, this);}
	virtual void loadSpeed (std::istream& is) override {loadSpeedBase<FIRST, REST...>(is, this);}
};

template <> struct SpeedTypes<> : virtual public LivingBeing {
	virtual void doubleSpeed() override {}  // LivingBeing that cannot move, so do nothing.
	virtual void saveSpeed (std::ostream&) const override {}
	virtual void loadSpeed (std::istream&) override {}
};

template < typename T > struct Usually : virtual LivingBeing, T {
    Usually() = default ;
    explicit Usually( int s ) : T(s) {} ;
    virtual int normal_speed() const override { return T::getSpeed() ; }
};

template < typename T > using Only = Usually<T> ;

struct Human : SpeedTypes<Usually<Walks>, Runs, Swims> { Human() : Usually<Walks>(4), Runs(10), Swims(3) {} };
struct Cheetah : SpeedTypes<Usually<Runs>, Swims, Walks> { Cheetah() : Usually<Runs>(50), Swims(2), Walks(5) {} };
struct Bluejay : SpeedTypes<Usually<Flies>, Walks> { Bluejay() : Usually<Flies>(12), Walks(2) {} };
struct Trout : SpeedTypes<Only<Swims>> { Trout() : Only<Swims>(4) {} };

int main() {
    Human human;
    std::cout << human.normal_speed() << ' ' << human.Runs::getSpeed() << '\n';
    Bluejay bluejay;
    std::cout << bluejay.normal_speed() << ' ' << bluejay.Walks::getSpeed() << '\n';
    Cheetah cheetah;
    Trout trout;
    std::reference_wrapper<const LivingBeing> animals[] = {human, bluejay, cheetah, trout};
    for( const auto& w : animals ) std::cout << w.get().normal_speed() << ' ';
    std::cout << "\n---------------\n\n";
    
    std::cout << human.Walks::getSpeed() << ' ' << human.Runs::getSpeed() << ' ' << human.Swims::getSpeed() << '\n';
    human.doubleSpeed();
    std::cout << "After doubling speeds:\n";
    std::cout << human.Walks::getSpeed() << ' ' << human.Runs::getSpeed() << ' ' << human.Swims::getSpeed() << '\n';
    
    const std::string path = "human.txt";
    std::ofstream outfile (path);
    human.saveSpeed(outfile);
    std::cout << "File contains:  " << std::ifstream(path).rdbuf();
    std::ifstream infile (path);
    Human h;
    h.loadSpeed(infile);
    std::cout << h.Walks::getSpeed() << ' ' << h.Runs::getSpeed() << ' ' << h.Swims::getSpeed() << '\n';
}

Output:
1
2
3
4
5
6
7
8
9
4 10
12 2
4 12 50 4

4 10 3
After doubling speeds:
8 20 6
File contains:  8 20 6
8 20 6

So it can be done! I would just like to finish it off by declaring SpeedTypes<T...> friend of LivingBeing, but I don't know the syntax for that. Update: None other than JLBorges wrote a solution for that as well, in an old thread: http://www.cplusplus.com/forum/general/120925/
Last edited on
Avoid virtual inheritance (except from animal) and friends, perhaps?

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

struct moves
{
    explicit constexpr moves( int s ) : speed_(s) {}
    int speed_ = 0 ;
    constexpr int speed() const { return speed_ ; }
    /* ... */
};

struct walks : moves { explicit constexpr walks( int w = 2 ) : moves(w) {} ; /* ... */ };
struct runs : moves { explicit constexpr runs( int r = 6 ) : moves(r) {} ; /* ... */ };
struct flies : moves { explicit constexpr flies( int f = 20 ) : moves(f) {} ; /* ... */ };
struct swims : moves { explicit constexpr swims( int s = 1 ) : moves(s) {} ; /* ... */ };

struct animal
{
    virtual ~animal() = default ;
    virtual int normal_speed() const = 0 ;
    virtual std::vector<int> all_speeds() const { return {} ; }
    virtual std::ostream& save_speeds( std::ostream& stm ) const
    { for( int s : all_speeds() ) stm << s << ' ' ; return stm ; }
};

template < typename T > struct usually : virtual animal, T
{
    usually() = default ;
    explicit usually( int s ) : T(s) {} ;
    virtual int normal_speed() const override { return T::speed() ; }
    virtual std::vector<int> all_speeds() const override { return { T::speed() } ; }
};
template < typename T > using only = usually<T> ;

template < typename... > struct speeds ;
template < typename T > struct speeds<T> : T
{
    virtual std::vector<int> all_speeds() const { return { T::speed() } ; }
};

template < typename FIRST, typename... REST > struct speeds<FIRST,REST...> : speeds<FIRST>, speeds<REST...>
{
    virtual std::vector<int> all_speeds() const override
    {
        auto a = speeds<FIRST>::all_speeds() ;
        auto b = speeds<REST...>::all_speeds() ;
        a.insert( a.end(), b.begin(), b.end() ) ;
        return a ;
    }
};

struct human : speeds< usually<walks>, runs, swims > { /* ... */ };
struct cheetah : speeds< swims, usually<runs>, walks > { /* ... */ };
struct bluejay : speeds< usually<flies>, walks > { /* ... */ };
struct trout : speeds< only<swims> > { /* ... */ };

int main()
{
    human h ; std::cout << h.normal_speed() << ' ' << h.runs::speed() << '\n' ;
    bluejay b ; std::cout << b.normal_speed() << ' ' << b.walks::speed() << '\n' ;
    cheetah c ;
    trout t ;

    std::reference_wrapper< const animal > animals[] = { h, b, c, t } ;
    for( const auto& w : animals )
    {
        const animal& a = w ;
        a.save_speeds( std::cout << typeid(a).name()+1 << " speeds: " ) ; // +1: GNU specific
        std::cout << " (usually " << a.normal_speed() << ")\n" ;
    }
}

http://coliru.stacked-crooked.com/a/d8045644ab14dd94
Last edited on
I can't get
void animal::load_speeds( std::istream& )
working in the above design. My implementation for it from my previous solution doesn't seem to work in this design. I assumed you omitted it because it is not so simple?

Update: Ok, I got it working in your design above, using what I did in my previous solution. But std::skipws was not working for some reason, so each speed had to be written and read in separate lines. And I had to put back in the same virtual inheritance as before to make it compile, sadly.
Last edited on
> I assumed you omitted it because it is not so simple?

I omitted it because a similar technique to the one for save could be used for the modify speeds operations.

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

struct moves
{
    explicit constexpr moves( int s ) : speed_(s) {}
    int speed_ = 0 ;
    constexpr int speed() const { return speed_ ; }
    void speed( int v ) { speed_  = v ; }
    /* ... */
};

struct walks : moves { explicit constexpr walks( int w = 2 ) : moves(w) {} ; /* ... */ };
struct runs : moves { explicit constexpr runs( int r = 6 ) : moves(r) {} ; /* ... */ };
struct flies : moves { explicit constexpr flies( int f = 20 ) : moves(f) {} ; /* ... */ };
struct swims : moves { explicit constexpr swims( int s = 1 ) : moves(s) {} ; /* ... */ };

struct animal
{
    virtual ~animal() = default ;
    virtual int normal_speed() const = 0 ;
    virtual std::vector<int> all_speeds() const { return {} ; }
    virtual void all_speeds( std::vector<int> ) {}

    virtual std::ostream& save_speeds( std::ostream& stm ) const
    { for( int s : all_speeds() ) stm << s << ' ' ; return stm << '\n' ; }

    virtual bool load_speeds( std::string str )
    {
        std::istringstream stm(str) ;
        std::vector<int> s ;
        int v ;
        while( stm >> v ) s.push_back(v) ;
        all_speeds(s) ;
        return bool(stm) ;
    }

    virtual std::istream& load_speeds( std::istream& stm )
    {
        std::string line ;
        if( std::getline( stm, line ) && load_speeds(line) ) return stm ;
        stm.setstate( std::ios::failbit ) ;
        return stm ;
    }

    template < typename FN > void adjust_speeds( FN&& fn )
    {
        auto s = all_speeds() ;
        for( int& v : s ) v = std::forward<FN>(fn)(v) ;
        all_speeds(s) ;
    }
};

template < typename T > struct usually : virtual animal, T
{
    usually() = default ;
    explicit usually( int s ) : T(s) {} ;
    virtual int normal_speed() const override { return T::speed() ; }
    virtual std::vector<int> all_speeds() const override { return { T::speed() } ; }
    virtual void all_speeds( std::vector<int> s ) override  { if( !s.empty() ) T::speed( s.front() ) ; }
};
template < typename T > using only = usually<T> ;

template < typename... > struct speeds ;
template < typename T > struct speeds<T> : T
{
    virtual std::vector<int> all_speeds() const { return { T::speed() } ; }
    virtual void all_speeds( std::vector<int> s ) { if( !s.empty() ) T::speed( s.back() ) ; }
};

template < typename FIRST, typename... REST > struct speeds<FIRST,REST...> : speeds<FIRST>, speeds<REST...>
{
    virtual std::vector<int> all_speeds() const override
    {
        auto a = speeds<FIRST>::all_speeds() ;
        auto b = speeds<REST...>::all_speeds() ;
        a.insert( a.end(), b.begin(), b.end() ) ;
        return a ;
    }

    virtual void all_speeds( std::vector<int> s ) override
    {
        if( !s.empty() )
        {
            speeds<FIRST>::speed( s.front() ) ;
            speeds<REST...>::all_speeds( { s.begin() + 1, s.end() } ) ;
        }
    }
};

struct human : speeds< usually<walks>, runs, swims > { /* ... */ };
struct cheetah : speeds< swims, usually<runs>, walks > { /* ... */ };
struct bluejay : speeds< usually<flies>, walks > { /* ... */ };
struct trout : speeds< only<swims> > { /* ... */ };

int main()
{
    human h ;
    animal& a = h ;
    std::cout << "normal speed: " << a.normal_speed() << '\n' ;

    a.save_speeds( std::cout << "original speeds: " ) ;

    a.load_speeds(std::cin) ;
    a.save_speeds( std::cout << "after load_speeds: " ) ;

    a.adjust_speeds( []( int v ) { return std::max( 2*v*v - 3*v + 5, 1 ) ; } ) ;
    a.save_speeds( std::cout << "after adjust_speeds: " ) ;
}

http://coliru.stacked-crooked.com/a/e2b9039a2fc0dc38

EDIT:
Removed virtual functions from speeds<>
Reason: Reduce memory footprint.

For instance, sizeof(human) is now not more than sizeof
struct h { unspecified* vtable_ptr ; int walk, run, swim ; };

And sizeof(bluejay) is now not more than sizeof
struct bj{ unspecified* vtable_ptr ; int fly, walk ; };

http://coliru.stacked-crooked.com/a/69de05375411667f
Last edited on
Thanks so much! I tried to define load_speeds myself, but couldn't think of using a stringstream helper.
'return bool(stm);' could be replaced by 'return !stm.fail();' but not 'return stm.good();', right (we don't check for eofbit flag)?

In speeds<FIRST,REST...>, I added the constructor
1
2
template <typename ARG, typename... ARGS>
speeds (ARG&& a, ARGS&&... args) : speeds<FIRST>(std::forward<ARG>(a)), speeds<REST...>(std::forward<ARGS>(args)...) {}

with using T::T; placed in speeds<T>, so then we can define
struct Human : speeds<usually<walks>, runs, swims> { Human() : speeds(3,10,2) {} };

Unless there is a better way initialize each animal subtype's speeds.

Strangely, when my GCC runs save_speeds onto a file, it does not write until the program ends, so I could not test load_speeds immediately after save_speeds. But load_speeds does work when rerunning the program, which I guess is all that matters.

I also had to add:
1
2
3
4
5
template <> struct speeds<> : public animal {
    virtual int normal_speed() const override {return 0;}
};

struct Tree : speeds<> {};

All the speed functions work on Tree (by doing nothing). sizeof(Tree) is 8.
Last edited on
> 'return bool(stm);' could be replaced by 'return !stm.fail();' but not 'return stm.good();', right?

Right.


> Unless there is a better way initialize each animal subtype's speeds

To initialise with specific speeds, a constructor with arguments is required.
Without such a constructor, we would have to write:
struct human : speeds< usually<walks>, runs, swims > { human() { all_speeds( {3,10,2} ) ; /* ... */ } };


> Strangely, when my GCC runs save_speeds onto a file, it does not write until the program ends,

flush() will commit the changes immediately. http://en.cppreference.com/w/cpp/io/basic_ostream/flush

Can be automated by tying the two streams: http://en.cppreference.com/w/cpp/io/basic_ios/tie
Topic archived. No new replies allowed.