C++ Logo

std-proposals

Advanced search

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

From: Phil Bouchard <phil_at_[hidden]>
Date: Fri, 13 Mar 2020 12:40:27 -0400
So what do you think? Pretty nice hey?

-- 
Phil Bouchard
Founder
C.: (819) 328-4743
> On Mar 12, 2020, at 5:24 PM, Phil Bouchard <phil_at_[hidden]> wrote:
> 
> Again, here is a very powerful example with different numeric types, their precedence with their regular and infix operators implemented only once:
> 
> 
> 
> namespace numeric
> {
> 
> template <typename Class, typename T>
>     inline Class::Class(T const & v) : value(v.value)
>     {
>         std::clog << __PRETTY_FUNCTION__ << std::endl;
>     }
>     
> template <typename Class, typename T>
>     inline T Class::operator + (T const & v) const
>     {
>         std::clog << __PRETTY_FUNCTION__ << std::endl;
> 
>         T res;
>         
>         res.value = value + v.value;
>         
>         return res;
>     }
>     
> template <typename Class, typename T>
>     inline T Class::operator - (T const & v) const
>     {
>         std::clog << __PRETTY_FUNCTION__ << std::endl;
> 
>         T res;
>         
>         res.value = value - v.value;
>         
>         return res;
>     }
>     
> template <typename Class, typename T>
>     inline T Class::operator * (T const & v) const
>     {
>         std::clog << __PRETTY_FUNCTION__ << std::endl;
> 
>         T res;
>         
>         res.value = value * v.value;
>         
>         return res;
>     }
>     
> template <typename Class, typename T>
>     inline T Class::operator / (T const & v) const
>     {
>         std::clog << __PRETTY_FUNCTION__ << std::endl;
> 
>         T res;
>         
>         res.value = value / v.value;
>         
>         return res;
>     }
> 
> template <typename Class, typename T>
>     inline T & Class::operator += (T const & v)
>     {
>         std::clog << __PRETTY_FUNCTION__ << std::endl;
>         
>         * this = this->operator + (v);
>         
>         return * this;
>     }
> 
> template <typename Class, typename T>
>     inline T & Class::operator -= (T const & v)
>     {
>         std::clog << __PRETTY_FUNCTION__ << std::endl;
>         
>         * this = this->operator - (v);
>         
>         return * this;
>     }
> 
> template <typename Class, typename T>
>     inline T & Class::operator *= (T const & v)
>     {
>         std::clog << __PRETTY_FUNCTION__ << std::endl;
>         
>         * this = this->operator * (v);
>         
>         return * this;
>     }
> 
> template <typename Class, typename T>
>     inline T & Class::operator /= (T const & v)
>     {
>         std::clog << __PRETTY_FUNCTION__ << std::endl;
>         
>         * this = this->operator / (v);
>         
>         return * this;
>     }
> 
> struct int_t;
> struct double_t;
> struct long_double_t;
> 
> struct int_t
> {
>     int value;
>     
>     int_t(int v) : value(v) {}
>     
>     int_t(int_t const & v) = generic;
>     
>     int_t operator + (int_t const & v) const = generic;
>     int_t operator - (int_t const & v) const = generic;
>     int_t operator * (int_t const & v) const = generic;
>     int_t operator / (int_t const & v) const = generic;
> 
>     double_t operator + (double_t const & v) const = generic;
>     double_t operator - (double_t const & v) const = generic;
>     double_t operator * (double_t const & v) const = generic;
>     double_t operator / (double_t const & v) const = generic;
> 
>     long_double_t operator + (long_double_t const & v) const = generic;
>     long_double_t operator - (long_double_t const & v) const = generic;
>     long_double_t operator * (long_double_t const & v) const = generic;
>     long_double_t operator / (long_double_t const & v) const = generic;
> 
>     int_t & operator += (int_t const & v) = generic;
>     int_t & operator -= (int_t const & v) = generic;
>     int_t & operator *= (int_t const & v) = generic;
>     int_t & operator /= (int_t const & v) = generic;
> };
> 
> struct double_t
> {
>     double value;
>     
>     double_t(double v) : value(v) {}
>     
>     double_t(int_t const & v) = generic;
>     double_t(double_t const & v) = generic;
>     
>     double_t operator + (double_t const & v) const = generic;
>     double_t operator - (double_t const & v) const = generic;
>     double_t operator * (double_t const & v) const = generic;
>     double_t operator / (double_t const & v) const = generic;
>     
>     long_double_t operator + (long_double_t const & v) const = generic;
>     long_double_t operator - (long_double_t const & v) const = generic;
>     long_double_t operator * (long_double_t const & v) const = generic;
>     long_double_t operator / (long_double_t const & v) const = generic;
> 
>     double_t & operator += (int_t const & v) = generic;
>     double_t & operator -= (int_t const & v) = generic;
>     double_t & operator *= (int_t const & v) = generic;
>     double_t & operator /= (int_t const & v) = generic;
>     
>     double_t & operator += (double_t const & v) = generic;
>     double_t & operator -= (double_t const & v) = generic;
>     double_t & operator *= (double_t const & v) = generic;
>     double_t & operator /= (double_t const & v) = generic;
> };
> 
> struct long_double_t
> {
>     long double value;
>     
>     long_double_t(long double v) : value(v) {}
>     
>     long_double_t(int_t const & v) = generic;
>     long_double_t(double_t const & v) = generic;
>     long_double_t(long_double_t const & v) = generic;
>     
>     long_double_t operator + (long_double_t const & v) const = generic;
>     long_double_t operator - (long_double_t const & v) const = generic;
>     long_double_t operator * (long_double_t const & v) const = generic;
>     long_double_t operator / (long_double_t const & v) const = generic;
>     
>     long_double_t & operator += (int_t const & v) = generic;
>     long_double_t & operator -= (int_t const & v) = generic;
>     long_double_t & operator *= (int_t const & v) = generic;
>     long_double_t & operator /= (int_t const & v) = generic;
>     
>     long_double_t & operator += (double_t const & v) = generic;
>     long_double_t & operator -= (double_t const & v) = generic;
>     long_double_t & operator *= (double_t const & v) = generic;
>     long_double_t & operator /= (double_t const & v) = generic;
>     
>     long_double_t & operator += (long_double_t const & v) = generic;
>     long_double_t & operator -= (long_double_t const & v) = generic;
>     long_double_t & operator *= (long_double_t const & v) = generic;
>     long_double_t & operator /= (long_double_t const & v) = generic;
> };
> 
> }
> 
> using numeric::int_t;
> using numeric::double_t;
> using numeric::long_double_t;
> 
> 
> -- 
> 
> Phil Bouchard
> Founder
> C.: (819) 328-4743
> 
> 
> 
> 
> 
>> On 3/11/20 7:34 AM, Phil Bouchard wrote:
>> To go back to the generic template 'this', here is another useful example that centralizes mathematical operations:
>> 
>> // What the user sees:
>> 
>> struct A
>> {
>>     int value;
>>     
>>     A(A const & a) : value(a.value)
>>     {
>>     }
>>     
>>     A & operator += (A const & a)
>>     {
>>         value += a.value;
>>         
>>         return * this;
>>     }
>> 
>>     A & operator -= (A const & a)
>>     {
>>         value -= a.value;
>>         
>>         return * this;
>>     }
>>     
>>     A & operator *= (A const & a)
>>     {
>>         value *= a.value;
>>         
>>         return * this;
>>     }
>> 
>>     A & operator /= (A const & a)
>>     {
>>         value /= a.value;
>>         
>>         return * this;
>>     }    
>>     
>>     A & operator = (A const & a) = generic; // the 'generic' keyword could be used to hint the compiler of our intents
>> 
>>     A operator + (A const & a) const = generic;
>>     A operator - (A const & a) const = generic;
>>     A operator * (A const & a) const = generic;
>>     A operator / (A const & a) const = generic;
>> };
>> 
>> 
>> // Some "generic" header:
>> 
>> template <typename Class>
>>     Class & Class::operator = (Class const & c)
>>     {
>>         ~Class();
>>         
>>         new (this) Class(c);
>>         
>>         return * this;
>>     }
>>     
>> template <typename Class>
>>     Class Class::operator + (Class const & c) const
>>     {
>>         Class r(* this);
>>         
>>         r += c;
>>         
>>         return r;
>>     }
>> 
>> template <typename Class>
>>     Class Class::operator - (Class const & c) const
>>     {
>>         Class r(* this);
>>         
>>         r -= c;
>>         
>>         return r;
>>     }
>> 
>> template <typename Class>
>>     Class Class::operator * (Class const & c) const
>>     {
>>         Class r(* this);
>>         
>>         r *= c;
>>         
>>         return r;
>>     }
>> 
>> template <typename Class>
>>     Class Class::operator / (Class const & c) const
>>     {
>>         Class r(* this);
>>         
>>         r /= c;
>>         
>>         return r;
>>     }
>> 
>> 
>> 
>> -- 
>> 
>> Phil Bouchard
>> Founder
>> C.: (819) 328-4743
>> 
>> 
>> 
>> 
>> 
>>> On 3/10/20 10:00 PM, Phil Bouchard wrote:
>>> Regarding the for_each_member() functor, here is a possible implementation:
>>> 
>>> #include <iostream>
>>> 
>>> using namespace std;
>>> 
>>> 
>>> template <typename F>
>>>     inline ostream & print(F && f, char const * n, ostream & out)
>>>     {
>>>         out << n;
>>>         
>>>         return out;
>>>     }
>>> 
>>> template <typename F>
>>>     inline ostream & print(F && f, char const * n, char const * t, ostream & out)
>>>     {
>>>         out << n << " = " << t;
>>>         
>>>         return out;
>>>     }
>>> 
>>> template <typename F>
>>>     inline ostream & print(F && f, char const * n, int const t, ostream & out)
>>>     {
>>>         out << n << " = " << t;
>>>         
>>>         return out;
>>>     }
>>> 
>>> template <typename F, typename T>
>>>     inline ostream & print(F && f, char const * n, T const & t, ostream & out)
>>>     {
>>>         f.begin(n, t, out);
>>>         typename T::template for_each_member<F, ostream>()(forward<F>(f), t, out);
>>>         f.end(n, t, out);
>>>         
>>>         return out;
>>>     }
>>>     
>>>     
>>> // New "pretty" header:
>>> 
>>> struct pretty
>>> {
>>>     size_t s;
>>>     char c;
>>>     
>>>     pretty(size_t s = 0, char c = '\t') : s(s), c(c)
>>>     {
>>>     }
>>>     
>>>     ostream & indent(ostream & out)
>>>     {
>>>         for (size_t i = 0; i < s; ++ i)
>>>             out << c;
>>> 
>>>         return out;
>>>     }
>>>     
>>>     template <typename T>
>>>         void begin(char const * n, T const & t, ostream & out)
>>>         {
>>>             out << n << " = " << endl;
>>>             indent(out) << "{" << endl;
>>>             
>>>             ++ s;
>>>         }
>>>     
>>>     template <typename T>
>>>         void end(char const * n, T const & t, ostream & out)
>>>         {
>>>             -- s;
>>>             
>>>             indent(out) << "}";
>>>         }
>>>     
>>>     template <typename T>
>>>         void operator () (char const * n, T const & t, size_t i, size_t e, ostream & out)
>>>         {
>>>             indent(out);
>>>             
>>>             print(* this, n, t, out);
>>> 
>>>             if (i != e - 1)
>>>                 out << ',';
>>>             
>>>             out << endl;
>>>         }
>>> };
>>> 
>>> 
>>> struct sql
>>> {
>>>     template <typename T>
>>>         void begin(char const * n, T const & t, ostream & out) const
>>>         {
>>>             out << "SELECT ";
>>>         }
>>>     
>>>     template <typename T>
>>>         void end(char const * n, T const & t, ostream & out) const
>>>         {
>>>             out << " FROM " << n << ";";
>>>         }
>>>     
>>>     template <typename T>
>>>         void operator () (char const * n, T const & t, size_t i, size_t e, ostream & out) const
>>>         {
>>>             print(* this, n, out);
>>> 
>>>             if (i != e - 1)
>>>                 out << ", ";
>>>         }
>>> };
>>> 
>>> 
>>> // Usage example:
>>> 
>>> struct A
>>> {
>>>     int a = 0;
>>>     int b = 1;
>>>     int c = 2;
>>>     
>>>     // Generated by the compiler:
>>>     template <typename F, typename ...T>
>>>         struct for_each_member
>>>         {
>>>             void operator () (F && f, A const & a, T & ...t)
>>>             {
>>>                 f("a", a.a, 0, 3, t...);
>>>                 f("b", a.b, 1, 3, t...);
>>>                 f("c", a.c, 2, 3, t...);
>>>             }
>>>         };
>>> };
>>> 
>>> 
>>> struct B
>>> {
>>>     int d = 0;
>>>     int e = 1;
>>>     A f = A();
>>>     
>>>     // Generated by the compiler:
>>>     template <typename F, typename ...T>
>>>         struct for_each_member
>>>         {
>>>             void operator () (F && f, B const & b, T & ...t)
>>>             {
>>>                 f("d", b.d, 0, 3, t...);
>>>                 f("e", b.e, 1, 3, t...);
>>>                 f("f", b.f, 2, 3, t...);
>>>             }
>>>         };
>>> };
>>> 
>>>     
>>> int main()
>>> {
>>>     B b;
>>>     
>>>     cout << "Pretty print:" << endl;
>>>     print(pretty(), "b", b, cout) << endl;
>>>     cout << endl;
>>> 
>>>     cout << "SQL print:" << endl;
>>>     print(sql(), "b", b, cout) << endl;
>>> }
>>> 
>>> 
>>> 
>>> Outputs:
>>> 
>>> Pretty print:
>>> b = 
>>> {
>>>         d = 0,
>>>         e = 1,
>>>         f = 
>>>         {
>>>                 a = 0,
>>>                 b = 1,
>>>                 c = 2
>>>         }
>>> }
>>> 
>>> SQL print:
>>> SELECT d, e, f FROM b;
>>> 
>>> 
>>> -- 
>>> 
>>> Phil Bouchard
>>> Founder
>>> C.: (819) 328-4743
>>> 
>>> 
>>> 
>>> 
>>> 
>>>> On 3/10/20 1:56 PM, Phil Bouchard wrote:
>>>> 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

Received on 2020-03-13 11:44:28