On Mon, Jul 21, 2008 at 5:02 PM, Zeljko Vrba
On Mon, Jul 21, 2008 at 03:27:39PM +0200, Istvan Buki wrote:
InMetaData contain, among other things, the type of the input data. More specifically, InMetaData::type is a fusion map describing all the fields
of
the input data.
Given that you're using a map, I guess that the user has to define "dummy" types as field names (unless he's satisfied with exactly one value for each primitive type)?
Yes, Indeed. But they are not as dummy as one could first think, I use them to store the database field name in case I use one. For example: struct id { typedef fields::id type ; typedef int value_type ; static std::string value_name( void ) { return "id" ; } // return the database field name } ;
typename result_of::join< InMetaData, CacheMetaData, IndexType
::type apply( const typename InMetaData::type &r ) { typename IndexType::value_type key_value = fusion::at_key< IndexType >( r ) ; return fusion::as_map( fusion::join( r, fusion::erase_key< IndexType ( cache_.lookup( key_value ) ) ) ) ; }
Given your explanation, I have several questions about this code:
- the name apply -- is this function a part of some larger class, so the name is prescribed by some protocol?
Yes, the join component derives from a class whose responsibility is to manage input and output connections. In the base class I do: void exec( void ) { output_connection_->value() = static_cast< FuncType *>( this )->apply( input_connection_.value() ) ; }
- You have CacheMetaData as template parameter, yet you do not use it -- you have an instance variable cache_ instead. Is this just for illustration purposes or some other reason?
It is used to create the cache_ object which is a reference to a cache object . Here is the code of the join class that shows more clearly how it works: template < typename InMetaData, typename CacheMetaData, typename IndexType
struct join { typedef typename data_cache< CacheMetaData, IndexType >::impl join_cache ; struct impl : public one2one_component< InMetaData, result_of::join< InMetaData, CacheMetaData, IndexType >, impl > { impl( join_cache &c ) : cache_( c ) { } typename result_of::join< InMetaData, CacheMetaData, IndexType >::type apply( const typename InMetaData::type &r ) { typename IndexType::value_type key_value = fusion::at_key< IndexType >( r ) ; return fusion::as_map( fusion::join( r, fusion::erase_key< IndexType >( cache_.lookup( key_value ) ) ) ) ; } private: join_cache &cache_ ; } ; } ;
- the return statement prejudices "map" as implementation, i.e., the code does not look generic enough :-) is there some metafunction that could replace fusion::as_map with something more generic, in case the user would like to use e.g. vectors? ditto for erase_key?
I did not think about it. Currently my design is to pass map only between components. However, I use some transformation between map and vector in the database component (see beginning of this thread for more detail). This can perhaps be used as the starting point to achieve exactly that. [...]
Could you please also write a simple example of composition of two elements?
(I.e. how it would be used by the 'end-user' programmer?)
Here is a small example that read data from a CSV file, filter out a column and write the result to another CSV file. namespace fields { struct first_name { typedef fields::first_name type ; typedef std::string value_type ; } ; struct last_name { typedef fields::last_name type ; typedef std::string value_type ; } ; struct age { typedef fields::age type ; typedef int value_type ; } ; struct address { typedef fields::address type ; typedef std::string value_type ; } ; struct postal_code { typedef fields::postal_code type ; typedef std::string value_type ; } ; } struct AddressBook { typedef fusion::map < fusion::pair< fields::first_name::type, fields::first_name::value_type
, fusion::pair< fields::last_name::type, fields::last_name::value_type >, fusion::pair< fields::age::type, fields::age::value_type >, fusion::pair< fields::address::type, fields::address::value_type >, fusion::pair< fields::postal_code::type, fields::postal_code::value_type
type ; } ;
int main() { metl::csv_istream< AddressBook >::impl in_file( "addrbook.csv" ) ; typedef metl::filter< AddressBook, fields::age >::impl filter_t ; // remove the 'age' column typedef metl::result_of::filter< AddressBook, fields::age > filter_result_t ; filter_t my_filter ; metl::csv_ostream< filter_result_t >::impl out_file( "out.csv" ) ; // connect the components. in_file.output( my_filter.input() ) ; my_filter.output( out_file.input() ) ; while ( in_file.good() ) { in_file.exec() ; my_filter.exec() ; out_file.exec() ; } return 0 ;