
syvyi <alexander.svk <at> gmail.com> writes:
What if the composite key should be parameterized from a file?
OK, this can be done, but you need to move some of the container definition components from compile time to run time. The following typedef std::function<boost::any(const person&)> any_person_extractor; defines a polymorhpic key extractor on person: something that takes a person and return something (which we wrap in a boost::any object, since we won't know what this something is until run time.) Similarly, typedef std::function< bool(const boost::any&,const boost::any&)> any_compare; is a polymorphic wraper for compare objects taking boost::any's: each compare objects we feed into any_compare will have to know what's inside these boost::any's to do the proper comparison, which we can do with template<typename T> struct any_less { bool operator()(const boost::any& x,const boost::any& y)const { return boost::any_cast<T>(x)<boost::any_cast<T>(y); } }; As composite_keys have to be defined with a predefined number of slots, we must have dummy extractors and compare objects to fill those up when the number of keys we specify at tun time is less than the number of slots: struct void_person_extractor { typedef boost::any result_type; result_type operator()(const person&)const{return boost::any();} }; template<> struct any_less<void> { bool operator()(const boost::any& x,const boost::any& y)const {return false;} }; Now, let's define the multi_index_container instantiation as: typedef multi_index_container< person, indexed_by< ordered_non_unique< composite_key< person, any_person_extractor, any_person_extractor, any_person_extractor, any_person_extractor >, composite_key_compare< any_compare, any_compare, any_compare, any_compare > >
person_container;
What we are doing here is reserve 4 slots (we can have more, of course) to fill at run time with specific key extractors (and their corresponding compare objects.) For instance, the following person_container c( boost::make_tuple( boost::make_tuple( boost::make_tuple( any_person_extractor(member<person,int,&person::birth_year>()), any_person_extractor(member<person,std::string,&person::name>()), any_person_extractor( member<person,std::string,&person::surname>()), void_person_extractor() ), boost::make_tuple( any_less<int>(), any_less<std::string>(), any_less<std::string>(), any_less<void>() ) ) )); instantiates a person_container with a composite key having three extractors on birth year, name and surname (a more elaborate example would fetch the info on which extractors toprovide from a configuration file or something); the remainng slot we fill with our dummy key extractor and compa object. Once instantiated, c can be used as a normal (compile-time defined) container. for instance, the following retrieves the persons born in 1959: std::pair<person_container::iterator,person_container::iterator> p= c.equal_range(1959); If the first key extractor weren't compatible with the int we've provided, for instance if we'd passed a string, a boost:any exception would be thrown --that's the price we pay for having this run-time flexibility, lack of compile time type checkhing. And that's it. You can play with a full example at http://coliru.stacked-crooked.com/a/4fa76b314298516d Joaquín M López Muñoz Telefónica Digital