visiting - acfr/comma GitHub Wiki

Table of Contents

concept

Visiting is traversing members of an object and doing something to each of them.

In comma, visiting:

  • is applied to a single object (as opposed to visiting several object in parallel)
  • supports, although not imposes, key-value semantics where the key may be visitable, too
  • is done depth-first
  • caveat: is recursive and therefore may not scale for very deep structures, e.g. arbitrary trees
  • caveat: is specialized and unwound in compile time, which may substantially increase compilation time and therefore it may be better to put visiting traits (see below) in a separate header and include it only where the traits are actually used
All the aspects of visiting are decoupled: the visited class, the visitor, and the visiting traits do not need to know about each other: no base classes to derive or alike. Any new visitor will work on a visitable class out of box.

artifacts

In comma/visiting/traits.h, a declaration of traits:

 template < typename T > traits;

as well as traits specialization for some basic containers like std::vector or std::map.

In comma/visiting/apply.h the apply() functions, like:

 template < typename V, typename T > void apply( V& visitor, T& visited );
 template < typename V, typename T > void apply( V& visitor, const T& visited );

See doxygen documentation for more details.

usage examples

Since the visiting library as such does not implement any concrete functionality, it is better to start with the tutorials for concrete types of visiting (comma-separated values, name-value, etc). Nevertheless:

serialization examples

E.g. in foo.h:

 // the only routine part: define how we visit foo in some header
 // assume, hello.h contains traits definition for hello_type, same as below
 namespace comma { namespace visiting {
 template <> traits< foo >
 {
     // non-const visitor
     template < typename K, typename V > void visit( const K& k, foo& f, V& v )
     {
         v.apply( "hello", f.hello );
         v.apply( "world", f.world );
         v.apply( "index", f.index );
     }
     // const visitor
     template < typename K, typename V > void visit( const K& k, const foo& f, V& v )
     {
         v.apply( "hello", f.hello );
         v.apply( "world", f.world );
         v.apply( "index", f.index );
     }
 };
 } }

Say, in main.cpp:

 int main( int, char** )
 {
     // load foo from xml
     std::ifstream ifs( "foo.xml" );
     foo f = comma::from_xml< foo >( ifs );
 
     // output to stdout as json
     comma::to_json( std::cout, f );
 
     // output to stdout as comma-separated values
     comma::csv::ascii_output_stream< foo > ostream( std::cout );
     ostream.write( f );
 }

foo can be a legacy or third-party class, but once you specialize the visiting traits for foo, no change is required for any of the visitors under the hood of functions from_xml(), to_json(), or ostream.write() to handle an instance of foo.

a visitor example

For simplicity's sake, assume that we deal with classes whose leaf members are either fundamental types or strings. The visitor below prints name and value of each leaf of an object.

 struct print_name_value
 {
     // traverse
     template < typename K, typename T >
     void apply( const K& name, const T& value )
     {
         visiting::do_while<    !boost::is_fundamental< T >::value
                             && !boost::is_same< T, std::string >::value >::visit( name, value, *this );
     }
 
     // handle non-leaf elements
     template < typename K, typename T >
     void apply_next( const K& name, const T& value ) { comma::visiting::visit( name, value, *this ); }
        
     // handle leaf elements
     template < typename K, typename T >
     void apply_final( const K& key, char ) { std::cout << key << ": " << value << std::endl; }
 }

Usage:

 int main( int, char** )
 {
     foo f;
     f.world = "theatre";
     f.index = 20;
     // either:
     comma::visiting::apply( print_name_value(), f );
     // or an equivalent form:
     comma::visiting::apply( print_name_value() ).to( f );
 }
⚠️ **GitHub.com Fallback** ⚠️