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-10 07:50:08


Here are little corrections:

#include <iostream>
#include <functional>

using namespace std;

// Compiler internal:

template <typename F, typename ...T>
 Â Â Â  struct for_each_member;

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

// New "pretty" header:

struct pretty
{
 Â Â Â  size_t n;

 Â Â Â  pretty(size_t n = 0):n(n) {}

 Â Â Â  ostream & indent(size_t n, ostream & out)
 Â Â Â  {
 Â Â Â Â Â Â Â  for (size_t i = 0; i < n * 4; ++ i)
 Â Â Â Â Â Â Â Â Â Â Â  out << ' ';

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

 Â Â Â  template <typename T>
 Â Â Â Â Â Â Â  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) << '}';
 Â Â Â Â Â Â Â  }
};

// New "print" header:

template <typename T, typename F>
 Â Â Â  inline ostream & T::print(F && f, ostream & out)
 Â Â Â  {
 Â Â Â Â Â Â Â  make_for_each_member(f, * this, cout);

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

// Usage example:

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

 Â Â Â  // Capabilities:
 Â Â Â  template <typename F>
 Â Â Â Â Â Â Â  ostream & print(F && f, ostream & out); // only a declaration
needed if "print" header included
};

// Generated by the compiler:

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

int main()
{
 Â Â Â  A a;

 Â Â Â  a.print(pretty(), cout) << endl;
 Â Â Â  a.print(binary(), cout) << endl;
 Â Â Â  a.print(sql(), cout) << endl;
}

-- 
*Phil Bouchard*
Founder
C.: (819) 328-4743
Fornux Logo <http://www.fornux.com>
On 3/10/20 12:03 AM, Phil Bouchard wrote:
>
> Here's a prettier example, without the template template functor:
>
> #include <iostream>
> #include <functional>
>
> using namespace std;
>
>
> // Compiler internal:
>
> template <typename F, typename ...T>
>     struct for_each_member;
>
> template <typename F, typename ...T>
>     inline void make_for_each_member(F && f, T && ...t)
>     {
>         for_each_member<F, T...>()(forward<F>(f), forward<T>(t)...);
>     }
>
>
> // New "pretty_print" header:
>
> struct pretty_print
> {
>     size_t n;
>
>     pretty_print(size_t n = 0):n(n) {}
>
>     ostream & indent(size_t n, ostream & out)
>     {
>         for (size_t i = 0; i < n * 4; ++ i)
>             out << ' ';
>
>         return out;
>     }
>
>     template <typename T>
>         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(size_t n, ostream & out)
>     {
>         pretty_print f(n);
>
>         make_for_each_member<pretty_print>(f, T(), cout);
>
>         return out;
>     }
>
>
> // Usage example:
>
> struct A
> {
>     int a = 0, b = 1, c = 2;
>
>     // Capabilities:
>     ostream & pretty_print(size_t n, ostream & out); // only a 
> declaration needed if "pretty_print" header included
>     ostream & binary_print(ostream & out); // same here
>     ostream & sql_print(ostream & out); // same
> };
>
>
> // Generated by the compiler:
>
> template <typename F, typename ...T>
>     struct for_each_member<F, A, T...>
>     {
>         void operator () (F && f, A && a, T && ...t)
>         {
>             f(forward<decltype(a.a)>(a.a), 0, 3, forward<T>(t)...);
>             f(forward<decltype(a.b)>(a.b), 1, 3, forward<T>(t)...);
>             f(forward<decltype(a.c)>(a.c), 2, 3, forward<T>(t)...);
>         }
>     };
>
>
> int main()
> {
>     A a;
>
>     a.pretty_print(0, cout) << endl;
> }
>
> -- 
>
> *Phil Bouchard*
> Founder
> C.: (819) 328-4743
>
> Fornux Logo <http://www.fornux.com>
>
>
> On 3/9/20 8:44 PM, Phil Bouchard wrote:
>>
>> 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