Boost logo

Boost :

Subject: [boost] [RFC] Protocol for operator. (dot) overloading
From: Corrado Zoccolo (czoccolo_at_[hidden])
Date: 2008-10-05 15:16:09


Hi boosters,
I think that Adobe poly approach on "operator . overloading" could be
extracted as orthogonal to the polymorphic object container concept, and
easily generalized to serve all kinds of wrappers.

My proposal (see code in the vault: *http://tinyurl.com/5xo9or*) is to have
a class template (interface<T,W,B>) that describes the interface of type T,
forwarding any operation to the implementation type, got directly from the
wrapper (W). B is used when vertical inheritance is needed, either to
benefit of EBO or to insert a base class on top of the hierarchy (see
type_erasure_wrapper). There is actually a fourth boolean parameter, that
will be explained later.

interface<T,W,B> can be partially specialized for user types (and libraries
can provide the specializations for theirs), leaving the wrapper as free
parameter, so that the same specialization can be used for any wrapper we
can subsequently write, thus decoupling the interface description step from
the interface usage by a wrapper.

The interface is separated in 3 parts:
* const interface (interface<const T, W, B>)
* mutable interface (interface<T, W, B, false>) for const propagating
wrappers (inherits from the const interface)
* mutable interface (interface<T, W, B, true>) for non const propagating
wrappers (simply a copy of the previous one, but with all members/member
functions declared const)

*Example, for std::string*
// sample (*const*) interface for std::string
template<typename W, typename B>
struct interface<const std::string, W, B> : B {
  typedef const std::string type;

  std::size_t size() const { return get_()->size(); }
  const char * c_str() const { return get_()->c_str(); }

  // free operators as friend
  template<typename W1, typename B1> friend
  std::string operator+ (const interface<const std::string,W,B>& lhs, const
interface<const std::string,W1,B1>& rhs)
  { return *lhs.get_()+*rhs.get_(); }
  // snip other overloads of operator+

private:
  typename interface_hook_type<type, W>::type
  get_() const { return static_cast<W const*>(this)->get_(); }
};

// sample (*mutable*) interface for std::string
template<typename W, typename B>
struct interface<std::string, W, B, false> : interface<const std::string, W,
B> {
  typedef std::string type;

  void swap(std::string& other) { get_()->swap(other); }
  std::string& operator=(std::string other) { get_()->swap(other); return
*get_(); }

  std::string& append ( const std::string& str ) { return
get_()->append(str); }
  //snip other overloads of append
private:
  typename interface_hook_type<type, W>::type
  get_() { return static_cast<W*>(this)->get_(); }
};

// mutable, version for non-const propagating wrappers
template<typename W, typename B>
struct interface<std::string, W, B, true> : interface<const std::string, W,
B> {
  typedef std::string type;

  void swap(std::string& other) const { get_()->swap(other); }
  std::string& operator=(std::string other) const { get_()->swap(other);
return *get_(); }

  std::string& append ( const std::string& str ) const { return
get_()->append(str); }
  //snip other overloads of append
private:
  typename interface_hook_type<type, W>::type
  get_() const { return static_cast<W const*>(this)->get_(); }
};

In the archive, you can find:
* interface.hpp = definition of interface class template
* string_interface.hpp = partial specialization of interface class template
for std::string
* pair_interface.hpp = partial specialization of interface class template
for std::pair (interesting for the way to handle first and second)
* wrappers.hpp = some example wrappers. We distinguish between wrappers that
propagate their constness to their wrapped type (value semantics, like
containers) and wrappers that do not propagate constness (ptr semantics,
like reference_wrapper, smart pointers), and provite examples for both. A
wrapper (computation_wrapper) is provided in both flavors.
* lockable.hpp = a more complex example wrapper. lockable<T> (locking proxy)
automatically locks when accessing a method of T through the wrapper.
* test{1,2,3,4,5,6}.cc instantiate the wrappers on various types, showing
which expressions are well formed and which are't.
** test1: lockable<T>
** test2: string interface (methods & operators), reference vs. value
wrappers
** test3: type_erasure_wrapper<I,T>
** test4: const-propagating vs. non-const propagating wrappers, in various
scenarios (const wrapper<T>, wrapper<const T>, const wrapper<const T>)
** test5: computation_wrapper<T>, and fundamental types
** test6: pair interface

Corrado

-- 
__________________________________________________________________________
dott. Corrado Zoccolo                          mailto:czoccolo_at_[hidden]
PhD - Department of Computer Science - University of Pisa, Italy
--------------------------------------------------------------------------

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