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

Fornux Logo


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

Fornux Logo


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

Fornux Logo

On Mar 10, 2020, at 8:50 AM, Phil Bouchard <phil@fornux.com> 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

Fornux Logo


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


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


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

Fornux Logo

On Mar 8, 2020, at 7:36 PM, Phil Bouchard <phil@fornux.com> 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


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


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@lists.isocpp.org> 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


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@fornux.com> 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


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@lists.isocpp.org> 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
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals