CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 12 of 12
  1. #1
    Join Date
    Oct 2008
    Posts
    15

    Storing implicitly-created temporaries in references

    The following is not exactly what I'm trying to do but has been simplified to better illustrate the problem.

    I've got some code that looks something like this:

    Code:
    class Double
    {
    public:
        Double (double initialValue) : value(initialValue) {};
        virtual ~Double () {};
        virtual double get () const { return value; }
        virtual double set (const double newValue) { return value = newValue; }
    private:
        double value;
    }; // Double
    
    class Add : public
    {
    public:
        Add (const Double& lhs, const Double& rhs) : left(lhs), right(rhs) {};
        double get () const { return left.get() + right.get(); }
    private:
        const Double& left;
        const Double& right;
    }; // Add
    Now, let's say I have a function that takes a Double like this:

    Code:
    void printMe (const Double& double) { cout << double.get() << endl; }
    I want to be able to do all of the following:
    Code:
    Double double1(1.0);
    Double double2(1.0);
    printMe(double1 + double2);
    printMe(double1 + 1.0);
    printMe(1.0 + double2);
    printMe(1.0 + 1.0);
    The thing is, I want the implicit conversions from double to work, but when I don't use the implicit conversion and instead pass in an already declared Double object, I do not want that object to be copied.

    I could do this...

    Code:
    const Add&
        operator+ (
            const Double& lhs,
            const Double& rhs
            )
        {
        return Add(lhs, rhs);
        }
    ...but this code seems to insist on calling the Add copy constructor and produces a warning about returning a reference to a temporary.

    I could also do this...
    Code:
    const Add&
        operator+ (
            const Double& lhs,
            const Double& rhs
            )
        {
        return *(new Add(lhs, rhs));
        }
    ...but then I have no way of knowing when the Add object needs to be destroyed.

    I need to return a reference, not a pointer, because there is one of these classes for every operator, and I needs compound statements like (x + y) / z to work.

    To reiterate: I have a base class - let's call it Double - that cannot be copied. I then have operator classes (like Add) derived from Double that store references to two other Doubles. I need overloaded operator functions to instantiate these operator classes and return them, taking in both already-created Double objects (without copying them) or temporaries implicitly-created at the call.

    I cannot figure out how to do this without causing memory leaks, compiler warnings, or copy-constructor calls. Any ideas?

    As for why I need to do this: there are other classes that derive from Double (though it's not really called that). These classes do not store an internal value but instead get values from external hardware; such classes are difficult and expensive to copy. Then, I have a function (let's call it control()) that takes in two arguments - a Double called "input" and a Double called "output". This function spawns an OS task with a timer that periodically sends the input to the output. Here's an example function call:

    Code:
    control(input + 1.0, output);
    In this case, the expression "input + 1.0" must be reevaluated every time the timer goes off - not just once at the function call. It is sort of like a nullary lambda function.

    Thanks for any assistance.

  2. #2
    Join Date
    Oct 2008
    Posts
    1,456

    Re: Storing implicitly-created temporaries in references

    if you use the const reference returned by

    Code:
    const Add&
        operator+ (
            const Double& lhs,
            const Double& rhs
            )
        {
        return Add(lhs, rhs);
        }
    then it's undefined behaviour, because the Add temporary is destroyed at the end of the function call (and this is the reason your compiler is complaining).

    So, writing "Add(Add(a,b),c)" is UB.

    You can write

    Code:
    Add
        operator+ (
            const Double& lhs,
            const Double& rhs
            )
        {
        return Add(lhs, rhs);
        }
    and with any chance (depending on the compiler) no copy will be performed, and the Add object will be passed to the stack of the calling expression.
    Anyway, all the references stored in the objects tree are valid only until the complete expression is evaluated.

    So, if you write

    Code:
    Add     add_instance(Add(a,b),c);
    /* use add_instance */
    is UB, because the inner Add temporary has been destroyed.

    So I suspect there are two possible choices (that comes to my mind now ):

    1) define two hierarchies of classes: a "construction" hierarchy ( Add,Mul,... ) used to setup your preferred syntax and a "concrete" hierarchy (Add::Concrete, Mul::Concrete,...) that are the objects that actually convey all the information you need to run your lambda-like functions.

    So, you could write

    Code:
    double  c;
    LFun    lfun = Add(Add(1.0,2.0),c); // lfun embeds a container of properly coordinated "concrete" objects
    lfun.Evaluate();

    2) avoid temporaries ( e.g. renouncing the lambda-like syntax )

  3. #3
    Join Date
    Feb 2005
    Location
    "The Capital"
    Posts
    5,306

    Re: Storing implicitly-created temporaries in references

    Quote Originally Posted by (V|G)CC4ME View Post
    The thing is, I want the implicit conversions from double to work, but when I don't use the implicit conversion and instead pass in an already declared Double object, I do not want that object to be copied.
    What do you mean? I couldn't get your problem. If you don't have expressions that might involve implicit conversions - there should be no copy created. Which copy/temporary are you talking about?

  4. #4
    Join Date
    Oct 2008
    Posts
    15

    Re: Storing implicitly-created temporaries in references

    Quote Originally Posted by superbonzo View Post
    So I suspect there are two possible choices (that comes to my mind now ):

    1) define two hierarchies of classes: a "construction" hierarchy ( Add,Mul,... ) used to setup your preferred syntax and a "concrete" hierarchy (Add::Concrete, Mul::Concrete,...) that are the objects that actually convey all the information you need to run your lambda-like functions.

    So, you could write

    Code:
    double  c;
    LFun    lfun = Add(Add(1.0,2.0),c); // lfun embeds a container of properly coordinated "concrete" objects
    lfun.Evaluate();

    2) avoid temporaries ( e.g. renouncing the lambda-like syntax )
    I'm not quite sure what you mean by this. Are you saying that Add should create a new Add::Concrete and store a handle to it? How does Add::Concrete store the operands?

    Quote Originally Posted by exterminator View Post
    What do you mean? I couldn't get your problem. If you don't have expressions that might involve implicit conversions - there should be no copy created. Which copy/temporary are you talking about?
    Basically, I have one class - Double (capital D). Then, I have operator+() taking in two Doubles as arguments. Two types of Double objects could be passed into this function. The first is a hardware object derived from Double that has been declared elsewhere - this object cannot be copied when I place the call to operator+(). The second is a Double implicitly-constructed at the call site from a double (built-in, lowercase d) - this object needs to be copied because it is a temporary. My problem is how to tell the difference.

    Right now, I've got it working using a boost:shared_ptr<> based system in which you do arithmetic with the pointers themselves rather than the objects they refer to (i.e. pointer1 + pointer2 instead of *pointer1 + *pointer2), which is a bit weird and not what many C programmers are used to. Then, there is an implicit conversion all the way from double to shared_ptr<Double> that creates a new Double and stores it in the pointer. Because it is a managed pointer, the created Double will be destroyed when it is no longer needed.

    I would like to get it working with references if possible, however.

  5. #5
    Join Date
    Nov 2003
    Posts
    1,902

    Re: Storing implicitly-created temporaries in references

    >> The following is not exactly what I'm trying to do but...
    Nothing wrong with starting with exactly what you're trying to do.

    >> you do arithmetic with the pointers themselves rather than the objects they refer to
    >> I would like to get it working with references if possible
    So get rid of the shared_ptr<> and regular pointer overloads of + and just use the const Double& version. Then simply add a * in front of any (*ptr1 + *ptr2) expressions.

    gg

  6. #6
    Join Date
    Oct 2008
    Posts
    15

    Re: Storing implicitly-created temporaries in references

    Quote Originally Posted by Codeplug View Post
    >> The following is not exactly what I'm trying to do but...
    Nothing wrong with starting with exactly what you're trying to do.

    >> you do arithmetic with the pointers themselves rather than the objects they refer to
    >> I would like to get it working with references if possible
    So get rid of the shared_ptr<> and regular pointer overloads of + and just use the const Double& version. Then simply add a * in front of any (*ptr1 + *ptr2) expressions.

    gg
    I am programming for an embedded system - specifically the National Instruments cRIO.

    I have two base classes, Analog and Digital. By themselves, these are effective doubles and bools, respectively. An implicit constructor exists to create an Analog from a double and a Digital from a bool. These classes have get() and set() functions that access and store an internal double and bool.

    Derived classes also exist that overload the get() and set() functions of Analog and Digital to access values from external hardware rather than storing them internally.

    I then have this free function called control() that takes in either two Analogs or two Digitals. The first argument is the "input" and the second argument is the "output". The control() function stores a reference or pointer to the input and output and sets up an OS task that periodically checks the value of the input and sends it to the output.

    Here's an example of the syntax I want to be able to use:
    Code:
    AnalogInput in(1); // This is a hardware object derived from Analog - cannot be copied
    AnalogOutput out(1); // Another hardware object derived from Analog
    control(in, out); // Should periodically send the value of in to out
    control(1.0, out); // Should periodically send 1.0 to out
    control(in + 1.0, out); // Should periodically get value of in, add 1.0, and send the result to out
    The idea here is that whatever expression I pass to control() must somehow be stored and reevaluated every time the OS task asks for it. This must work whether I pass in hardware objects (which cannot be copied) or implicitly-created base objects (which must be copied, because they are temporary).

    I don't really mind if I have to store the hardware objects in managed or unmanaged pointers, and I don't care whether or not I have to dereference the objects when passing them to control(). The implicit conversions, however, need to work. control(in + new Analog(1.0), out) is not an acceptable syntax.

  7. #7
    Join Date
    Nov 2003
    Posts
    1,902

    Re: Storing implicitly-created temporaries in references

    What are the prototype(s) for control? Do they take references to base classes? Is it a template?

    gg

  8. #8
    Join Date
    Oct 2008
    Posts
    15

    Re: Storing implicitly-created temporaries in references

    Quote Originally Posted by Codeplug View Post
    What are the prototype(s) for control? Do they take references to base classes? Is it a template?

    gg
    The prototypes are:

    control(const Analog&, Analog&);
    control(const Digital&, Digital&);

    I can make them take in pointers rather than references if that would work better.

  9. #9
    Join Date
    Nov 2003
    Posts
    1,902

    Re: Storing implicitly-created temporaries in references

    >> control(in + 1.0, out);
    One way to express "in + 1.0" in this case is to use an adapter object which supports the Analog/Digital interface, and performs the operation for you on each invocation of get(). For example:
    Code:
    #include <iostream>
    using namespace std;
    
    class Double
    {
        double m_val;
    public:
        Double() : m_val(0) {}
        Double(double d) : m_val(d) {}
        virtual ~Double() {}
        virtual double get() const {return m_val;}
        virtual void set(const double &d) {m_val = d;}
    };//Double
    
    // DoubleGetAdapter - Supports Double interface. Forwards calls to get() to 
    // m_pval->get(), the result goes through the GetOp_fn "get operation functor".
    template<typename GetOp_fn>
    class DoubleGetAdapter : public Double
    {
        const Double *m_pval;
        const GetOp_fn m_op;
    public:
        DoubleGetAdapter(const Double *p, 
                         const GetOp_fn &fn = GetOp_fn()) : m_pval(p), m_op(fn) {}
        virtual ~DoubleGetAdapter(){}
        virtual double get() const {return m_op(m_pval->get());}
        virtual void set(const double&) {}
    };//DoubleGetAdapter
    
    struct AddOne
    {
        double operator()(const double &d) const
        {
            return d + 1.0;
        }//operator()
    };//AddOne
    
    void control(const Double &in, Double &out)
    {
        out.set(in.get());
    }//control
    
    int main()
    {
        Double d1(1.0), d2;
    
        control(DoubleGetAdapter<AddOne>(&d1), d2);
        cout << d2.get() << endl;
    
        return 0;
    }//main
    I may be mixing terms a bit, but the idea is that the get() interface is "adapted" to suit your needs. Notice that the adapter class uses a pointer and not a reference to avoid any potential undefined behavior.

    gg

  10. #10
    Join Date
    Oct 2008
    Posts
    1,456

    Re: Storing implicitly-created temporaries in references

    Quote Originally Posted by (V|G)CC4ME
    I'm not quite sure what you mean by this. Are you saying that Add should create a new Add::Concrete and store a handle to it? How does Add::Concrete store the operands?
    Yes. I mean, you have to make a recursive copy of the expression tree of "syntax" objects into an equivalent tree of concrete objects.

    for example, via polymorphism:

    Code:
    class ConcreteObject
    {
        /*...*/
    };
    
    class SyntaxObject
    {
        virtual void Build( std::list<ConcreteObject*> nodes ) const= 0;
    };
    
    class SomeFunction: public SyntaxObject
    {
        const SyntaxObject& op_;
        
        class Concrete: public ConcreteObject
        {
            ConcreteObject* op_;
    
        public:
            Concrete( ConcreteObject* op ): op_(op) {}
        };
    
    public:
        SomeFunction( const SyntaxObject& op ): op_(op) {}
    
        void Build( std::list<ConcreteObject*> nodes ) const
        {
            op_.Build( nodes );
    
            nodes.push_back( new Concrete( nodes.back() ) );
            // above, I assume op_ stores the "result" node at the end of the list
        }
    };
    
    class Double: public SyntaxObject
    {
        double value_;
        
        class Concrete: public ConcreteObject
        { /*...*/ };
    
    public:
        Double( double value ): value_(value) {}
        
        /*here goes a Build implementation conceptually similiar to SomeFunction::Bind*/
    };
    
    class Add: public SyntaxObject
    {
        const SyntaxObject& left_;
        const SyntaxObject& right_;
    
        class Concrete: public ConcreteObject
        { /*...*/ };
    
    public:
        Add( const SyntaxObject& left,const SyntaxObject& right ): left_(left),right_(right) {}
        
        /*here goes a Build implementation conceptually similiar to SomeFunction::Bind*/
    };
    
    Add operator+(const SyntaxObject& left,const SyntaxObject& right)
    {
        return Add(left,right);
    }
    
    void control(const SyntaxObject& left,const SyntaxObject& right)
    {
        std::list<ConcreteObject*> left_operand; // here you can use any "handle" you like and any container you like
        std::list<ConcreteObject*> right_operand;
    
        left.Build( left_operand );
        right.Build( right_operand );
        
        // here you can store/manipulate/return the "concrete" tree
    }
    
    int main()
    {
        control( SomeFunction( SomeFunction(1.0) + 2.0 + SomeFunction(3.0) ), SomeFunction(4.0) );
        
        // from now on, the SyntaxObject's are destroyed, whilst the ConcreteObject's are not ( off
        // course, it depends on what control does...)
    }
    Off course, there are infintely many ways of doing this (and the code above is just a suggestion that needs further tuning ), but the idea is to have a tree of syntax objects (that is, an "aggregate" of temporaries and const references whose life is limited to the containing expression ) that is recursively copied in a tree of concrete objects ( that carries any data you want, from pointers to external resources or whatever). In this way, you decouple the syntax of your lamda-like functions to the actual internal implementation.
    Last edited by superbonzo; January 28th, 2009 at 09:01 AM.

  11. #11
    Join Date
    Oct 2008
    Posts
    15

    Re: Storing implicitly-created temporaries in references

    Ah, I see. I think the complexity of this lambda-like syntax is starting to outweigh its benefits.

    Thanks for all the help guys.

  12. #12
    Join Date
    Jun 2008
    Posts
    37

    Re: Storing implicitly-created temporaries in references

    That's thrilling and really am worried about what-chya-call-BENEFITS. [Share plz plz plz offer more more more help plz]

    Sounds good you realize it!


    Xua roi d9iem

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured