[mpl][fusion] making a heterogeneous collection of classes from a list of types.
Hello, I have the MPL book and I've used some metaprogramming before but I'm always at the edge of my understanding of the language when I do it. I have a problem right now which I suspect is trivial but I can't for the life of me figure out how to do it. I have a list of 25 resource types (class Sound, class Script, etc.) and since they are not mutable I want to share them so I have a resource cache template that looks something like: template< typename Resource > class Cache { public: typedef typename Tag<Resource>::type resource_tag; /// Retrieve a resource by id /// Throws cache_error if the resource couldn't be found in the Repository Resource& Retrieve( ResourceId id ) { // is the resource in the cache? typename ResourceMap::iterator it = m_resources.find( id ); if( it != m_resources.end() ) { return it->second; } else { return LoadResource( id ); } } /// Clears the cache and destructs all cached resources. void Clear() { m_resources.clear(); } private: Resource& LoadResource( ResourceId id ) { Stream stream( Repository::Instance().Retrieve( id, resource_tag::type::value ) ); if( !stream.fail() ) { Resource* res = new Resource(stream); m_resources.insert( id, res ); return *res; } else { throw cache_error( id, resource_tag::type::value ); } } typedef boost::ptr_map< ResourceId, Resource > ResourceMap; ResourceMap m_resources; }; I have a class which must contain a Cache for each of the 25 resource types and support calls to get a resource of a particular type given an id and be able to call Clear on all 25 caches. Something like class Manager { public: /// Returns Cache<Resource>::Retrieve( id ) template< typename Resource > const Resource& Get( ResourceId id ); /// Calls Cache::Clear for all resource types void Clear(); }; It would be pretty easy to just copy and paste it out 25 times but I would like to reduce code duplication and simplify maintenance through metaprogramming. I have a private boost::mpl::list25 named resource_list containing all the resource types. I think I need to transform that in to a boost::fusion::map of 25 Resource to Cache<Resource> pairs but I'm not sure that is what I really need to do or how to go about doing that. Thanks, Michael Marcin
Looking back into the MPL book I think I can create the required members and implement Get by modifying the class in the last posting with: template< typename Resource > struct Cache { resource::Cache<Resource> value; }; class Manager { public: /// Returns Cache<Resource>::Retrieve( id ) template< typename Resource > Resource& Get( ResourceId id ) { return static_cast<Cache<Resource>&>(m_caches).value.Retrieve(id); } /// Calls Cache::Clear for all resource types void Clear(); private: typedef boost::mpl::vector25< Sound, Script, // etc. > resource_types; boost::mpl::inherit_linearly< resource_types, boost::mpl::inherit< Cache< boost::mpl::_2 >, boost::mpl::_1 > >::type m_caches; }; Now I need to figure out a way to iterate the members of m_caches and call the Clear method on each value. As an aside it's syntactically heavy to have to qualify each placeholder with "boost::mpl::". Unfortunately if I don't do that it picks up boost::bind placeholders from an unnamed namespace and gives an impressive spew of error messages. Thanks, Michael Marcin
Okay I think I've got a solution but I it is a bit ugly. Is there a cleaner solution? template< typename T > struct wrap { }; template< typename ResourceCaches > struct CacheClearer { ResourceCaches& m_caches; CacheClearer( ResourceCaches& caches ) : m_caches( caches ) {} template< typename Resource > void operator()( wrap<Resource> ) { static_cast<Cache<Resource>&>(m_caches).value.Clear(); } }; template< typename ResourceCaches > CacheClearer<ResourceCaches> MakeCacheClearer( ResourceCaches& caches ) { return CacheClearer<ResourceCaches>( caches ); }; template< typename Resource > struct Cache { resource::Cache<Resource> value; }; class Manager { public: /// Returns Cache<Resource>::Retrieve( id ) template< typename Resource > Resource& Get( ResourceId id ) { return static_cast<Cache<Resource>&>(m_caches).value.Retrieve(id); } /// Calls Cache::Clear for all resource types void Clear() { boost::mpl::for_each<resource_types, wrap<boost::mpl::_1> >( MakeCacheClearer(m_caches) ); } private: typedef boost::mpl::vector25< Sound, Script, // etc.
resource_types;
boost::mpl::inherit_linearly< resource_types, boost::mpl::inherit< Cache< boost::mpl::_2 >, boost::mpl::_1 >
::type m_caches; };
Thanks, Michael Marcin
on Thu Oct 11 2007, Michael Marcin <mmarcin-AT-method-solutions.com> wrote:
Okay I think I've got a solution but I it is a bit ugly. Is there a cleaner solution?
Looks OK to me. What do you find ugly about it? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
David Abrahams wrote:
on Thu Oct 11 2007, Michael Marcin <mmarcin-AT-method-solutions.com> wrote:
Okay I think I've got a solution but I it is a bit ugly. Is there a cleaner solution?
Looks OK to me. What do you find ugly about it?
Thanks for looking. I guess the only concrete thing I can point to is having to declare my own template< typename T > struct wrap { }; since this seems like its probably pretty common I figured there might be one in mpl somewhere. Thanks, Michael Marcin
on Fri Oct 12 2007, Michael Marcin <mmarcin-AT-method-solutions.com> wrote:
David Abrahams wrote:
on Thu Oct 11 2007, Michael Marcin <mmarcin-AT-method-solutions.com> wrote:
Okay I think I've got a solution but I it is a bit ugly. Is there a cleaner solution?
Looks OK to me. What do you find ugly about it?
Thanks for looking.
I guess the only concrete thing I can point to is having to declare my own
template< typename T > struct wrap { };
since this seems like its probably pretty common I figured there might be one in mpl somewhere.
You can use mpl::identity for this. There's also boost/type.hpp -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
on Wed Oct 10 2007, Michael Marcin <mmarcin-AT-method-solutions.com> wrote:
As an aside it's syntactically heavy to have to qualify each placeholder with "boost::mpl::". Unfortunately if I don't do that it picks up boost::bind placeholders from an unnamed namespace and gives an impressive spew of error messages.
namespace m = boost::mpl::placeholders; typedef is_same<m::_1,int> is_int; HTH, -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
participants (2)
-
David Abrahams
-
Michael Marcin