[RFC] Protocol for operator. (dot) overloading

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@gmail.com PhD - Department of Computer Science - University of Pisa, Italy --------------------------------------------------------------------------

Corrado Zoccolo wrote:
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.
Such a complicated and specific mechanism. I think a lot of people come up with lots of approaches at overloading the dot operator which really do not truly overload its meaning. What that operator takes is an an object, a name, and arguments. Therefore, in my opinion, overloading the dot operator should mean calling a function with a compile-time string and those arguments. Then, with proper compile-time introspection support (an mpl map of compile-time strings to functor types), one can for example choose to call a member function with that name on some type, or do something else.

Hi Matias, <http://www.research.att.com/%7Ebs/bs_faq2.html#overload-dot> On Mon, Oct 6, 2008 at 2:02 AM, Mathias Gaunard < mathias.gaunard@ens-lyon.org> wrote:
Such a complicated and specific mechanism.
I think a lot of people come up with lots of approaches at overloading the dot operator which really do not truly overload its meaning.
actually, my proposal approximates in current C++ Gary Powell's, Doug Gregor's and Jaakko Jaarvi's proposal (see WG21/N1671), and is inline with Stroustrup's initial idea of what overloading operator. would be in C++, if it was allowed (see: http://www.research.att.com/~bs/bs_faq2.html#overload-dot<http://www.research.att.com/%7Ebs/bs_faq2.html#overload-dot>, or D&E).
What that operator takes is an an object, a name, and arguments. Therefore, in my opinion, overloading the dot operator should mean calling a function with a compile-time string and those arguments. Then, with proper compile-time introspection support (an mpl map of compile-time strings to functor types), one can for example choose to call a member function with that name on some type, or do something else.
This idea is nice, but it introduces an asymmetry with how operator -> is handled. Moreover, the same counter arguments as those for not overloading operator :: (scope resolution) hold (see: http://www.research.att.com/~bs/bs_faq2.html#overload-dot<http://www.research.att.com/%7Ebs/bs_faq2.html#overload-dot>). Corrado
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- __________________________________________________________________________ dott. Corrado Zoccolo mailto:czoccolo@gmail.com PhD - Department of Computer Science - University of Pisa, Italy --------------------------------------------------------------------------
participants (2)
-
Corrado Zoccolo
-
Mathias Gaunard