Help me generalize this function please?

I'm trying to generalize the Person::insert<T> function to all T values. Note: it uses pointers to data members.
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 <map>
#include <typeinfo>

struct Data {
	int numFriends = 0, numAcquaintances = 0, numDates = 0;  // etc...
};

enum Country {USA, Canada, France};
enum Personality {Serious, Lazy, Funny};

class Religion {} *Islam = new Religion, *Hindu = new Religion;

class Person {
private:
	std::map<Country, Data> countryStats;
	std::map<Personality, Data> personalityStats;
	std::map<Religion*, Data> religionStats;

	template<typename T>
	std::map<T, Data>& statsMap() {
		if (typeid(T) == typeid(Country)) {return countryStats;}
//		else if (typeid(T) == typeid(Personality)) return personalityStats;  // these won't compile ???
//		else if (typeid(T) == typeid(Religion)) return religionStats;
	}
public:
	template<typename T>
	void insert (const T& key, int Data::*dataPtr, int num) {statsMap<T>()[key].*dataPtr += num;}
	
	template<typename T>
	void insertData (const T& key, const Data& data) {statsMap<T>()[key] = data;}

	void showAllStats() {  // for testing the results only (but only works for Country)
		for (int COUNTRY = USA; COUNTRY <= France; COUNTRY++) {
			auto it = statsMap<Country>().find (static_cast<Country>(COUNTRY));
			if (it != statsMap<Country>().end()) {
		 		std::cout << "Country " << COUNTRY << ": " << it->second.numFriends << " friends" << std::endl;
		 		std::cout << "Country " << COUNTRY << ": " << it->second.numAcquaintances << " acquaintances" << std::endl;
		 		std::cout << "Country " << COUNTRY << ": " << it->second.numDates << " dates" << std::endl;
		 	}
		}
	}
};

int main() {
	Person bob, sam;

	bob.insert (USA, &Data::numFriends, 10);
	bob.insert (Canada, &Data::numFriends, 100);
	bob.insert (Canada, &Data::numDates, 5);
	sam.insert (USA, &Data::numFriends, 30);
	sam.insert (USA, &Data::numAcquaintances, 50);
	
//	bob.insert (Funny, &Data::numAcquaintances, 20);  // won't compile
//	sam.insert (Islam, &Data::numFriends, 35);  // won't compile
	std::cout << "Bob:" << std::endl;  bob.showAllStats();  // correct results, but only for Country
	std::cout << "Sam:" << std::endl;  sam.showAllStats();  // correct (and different from Bob)
	std::cin.get();
}
Last edited on
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
#include <iostream>
#include <map>

struct Data {
    int numFriends = 0, numAcquaintances = 0, numDates = 0;  // etc...
};

void print_tabbed(std::ostream& os, const Data& d)
{
    os << '\t' << d.numAcquaintances << " acquaintances\n";
    os << '\t' << d.numDates << " dates\n";
    os << '\t' << d.numFriends << " friends\n";
}

enum Country { USA, Canada, France };
enum Personality { Serious, Lazy, Funny };

class Religion {} *Islam = new Religion, *Hindu = new Religion;

class Person {
private:
    std::map<Country, Data> countryStats;
    std::map<Personality, Data> personalityStats;
    std::map<Religion*, Data> religionStats;

    // overloads
    std::map<Country, Data>&     statsMap(const Country&)     { return countryStats; }
    std::map<Personality, Data>& statsMap(const Personality&) { return personalityStats; }
    std::map<Religion*, Data>&   statsMap(const Religion*)    { return religionStats; }

public:
    template<typename T>
    void insert(const T& key, int Data::*dataPtr, int num) 
    {     
        statsMap(key)[key].*dataPtr += num; 
    }

    template<typename T>
    void insertData(const T& key, const Data& data) { statsMap(key)[key] = data; }

    template <typename map_type>
    void print_map(std::ostream& os, map_type& m, bool blank_line = true)
    {
        for (auto& e : m)
        {
            os << e.first << '\n';
            print_tabbed(os, e.second);
        }

        if (!m.empty() && blank_line)
            os << '\n';
    }

    void showAllStats() {  // for testing the results only (but only works for Country)
        print_map(std::cout, countryStats);
        print_map(std::cout, personalityStats);
        print_map(std::cout, religionStats, false);

        std::cout << '\n';
    }
};

int main() {
    Person bob, sam;

    bob.insert(USA, &Data::numFriends, 10);
    bob.insert(Canada, &Data::numFriends, 100);
    bob.insert(Canada, &Data::numDates, 5);
    sam.insert(USA, &Data::numFriends, 30);
    sam.insert(USA, &Data::numAcquaintances, 50);

    bob.insert (Funny, &Data::numAcquaintances, 20);  // won't compile
    sam.insert (Islam, &Data::numAcquaintances, 35);  // won't compile
    std::cout << "Bob:" << std::endl;  bob.showAllStats();  // correct results, but only for Country
    std::cout << "Sam:" << std::endl;  sam.showAllStats();
    std::cin.get();
}


http://ideone.com/rkMmaW
1
2
3
        print_map(std::cout, countryStats);
        print_map(std::cout, personalityStats);
        print_map(std::cout, religionStats, false);

This section here in your solution, how to put this into a loop? For example, in the form
1
2
for (int TYPE = FIRST_MAP; TYPE <= LAST_MAP; ++TYPE)
    print_map(std::cout, DataMap[TYPE]);

where DataMap[0] = countryStats, DataMap[1] = personalityStats, DataMap[2] = religionStats, etc... if that is even possible.
I've reached the stage in my program where I need to loop these as well because they are many such maps. But all the maps are of different types, so how do I contain these maps for iterating the maps countryStats, personalityStats, religionStats, etc... ? (not iterating through these maps but iterating the maps themselves)
Last edited on
One way would be to homogenize the key types for the maps and stick them in a container.

A std::vector<std::map<std::string, DATA>> where you could use position in the vector to signify Country, Personality or Religion and the keys for the maps at each position would be the textual representation for a specific Country, Personality or Religion. This assumes you can represent each key type this simplistically though. If the key types are more complex an additional layer of indirection may be required.
> how to put this into a loop? For example, in the form
1
2
> for (int TYPE = FIRST_MAP; TYPE <= LAST_MAP; ++TYPE)
>     print_map(std::cout, DataMap[TYPE]);


Hasn't this been discussed earlier?

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
#include <iostream>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
#include <map>

enum Country { USA, Canada, France } ;
std::ostream& operator<< ( std::ostream& stm, Country c ) 
{ return stm << ( c == USA ? "USA" : ( c == Canada ? "Canada" : "France" ) ) ; }

enum Personality { Serious, Lazy, Funny } ;
struct Religion {} islam, hindu, *Islam = &islam, *Hindu = &hindu ;

struct Person 
{
    using Data = int ;
    
    std::map<Country, Data> countryStats = { {USA,123}, {Canada,9999}, {France,-23} };
    std::map<Personality, Data> personalityStats = { {Serious,0}, {Lazy,-1}, {Funny,+1} };
    std::map<Religion*, Data> religionStats = { { Islam,1}, {Hindu,2} } ;

    // overloads
    std::map<Country, Data>&     statsMap(const Country&)     { return countryStats; }
    std::map<Personality, Data>& statsMap(const Personality&) { return personalityStats; }
    std::map<Religion*, Data>&   statsMap(const Religion*)    { return religionStats; }
};

struct print_it
{
    template < typename MAP > void operator() ( const MAP& map ) const 
    { 
        for( const auto& pair : map ) std::cout << '{' << pair.first << ": " << pair.second << "} " ;
        std::cout << '\n' ;
    }
};

Person per ;

struct do_something_with_it
{
    template < typename T > void operator() ( const T& t ) const 
    { print_it() ( per.statsMap(t) ) ; } // for example, print it 
};

int main()
{
    // http://www.boost.org/doc/libs/1_55_0/libs/mpl/doc/refmanual/sequences.html
    using types = boost::mpl::vector< Country, Personality, Religion* > ;
    
    // http://www.boost.org/doc/libs/1_55_0/libs/mpl/doc/refmanual/for-each.html
    boost::mpl::for_each<types>( do_something_with_it() ) ;
}

http://coliru.stacked-crooked.com/a/d366803ccaa9aef5
->Hasn't this been discussed earlier?

Yes, you've referred me to boost::mpl before, and I now have boost in place. But I'm really overwhelmed by the size of the library, and don't know where to start reading. I assume the boost library is much larger than the c++11 library of classes. How to remember so much and then recognize which class is needed for a particular problem? How many years will that take? And how to know which classes are the most imporant and which classes will later be discarded?
Last edited on
> But I'm really overwhelmed by the size of the library

Boost is not a single library; it is a collection of dozens of different libraries. Programmers who use Boost learn to use those parts of Boost that are useful for the kind of programs that they write.

When I encounter a problem, for which the standard library or the few Boost libraries that I'm familiar with have no ready solution, I look at the list of Boost libraries. http://www.boost.org/doc/libs/?view=categorized

If I find a library that could be handily used, I have to read up, write toy programs, experiment, see if I can tap someone who has already used that library - in short, learn how to use that library before I can start using it in production code. Almost always, it turns out to be a lot faster (and far less error-prone) than trying to reinvent the wheel on my own.
Topic archived. No new replies allowed.