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,
- ...
#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