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 12:56:35


Even better: for_each_member<> should be nested inside their respective class (i.e. typename A::for_each_member<>), this way we won’t pollute the global namespace at all and have a million error messages when something goes wrong.

Also we could have:
...::for_each_member<>
...::for_each_public_member<>
...::for_each_private_member<>
...::for_each_protected_member<>

-- 
Phil Bouchard
Founder
C.: (819) 328-4743
> On Mar 10, 2020, at 8:50 AM, Phil Bouchard <phil_at_[hidden]> wrote:
> 
> 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
> 
> 
> 
> 
> 
>> 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
>> 
>> 
>> 
>> 
>> 
>>> 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
>>> 
>>> 
>>> 
>>> 
>>> 
>>>> 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
>>>> 
>>>> 
>>>> 
>>>> On Mar 8, 2020, at 7:36 PM, Phil Bouchard <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
>>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>>>> 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
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>>> 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]> 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
>>>>>>>> 
>>>>>>>> 
>>>>>>>> 
>>>>>>>> 
>>>>>>>> 
>>>>>>>>> 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]> 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
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>>> 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]> 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
>>>>>>>>>>>> 
>>>>>>>>>>>> 
>>>>>>>>>>>> -- 
>>>>>>>>>>>> Std-Proposals mailing list
>>>>>>>>>>>> Std-Proposals_at_[hidden]
>>>>>>>>>>>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>>>>>>> -- 
>>>>>>>> Std-Proposals mailing list
>>>>>>>> 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