C++ Logo

std-proposals

Advanced search

Re: Generic template 'this' + new for_each_member() template template functor

From: Phil Bouchard <phil_at_[hidden]>
Date: Mon, 9 Mar 2020 20:44:34 -0400
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
>>>>

Received on 2020-03-09 19:47:22