Using operator->

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Roger Leigh

    Using operator->

    I've written a simple container template class to contain a single
    value. This emits a signal when the value is changed (it's used as a
    notifier of changes), and listeners can connect to its changed signal.

    i.e. field<int> i(2);
    i = 4; // field<int>::m_v alue = 4; changed signal is emitted.

    Currently, the contained value may be accessed via get_value() and
    set_value() methods, and for ease of use, operator= and some type
    conversions are provided. However, accessing a member function
    requires a call to get_value(), and I'd like to overload operator->
    and operator* to allow access. Ideally, I'd like a const version
    (changes to the value are not allowed) and a non-const version, which
    allows changes and emits a signal. However, I can't see that this is
    possible (condensed for brevity):

    template<typena me T>
    class field {
    private:
    value_type m_value;
    SigC::Signal0<v oid> m_signal_change d; // libsigc++ signal type.
    public:
    typedef T value_type;

    field(): m_value() {}
    field(const value_type& value): m_value(value) {}
    field(const field<value_typ e>& rhs): m_value(rhs.m_v alue),
    m_signal_change d() {}
    virtual ~field() {}

    value_type& get_value() { return m_value; }
    const value_type& get_value() const { return m_value; }

    void set_value(const value_type& value)
    { m_value = value; m_signal_change d.emit(); }

    field<value_typ e>& operator = (const field<value_typ e>& rhs)
    { set_value(rhs.m _value); return *this; }

    field<value_typ e>& operator = (const value_type& rhs)
    { set_value(rhs); return *this; }

    operator const value_type& () const { return m_value; }

    SigC::Signal0<v oid>& signal_changed( ) { return m_signal_change d; }
    }; // class field

    If I provide a method like this:

    const value_type *operator -> () { return &m_value; }

    this is OK, but for the non-const version, I want to do this:

    value_type *operator -> () { return &m_value; m_signal_change d() }

    i.e. I want to emit the changed signal /after/ the caller has got the
    pointer, used the method/member chosen and finished. If I emit the
    signal before, the change won't yet have happened. However, I need to
    return the pointer, so this is obviously impossible.

    In addition, I'd like to make the const version the default (if a
    const method is called from a non-const pointer), if possible, so that
    the changed signal is only emitted on a genuine state change.

    Are either of these possible using standard C++? If not, could anyone
    suggest a different design to achieve the goal (signal emission after
    value change)?


    Many thanks,
    Roger

    --
    Roger Leigh

    Printing on GNU/Linux? http://gimp-print.sourceforge.net/
    GPG Public Key: 0x25BFB848. Please sign and encrypt your mail.


    -----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
    http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
    -----== Over 100,000 Newsgroups - 19 Different Servers! =-----
  • lilburne

    #2
    Re: Using operator-&gt;



    Roger Leigh wrote:[color=blue]
    > i.e. I want to emit the changed signal /after/ the caller has got the
    > pointer, used the method/member chosen and finished. If I emit the
    > signal before, the change won't yet have happened. However, I need to
    > return the pointer, so this is obviously impossible.
    >
    > In addition, I'd like to make the const version the default (if a
    > const method is called from a non-const pointer), if possible, so that
    > the changed signal is only emitted on a genuine state change.
    >
    > Are either of these possible using standard C++? If not, could anyone
    > suggest a different design to achieve the goal (signal emission after
    > value change)?
    >[/color]

    You can't make the compiler use the const version of a function that is
    overloaded on const alone i.e., the non-const method will always be
    called on non-const objects. You have to differentiate the functions by
    name i.e, rename non-const version of operator->() to be something like
    get_value_to_ch ange(). The long name encourages use of const values.

    If you want to broadcast changes after the modification then you'll need
    to use a set method and abandon the operator-> idea.

    Comment

    • Michael Mellor

      #3
      Re: Using operator-&gt;

      Roger Leigh wrote:[color=blue]
      > I've written a simple container template class to contain a single
      > value. This emits a signal when the value is changed (it's used as a
      > notifier of changes), and listeners can connect to its changed signal.
      >
      > i.e. field<int> i(2);
      > i = 4; // field<int>::m_v alue = 4; changed signal is emitted.
      >
      > Currently, the contained value may be accessed via get_value() and
      > set_value() methods, and for ease of use, operator= and some type
      > conversions are provided. However, accessing a member function
      > requires a call to get_value(), and I'd like to overload operator->
      > and operator* to allow access. Ideally, I'd like a const version
      > (changes to the value are not allowed) and a non-const version, which
      > allows changes and emits a signal. However, I can't see that this is
      > possible (condensed for brevity):
      >
      > template<typena me T>
      > class field {
      > private:
      > value_type m_value;
      > SigC::Signal0<v oid> m_signal_change d; // libsigc++ signal type.
      > public:
      > typedef T value_type;[/color]
      This typedef needs to go before the line (value_type m_value).
      [color=blue]
      >
      > field(): m_value() {}
      > field(const value_type& value): m_value(value) {}
      > field(const field<value_typ e>& rhs): m_value(rhs.m_v alue),
      > m_signal_change d() {}
      > virtual ~field() {}
      >[/color]

      Are you not risking the value being changed and not having a signal
      emitted with the following method.[color=blue]
      > value_type& get_value() { return m_value; }
      >
      > const value_type& get_value() const { return m_value; }
      >
      > void set_value(const value_type& value)
      > { m_value = value; m_signal_change d.emit(); }
      >
      > field<value_typ e>& operator = (const field<value_typ e>& rhs)
      > { set_value(rhs.m _value); return *this; }
      >
      > field<value_typ e>& operator = (const value_type& rhs)
      > { set_value(rhs); return *this; }
      >
      > operator const value_type& () const { return m_value; }
      >
      > SigC::Signal0<v oid>& signal_changed( ) { return m_signal_change d; }
      > }; // class field
      >
      > If I provide a method like this:
      >
      > const value_type *operator -> () { return &m_value; }
      >
      > this is OK, but for the non-const version, I want to do this:
      >
      > value_type *operator -> () { return &m_value; m_signal_change d() }
      >
      > i.e. I want to emit the changed signal /after/ the caller has got the
      > pointer, used the method/member chosen and finished. If I emit the
      > signal before, the change won't yet have happened. However, I need to
      > return the pointer, so this is obviously impossible.
      >
      > In addition, I'd like to make the const version the default (if a
      > const method is called from a non-const pointer), if possible, so that
      > the changed signal is only emitted on a genuine state change.
      >
      > Are either of these possible using standard C++? If not, could anyone
      > suggest a different design to achieve the goal (signal emission after
      > value change)?
      >[/color]
      I am not sure I completely understood what you want but I will try. Are
      you wanted to provide an operator-> for class field so that if T is a
      structure you can access the individual fields? If so I don't think you
      will be able to do that.

      Michael Mellor

      Comment

      • Roger Leigh

        #4
        Re: Using operator-&gt;

        Michael Mellor <news-at-@michaelmellor-dot-.com> writes:
        [color=blue]
        > Roger Leigh wrote:[/color]
        [color=blue][color=green]
        >> template<typena me T>
        >> class field {
        >> private:
        >> value_type m_value;
        >> SigC::Signal0<v oid> m_signal_change d; // libsigc++ signal type.
        >> public:
        >> typedef T value_type;[/color][/color]
        [color=blue]
        > This typedef needs to go before the line (value_type m_value).[/color]

        For what reason? Is this just a style issue? (Maybe this is just the C
        programmer in me declaring everything before it's used?)
        [color=blue][color=green]
        >> field(): m_value() {}
        >> field(const value_type& value): m_value(value) {}
        >> field(const field<value_typ e>& rhs): m_value(rhs.m_v alue),
        >> m_signal_change d() {}
        >> virtual ~field() {}
        >>[/color]
        >
        > Are you not risking the value being changed and not having a signal
        > emitted with the following method.[color=green]
        >> value_type& get_value() { return m_value; }[/color][/color]

        Yes, since this is the only possible way to call non-const methods in
        contained compounds. If you call this, you need to manually emit the
        changed signal (field<T>::sign al_changed().em it()).
        [color=blue][color=green]
        >> const value_type& get_value() const { return m_value; }
        >> void set_value(const value_type& value)
        >> { m_value = value; m_signal_change d.emit(); }
        >> field<value_typ e>& operator = (const field<value_typ e>& rhs)
        >> { set_value(rhs.m _value); return *this; }
        >> field<value_typ e>& operator = (const value_type& rhs)
        >> { set_value(rhs); return *this; }
        >> operator const value_type& () const { return m_value; }
        >> SigC::Signal0<v oid>& signal_changed( ) { return m_signal_change d;
        >> }
        >> }; // class field
        >> If I provide a method like this:
        >> const value_type *operator -> () { return &m_value; }
        >> this is OK, but for the non-const version, I want to do this:
        >> value_type *operator -> () { return &m_value; m_signal_change d()
        >> }
        >> i.e. I want to emit the changed signal /after/ the caller has got the
        >> pointer, used the method/member chosen and finished. If I emit the
        >> signal before, the change won't yet have happened. However, I need to
        >> return the pointer, so this is obviously impossible.
        >> In addition, I'd like to make the const version the default (if a
        >> const method is called from a non-const pointer), if possible, so that
        >> the changed signal is only emitted on a genuine state change.
        >> Are either of these possible using standard C++? If not, could
        >> anyone
        >> suggest a different design to achieve the goal (signal emission after
        >> value change)?
        >>[/color]
        > I am not sure I completely understood what you want but I will
        > try. Are you wanted to provide an operator-> for class field so that
        > if T is a structure you can access the individual fields? If so I
        > don't think you will be able to do that.[/color]

        This is exactly what I want to do, and I agree with your conclusions.
        What I've done is just provide an "operator -> () const", so it's
        effectively read-only.

        I've found a solution for non-const methods: If the contained class
        uses the same signal mechanism as the field class (SigC++), I can
        connect the changed signal of the contained class to the changed
        signal of the field contained, so that the signal cascades, and I get
        the behaviour I want. That is, I listen to the changed notification
        and then notify my own listeners. This is useful, since it can nest
        arbitrarily deeply for complex contained data structures.


        Many thanks to you (and lilburne) for increasing my understanding!


        Regards,
        Roger

        --
        Roger Leigh

        Printing on GNU/Linux? http://gimp-print.sourceforge.net/
        GPG Public Key: 0x25BFB848. Please sign and encrypt your mail.


        -----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
        http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
        -----== Over 100,000 Newsgroups - 19 Different Servers! =-----

        Comment

        • Michael Mellor

          #5
          Re: Using operator-&gt;

          Roger Leigh wrote:[color=blue]
          > Michael Mellor <news-at-@michaelmellor-dot-.com> writes:
          >
          >[color=green]
          >>Roger Leigh wrote:[/color]
          >
          >[color=green][color=darkred]
          >>> template<typena me T>
          >>> class field {
          >>> private:
          >>> value_type m_value;
          >>> SigC::Signal0<v oid> m_signal_change d; // libsigc++ signal type.
          >>> public:
          >>> typedef T value_type;[/color][/color]
          >
          >[color=green]
          >>This typedef needs to go before the line (value_type m_value).[/color]
          >
          >
          > For what reason? Is this just a style issue? (Maybe this is just the C
          > programmer in me declaring everything before it's used?)
          >[/color]
          What compiler do you use that allows a type to be used before the typedef?
          I get:

          $ g++ -Wall -pedantic c.cc
          c.cc:4: error: 'value_type' is used as a type, but is not defined as a type.

          and

          "ComeauTest .c", line 4: error: identifier "value_type " is undefined
          value_type m_value;

          Michael Mellor

          Comment

          • Roger Leigh

            #6
            Re: Using operator-&gt;

            Michael Mellor <news-at-@michaelmellor-dot-.com> writes:
            [color=blue]
            > Roger Leigh wrote:[color=green]
            >> Michael Mellor <news-at-@michaelmellor-dot-.com> writes:
            >>[color=darkred]
            >>>Roger Leigh wrote:[/color]
            >>[color=darkred]
            >>>> template<typena me T>
            >>>> class field {
            >>>> private:
            >>>> value_type m_value;
            >>>> SigC::Signal0<v oid> m_signal_change d; // libsigc++ signal type.
            >>>> public:
            >>>> typedef T value_type;[/color]
            >>[color=darkred]
            >>>This typedef needs to go before the line (value_type m_value).[/color]
            >> For what reason? Is this just a style issue? (Maybe this is just
            >> the C
            >> programmer in me declaring everything before it's used?)
            >>[/color]
            > What compiler do you use that allows a type to be used before the typedef?
            > I get:
            >
            > $ g++ -Wall -pedantic c.cc
            > c.cc:4: error: 'value_type' is used as a type, but is not defined as a type.
            >
            > and
            >
            > "ComeauTest .c", line 4: error: identifier "value_type " is undefined
            > value_type m_value;[/color]

            My apologies--I moved the private part of the class to the top in my
            posting to make the problem clearer. It's actually right at the
            bottom. I'm using GCC 3.3.3.

            The full class (licence boilerplate stripped) is:
            // database field container -*- C++ -*-
            #ifndef PQXX_OBJECT_FIE LD_H
            #define PQXX_OBJECT_FIE LD_H

            #include <sigc++/signal.h>

            namespace pqxxobject
            {
            /**
            * Database field template class.
            * This class is used to represent a single field in a row of a
            * table. This is a single value belonging to a column in a row,
            * rather than the whole column.
            *
            * As well as storing value, the class has the ability to emit
            * signals when the field value is changed. Listeners (e.g. user
            * interface widgets) may connect to the signal and will receive
            * notification of changes as they occur.
            */
            template<typena me T>
            class field
            {
            public:
            typedef T value_type;

            /// The constructor.
            field():
            m_value()
            {}

            /// The constructor.
            field(const value_type& value):
            m_value(value)
            {}

            /// The copy constructor.
            field(const field<value_typ e>& rhs):
            m_value(rhs.m_v alue),
            m_signal_change d()
            {}

            virtual ~field()
            {}

            /// Access member functions.
            const value_type *operator -> () const
            {
            return &m_value;
            }

            const value_type& operator * () const
            {
            return m_value;
            }

            /// Overloaded assignment operator.
            field<value_typ e>& operator = (const field<value_typ e>& rhs)
            {
            set_value(rhs.m _value);
            return *this;
            }

            /// Overloaded assignment operator.
            field<value_typ e>& operator = (const value_type& rhs)
            {
            set_value(rhs);
            return *this;
            }

            /// Conversion operator.
            operator const value_type& () const
            {
            return m_value;
            }

            /**
            * Get the contained value by reference.
            * @returns a reference to the value.
            */
            value_type& get_value()
            {
            return m_value;
            }

            /**
            * Get the contained value by constant reference.
            * @returns a constant reference to the value.
            */
            const value_type& get_value() const
            {
            return m_value;
            }

            /**
            * Set the contained value.
            * @param value the value to set.
            */
            void set_value(const value_type& value)
            {
            m_value = value;
            m_signal_change d.emit();
            }

            /**
            * Signal emitted on value change.
            * @returns the signal.
            *
            * For example:
            * @code
            * field<int> col;
            * someclass listener;
            * col.signal_chan ged().connect
            * ( SigC::slot(list ener, &someclass::on_ col_changed() );
            * @endcode
            * i.e. a class method (listener.on_co l_changed()) will be called
            * when the value is changed.
            */
            SigC::Signal0<v oid>& signal_changed( )
            {
            return m_signal_change d;
            }

            private:
            /// The contained value.
            value_type m_value;
            /// The changed signal.
            SigC::Signal0<v oid> m_signal_change d;

            }; // class field

            }; // namespace pqxxobject

            #endif // PQXX_OBJECT_FIE LD_H


            --
            Roger Leigh

            Printing on GNU/Linux? http://gimp-print.sourceforge.net/
            GPG Public Key: 0x25BFB848. Please sign and encrypt your mail.


            -----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
            http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
            -----== Over 100,000 Newsgroups - 19 Different Servers! =-----

            Comment

            Working...