Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2005-11-21 12:56:11


"Robert Ramey" <ramey_at_[hidden]> writes:

> Anyway, it seems you do have an understanding and even appreciation
> of my concerns so I'm optimistic that the next iteration will be
> better.

Here's the next iteration.

The Design
==========

In the text and code that follows, the word "array" usually refers to
a contiguous sequence of instances of a single datatype, and not to a
C++ builtin array type of the form T[N]. I'll try to be explicit when
I intend to describe the latter.

Organization
------------

We have no attachment to the organization proposed below and if you
don't like it we'd be happy to move all the proposed code into a
completely separate area of Boost. Please accept it just for the
purposes of this discussion. We propose to add the following new
files and directories:

boost/serialization/
  load_array.hpp - hooks for deserializing into arrays
  save_array.hpp - hooks for serializing from arrays
  array.hpp - dispatching tools

boost/archive/array/
                     
  iarchive.hpp - base class templates for authors of
  oarchive.hpp array-optimized archives.

  binary_iarchive.hpp - archives that use the hooks for std::vector and T[n]
  binary_oarchive.hpp
  polymorphic_binary_iarchive.hpp
  polymorphic_binary_oarchive.hpp

  ...other array-optimized archives...

Details
-------

In this section I'll show the important details of some of the files
above, to give a clear sense of the mechanisms in use. The snippets
below are synopses, leaving out details like #includes and, usually,
namespaces. We're also only showing the "load" half of the code,
since the "save" half is almost identical with s/load/save/ and
s/>>/<</. Finally, while all the mechanisms have been tested, the
code shown here is not a direct copy of tested code and may contain
errors.

serialization/array.hpp
.......................

  // When passed an archive pointer and a data pointer, returns a tag
  // indicating whether optimization should be applied.
  mpl::false_ optimize_array(...) { return mpl::false_(); }

serialization/load_array.hpp
............................

  // Hooks for loading arrays

  // optimized_load_array
  //
  // Used to select either the standard array loading procedure or an
  // optimized one depending on properties of the array's element type.
  // Will usually be called with an MPL predicate as a fifth argument
  // saying whether optimization should be applied, e.g.:
  //
  // optimized_load_array(ar, p, n, v, is_fundamental<element_type>())
  //
  // Most array-optimized archives won't need to call it directly,
  // since they will be derived from archive::array::optimized,
  // which provides the call.

  template <class Archive, class ValueType>
  void optimized_load_array(
    Archive& ar, ValueType * p, std::size_t n, unsigned int version,
    boost::mpl::false_)
  {
      // Optimization not appropriate; use standard procedure
      while (n--)
        ar >> serialization::make_nvp("item", *p++);
  }

  template <class Archive, class ValueType>
  void optimized_load_array(
    Archive& ar, ValueType * p, std::size_t n, unsigned int version,
    boost::mpl::true_)
  {
      // dispatch to archive-format-specific optimization for types that
      // meet the optimization criteria
      ar.load_array(p,n,version);
  }

  // load_array
  //
  // Authors of serialization for types containing arrays will call
  // this function to ensure that optimizations will be applied when
  // possible.

  template <class Archive, class ValueType>
  inline void load_array(
    Archive& ar, ValueType * p, std::size_t n, unsigned int version)
  {
     serialization::optimized_load_array(
       ar, p, version, optimize_array(&ar, p) );
  }

archive/array/iarchive.hpp
..........................

Based in part on a suggestion of yours, for handling vectors and
builtin arrays.

  // To conveniently array-optimize an input archive X:
  //
  // * Derive it from iarchive<X, Impl>, where Impl is an
  // archive implementation base class from
  // Boost.Serialization
  //
  // * add a member function template that implements the
  // procedure for serializing arrays of T (for appropriate T)
  //
  // template <class T>
  // load_array(T* p, size_t nelems, unsigned version)
  //
  // * add a unary MPL lambda expression member called
  // use_array_optimization whose result is convertible to
  // mpl::true_ iff array elements of type T can be serialized
  // with the load_array member function, and to mpl::false_ if
  // the unoptimized procedure must be used.

  namespace archive { namespace array
  {
    template <class Derived, class Base>
    class iarchive
      : public Base
    {
        template <class S>
        optimized(S& s, unsigned int flags)
          : Base(s,flags)
        {}

        // Load std::vector<T> using load_array
        template<class T>
        void load_override(std::vector<T> &x, unsigned int version);

        // Load T[N] using load_array
        template<class T, std::size_t N>
        void load_override(T(&x)[N], unsigned int version);

        // Load everything else in the usual way, forwarding on to the
        // Base class
        template<class T>
        void load_override(T&, unsigned BOOST_PFTO int version);

     protected:
        typedef iarchive iarchive_base; // convenience for derivers
    };
  }}

  namespace serialization
  {
    // Overload optimize_array for array-optimized iarchives. This
    // version evaluates an MPL lambda expression in the archive to
    // say whether its load_array member should be used.
    //
    // If not for the lack of ADL in vc6/7, this could go
    // in archive::array
    template <class Archive, class ValueType>
    typename mpl::apply1<
        typename Archive::use_array_optimization
      , ValueType
>::type
    optimize_array(array::iarchive<Archive>*, ValueType*)
    {
        typedef typename mpl::apply1<
            BOOST_DEDUCED_TYPENAME Archive::use_array_optimization
          , ValueType
>::type result;

        return result();
    }
  } // end namespace serialization

archive/array/binary_iarchive.hpp
..................................

  class binary_iarchive
    : public array::iarchive<
          array::binary_iarchive
        , archive::binary_iarchive_impl<binary_iarchive>
>
  {
      template <class S>
      binary_iarchive(S& s, unsigned int flags)
        : binary_iarchive::iarchive_base(s,flags)
      {}

      // use the optimized load procedure for all fundamental types.
      typedef boost::is_fundamental<mpl::_>
        use_array_optimization;

      // This is how we load an array when optimization is appropriate.
      template <class ValueType>
      void load_array(ValueType * p, std::size_t n, unsigned int version)
      {
          this->load_binary(p, n * sizeof(ValueType));
      }
  };

This completes the design presentation. After you've digested it and
we've answered any questions you might have, we can move on to
evaluating its strengths and weaknesses.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk