C++ Logo

STD-PROPOSALS

Advanced search

Subject: Re: [std-proposals] Generic template 'this' + new for_each_member() template template functor
From: Phil Bouchard (phil_at_[hidden])
Date: 2020-03-09 19:44:34


Here is a clean usage example:

#include <iostream>
#include <functional>

using namespace std;

// Compiler internal:

template <template <typename> class F, typename ...T> // note the usage
of the necessary template template parameter
 Â Â Â  struct for_each_member;

template <template <typename> class F, typename ...T>
 Â Â Â  inline void make_for_each_member(T && ...t)
 Â Â Â  {
 Â Â Â Â Â Â Â  for_each_member<F, T...>()(forward<T>(t)...);
 Â Â Â  }

// New "pretty_print" header:

template <typename T>
 Â Â Â  struct pretty_print
 Â Â Â  {
 Â Â Â Â Â Â Â  ostream & indent(size_t n, ostream & out)
 Â Â Â Â Â Â Â  {
 Â Â Â Â Â Â Â Â Â Â Â  for (size_t i = 0; i < n * 4; ++ i)
 Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â  out << ' ';

 Â Â Â Â Â Â Â Â Â Â Â  return out;
 Â Â Â Â Â Â Â  }

 Â Â Â Â Â Â Â  void operator () (T const & t, size_t i, size_t e, size_t n,
ostream & out)
 Â Â Â Â Â Â Â  {
 Â Â Â Â Â Â Â Â Â Â Â  if (i == 0)
 Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â  indent(n, out) << '{' << endl;

 Â Â Â Â Â Â Â Â Â Â Â  indent(n + 1, out) << t;

 Â Â Â Â Â Â Â Â Â Â Â  if (i != e - 1)
 Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â  out << ',';

 Â Â Â Â Â Â Â Â Â Â Â  out << endl;

 Â Â Â Â Â Â Â Â Â Â Â  if (i == e - 1)
 Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â  indent(n, out) << '}';
 Â Â Â Â Â Â Â  }
 Â Â Â  };

template <typename T>
 Â Â Â  inline ostream & T::pretty_print(ostream & out)
 Â Â Â  {
 Â Â Â Â Â Â Â  size_t indent = 0;

 Â Â Â Â Â Â Â  make_for_each_member<pretty_print>(T(), indent, cout);

 Â Â Â Â Â Â Â  return out;
 Â Â Â  }

// Usage example (what the user will only see):

struct A
{
 Â Â Â  int a = 0, b = 1, c = 2;

 Â Â Â  ostream & pretty_print(ostream & out); // only a declaration needed
if "pretty_print" header included
};

// Generated by the compiler:

template <template <typename> class F, typename ...T>
 Â Â Â  struct for_each_member<F, A, T...>
 Â Â Â  {
 Â Â Â Â Â Â Â  void operator () (A && a, T && ...t)
 Â Â Â Â Â Â Â  {
F<decltype(a.a)>()(forward<decltype(a.a)>(a.a), 0, 3, forward<T>(t)...);
F<decltype(a.b)>()(forward<decltype(a.b)>(a.b), 1, 3, forward<T>(t)...);
F<decltype(a.c)>()(forward<decltype(a.c)>(a.c), 2, 3, forward<T>(t)...);
 Â Â Â Â Â Â Â  }
 Â Â Â  };

int main()
{
 Â Â Â  A a;

 Â Â Â  a.pretty_print(cout) << endl;
}

Other applications of for_each_member() can be:

- comparisons,

- changing properties of filtered mutable member variables,

- generic debug functions changed on-the-fly at one place only,

- easily apply textual and binary I/O operations to huge structures,

- easily apply database operations to huge structures,

- automate the mapping of structures to a user-interface generator,

- ...

-- 
*Phil Bouchard*
Founder
C.: (819) 328-4743
Fornux Logo <http://www.fornux.com>
On 3/9/20 10:56 AM, Phil Bouchard wrote:
> ... Also we could limit generic ‘this’ overloads to specific namespaces:
>
> template <typename Class>
>     ostream & boost::detail::Class::operator << (ostream & out) { ... }
>
> -- 
>
> *Phil Bouchard*
> Founder
> C.: (819) 328-4743 <tel:(819)%20328-4743>
>
> Fornux Logo <http://www.fornux.com/>
>
> On Mar 8, 2020, at 7:36 PM, Phil Bouchard <phil_at_[hidden] 
> <mailto:phil_at_[hidden]>> wrote:
>
>> ... And only the classes with the proper declarations would take 
>> advantage of the generic template 'this':
>>
>>
>> -- 
>>
>> *Phil Bouchard*
>> Founder
>> C.: (819) 328-4743
>>
>> Fornux Logo <http://www.fornux.com>
>>
>>
>> On 3/8/20 7:34 PM, Phil Bouchard wrote:
>>>
>>> What I have in mind is to clean up the dispatch fiasco that is done 
>>> with either the constructors or the cast operator right now. 
>>> Conversions, comparisons, ... should be simplified as well. For example:
>>>
>>> struct X
>>>
>>> {
>>>
>>>     int value;
>>>
>>>
>>>     template <typename T>
>>>
>>>         X(T const & T);
>>>
>>>     template <typename T>
>>>
>>>         operator T ();
>>>
>>> };
>>>
>>> struct Y
>>>
>>> {
>>>
>>>     int value;
>>>
>>>
>>>     template <typename T>
>>>
>>>         Y(T const & T);
>>>
>>>     template <typename T>
>>>
>>>         operator T ();
>>>
>>> };
>>>
>>> template <typename Class, typename T>
>>>
>>>     inline Class::operator T () { return T(* this); } // generic 
>>> constructor wrapper
>>>
>>> template <typename Class, typename T>
>>>
>>>     inline Class::Class(T const &t) { value = t.value; } // cast 
>>> operation really done here
>>>
>>>
>>> And normal specializations here:
>>>
>>> template <typename T>
>>>
>>>     inline X::X(T const &t) { value = t.value; } // specialized cast 
>>> operation done here
>>>
>>>
>>> Also if we had a way to iterate all members (which would be another 
>>> important proposal) then we could also have a generic "operator <<" 
>>> as well:
>>>
>>> template <typename Class>
>>>
>>>     ostream & Class::operator << (ostream & out)
>>>
>>>     {
>>>
>>>         for (member_iterator<Class> i = 
>>> member_iterator<Class>::begin(); i != member_iterator<Class>::end(); 
>>> ++ i)
>>>
>>>         {
>>>
>>>             if (i != member_iterator<Class>::begin())
>>>
>>>                 out << ", ";
>>>
>>>             out << this->*i;
>>>
>>>         }
>>>
>>>         return out;
>>>
>>>     }
>>>
>>> The aforementioned should also remove the need for these global 
>>> friend function overloads.
>>>
>>>
>>> -- 
>>>
>>> *Phil Bouchard*
>>> Founder
>>> C.: (819) 328-4743
>>>
>>> Fornux Logo <http://www.fornux.com>
>>>
>>>
>>> On 3/8/20 6:03 PM, Jake Arkinstall wrote:
>>>> For me, this approach has some merits, but this particular example 
>>>> is something that, to me, makes much more sense as a free function.
>>>>
>>>> Do you have further examples?
>>>>
>>>> On Sun, 8 Mar 2020, 21:55 Phil Bouchard via Std-Proposals, 
>>>> <std-proposals_at_[hidden] 
>>>> <mailto:std-proposals_at_[hidden]>> wrote:
>>>>
>>>>     Well I believe a proposal should be open for debate, just like
>>>>     a thesis.
>>>>
>>>>     We both agree these overloads should be simplified but we
>>>>     disagree on the syntax.
>>>>
>>>>     I saw the recursive lambda thing and I think I already talked
>>>>     to you before regarding CV overloads but we were proposing the
>>>>     following syntax:
>>>>
>>>>     struct A
>>>>
>>>>     {
>>>>
>>>>         template <bool BC>
>>>>
>>>>             void foo() bool<BC>
>>>>
>>>>             {
>>>>
>>>>             }
>>>>
>>>>     };
>>>>
>>>>     And regarding the this generic overloading I still believe the
>>>>     following syntax is cleaner:
>>>>
>>>>     struct A
>>>>
>>>>     {
>>>>
>>>>         template <typename T>
>>>>
>>>>             bool compare(T const &);
>>>>
>>>>     };
>>>>
>>>>     template <typename C, typename T>
>>>>
>>>>         void C::compare(C const &)
>>>>
>>>>         {
>>>>
>>>>             ...
>>>>
>>>>         }
>>>>
>>>>     Because with your syntax I could technically reassign "self":
>>>>
>>>>     struct A
>>>>
>>>>     {
>>>>
>>>>         template <typename C, typename T>
>>>>
>>>>             bool compare(this C & c, T const &);
>>>>
>>>>     };
>>>>
>>>>     template <typename C, typename T>
>>>>
>>>>         bool C::compare(this C & c, T const &) // redundant C here
>>>>
>>>>         {
>>>>
>>>>             static C sc = C();
>>>>
>>>>             c = sc; // in theory we could do this unless you patch
>>>>     the compilers with a new error for that specific case
>>>>
>>>>         }
>>>>
>>>>
>>>>     -- 
>>>>
>>>>     *Phil Bouchard*
>>>>     Founder
>>>>     C.: (819) 328-4743
>>>>
>>>>     Fornux Logo <http://www.fornux.com>
>>>>
>>>>
>>>>     On 3/8/20 5:37 AM, Gašper Ažman wrote:
>>>>>     I meant that you should consider the incremental value of your
>>>>>     proposal over p0847, which is on track for c++23 unless
>>>>>     serious issues crop up
>>>>>
>>>>>     On Sat, Mar 7, 2020, 19:04 Phil Bouchard <phil_at_[hidden]
>>>>>     <mailto:phil_at_[hidden]>> wrote:
>>>>>
>>>>>         The syntaxes are way different:
>>>>>
>>>>>         http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0847r4.html
>>>>>
>>>>>         In your case the compiler will have to change all of its
>>>>>         rules for the number of parameters "operator" overloads.
>>>>>         For example:
>>>>>
>>>>>         struct A
>>>>>
>>>>>         {
>>>>>
>>>>>             int value;
>>>>>
>>>>>             ...
>>>>>
>>>>>             template <typename Self>
>>>>>
>>>>>                 bool operator < (this Self && self, A const & a)
>>>>>         // 2 parameters... ok
>>>>>
>>>>>                 {
>>>>>
>>>>>                     return self.value < a.value;
>>>>>
>>>>>                 }
>>>>>
>>>>>
>>>>>                 bool operator < (A const & a) // 1 parameter... ok
>>>>>
>>>>>                 {
>>>>>
>>>>>                     return value < a.value;
>>>>>
>>>>>                 }
>>>>>
>>>>>         };
>>>>>
>>>>>
>>>>>         -- 
>>>>>
>>>>>         *Phil Bouchard*
>>>>>         Founder
>>>>>         C.: (819) 328-4743
>>>>>
>>>>>         Fornux Logo <http://www.fornux.com>
>>>>>
>>>>>
>>>>>         On 3/7/20 1:35 PM, Gašper Ažman wrote:
>>>>>>         P0847
>>>>>>
>>>>>>         On Sat, Mar 7, 2020, 18:33 Phil Bouchard via
>>>>>>         Std-Proposals <std-proposals_at_[hidden]
>>>>>>         <mailto:std-proposals_at_[hidden]>> wrote:
>>>>>>
>>>>>>             Alright, I'm pretty sure this is not implemented yet.
>>>>>>             Suppose you have:
>>>>>>
>>>>>>             struct A
>>>>>>
>>>>>>             {
>>>>>>
>>>>>>                 int value;
>>>>>>
>>>>>>                 ...
>>>>>>
>>>>>>                 template <typename T>
>>>>>>
>>>>>>                     int compare(T const & t) const;
>>>>>>
>>>>>>             }
>>>>>>
>>>>>>
>>>>>>             struct B
>>>>>>
>>>>>>             {
>>>>>>
>>>>>>                 int value;
>>>>>>
>>>>>>                 ...
>>>>>>
>>>>>>                 template <typename T>
>>>>>>
>>>>>>                     int compare(T const & t) const;
>>>>>>
>>>>>>             }
>>>>>>
>>>>>>
>>>>>>             Then a generic way to define the same functionality
>>>>>>             for all classes would be to have a "template 'this'":
>>>>>>
>>>>>>             template <typename C, typename T>
>>>>>>
>>>>>>                     inline int C::compare(T const & t) const
>>>>>>
>>>>>>                     {
>>>>>>
>>>>>>                         return value == t.value ? 0 : value <
>>>>>>             t.value ? -1 : 1;
>>>>>>
>>>>>>                     }
>>>>>>
>>>>>>
>>>>>>             (Please include my email address in your replies)
>>>>>>
>>>>>>
>>>>>>             -- 
>>>>>>
>>>>>>             *Phil Bouchard*
>>>>>>             Founder
>>>>>>             C.: (819) 328-4743
>>>>>>
>>>>>>             Fornux Logo <http://www.fornux.com>
>>>>>>             -- 
>>>>>>             Std-Proposals mailing list
>>>>>>             Std-Proposals_at_[hidden]
>>>>>>             <mailto:Std-Proposals_at_[hidden]>
>>>>>>             https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>>>>>
>>>>     -- 
>>>>     Std-Proposals mailing list
>>>>     Std-Proposals_at_[hidden]
>>>>     <mailto:Std-Proposals_at_[hidden]>
>>>>     https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>>>


STD-PROPOSALS list run by herb.sutter at gmail.com

Standard Proposals Archives on Google Groups