Thread: Attributes as objects

  1. #1
    Registered User
    Join Date
    Jan 2008
    Posts
    24

    Attributes as objects

    Since primitive types in C++ are not objects and must be turned into objects via wrapper classes in order to use the "attributes as objects" accessor methodology, does this pose a significant risk to performance? Is it still considered good OO design to use a different accessor methodology?

  2. #2
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >does this pose a significant risk to performance?
    It depends on how you do it.

    >Is it still considered good OO design to use a different accessor methodology?
    No, thou shalt not deviate from what the OO gods dictate or thou shalt suffer the eternal damnation of maintaining bad code.
    My best code is written with the delete key.

  3. #3
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    the "attributes as objects" accessor methodology
    The what?
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    hmm... never heard of the term myself, but Uwyn C++ Coding Standard: Accessor Styles seems to describe what they are talking about.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    If it's that, then there's absolutely no relation to primitives that I can see. Why would they have to be wrapped?
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by CornedBee View Post
    If it's that, then there's absolutely no relation to primitives that I can see. Why would they have to be wrapped?
    "The main weakness of this approach is that when returning a reference to basic types (as in the rAge() method), it's impossible to perform checks on the provided value."
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    That applies to objects, too. If the owner wants to restrict the value to a subset of the domain of the type, you can't use the pattern. I still maintain that it has nothing to do with primitives.

    And of course, with references you bind yourself to a specific internal representation.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    That applies to objects, too. If the owner wants to restrict the value to a subset of the domain of the type, you can't use the pattern. I still maintain that it has nothing to do with primitives.
    I think the whole point is to get the object to perform the domain check by itself. Going by this reasoning, if you need to restrict the value to a subset of the domain of the type, it implies that you are using the wrong type. My guess is that the Uwyn programmers would then write yet another wrapper if they encounter this situation.

    Consequently, it does have something to do with primitives, since for the built-in types a self-performed domain check is quite impossible, whereas it is possible for class types.

    And of course, with references you bind yourself to a specific internal representation.
    I think that, to some extent, this is the case for all accessors.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    To return a reference, you need something to refer to. If you just return a value, you can compute it on the fly. If you get a value in a setter, you can store the result of a computation based on that, too.

    Any object should, of course, perform a domain check by itself. That's part of upholding the class invariant. Even built-in unsigned integers uphold their invariant: you cannot make a 16-bit unsigned integer hold anything outside [0,2^16), no matter how hard you try. So far, this has nothing to do with accessors (except the object's accessors themselves).
    We're talking about an object that contains sub-objects and wants to further constrain their domain. A game state class holding a play field and a player position wants to restrict the player position, which probably is a simple 2d or 3d point, to lie within the play field. The point is unlikely to have this restriction - it would have to know about the play field. That would make it larger than necessary and extremely non-generic.
    If the game state allows access to the player position by reference, it has no control over this further restriction of the domain. It cannot enforce it. And that's even though a point is no primitive.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #10
    Registered User
    Join Date
    Jan 2008
    Posts
    24
    I am trying to get away from returning references - hence why I mentioned creating wrapper classes for the primitives.

    I know it takes roughly twice as much memory to store a wrapped primitive object vs. storing the primitive itself, and I'm OK with that. What I was interested in finding out is if having these values wrapped would significantly affect the run-time speed, given that they would be getting accessed hundreds/thousands of times a minute.

    In other words, I care more about performance than proper OO design, but given the choice, I'd rather have my cake and eat it too.

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    To return a reference, you need something to refer to. If you just return a value, you can compute it on the fly. If you get a value in a setter, you can store the result of a computation based on that, too.
    I agree, and in this I was not careful with interpreting "specific internal representation", though I did qualify with "to some extent". I would rather discourage getters/setters in view that, as far as possible, a class should provide an interface by which it can perform its job without becoming like a POD struct that is a simple aggregation of data.

    I think that's the problem with this "attributes as object" method. It makes the class too much like a simple aggregation of data.

    We're talking about an object that contains sub-objects and wants to further constrain their domain. A game state class holding a play field and a player position wants to restrict the player position, which probably is a simple 2d or 3d point, to lie within the play field. The point is unlikely to have this restriction - it would have to know about the play field. That would make it larger than necessary and extremely non-generic.
    The solution would not be for the point to know about the play field. The solution would be to use a wrapper class for the point so as to enforce the restriction. This maintains the genericity of the point class while allowing the return by reference idea that this "attributes as objects" apparently requires.

    So, if we would follow the Uwyn coding standard, the choice of point objects for member variables is a poor one, if we want to provide such accessors/mutators/setters/getters.

    I am trying to get away from returning references - hence why I mentioned creating wrapper classes for the primitives.
    If that is the case, then you should use the second method listed by the coding standard I referred to.

    I know it takes roughly twice as much memory to store a wrapped primitive object vs. storing the primitive itself, and I'm OK with that. What I was interested in finding out is if having these values wrapped would significantly affect the run-time speed, given that they would be getting accessed hundreds/thousands of times a minute.
    I doubt it, but the only way to know for sure is to test.

    In other words, I care more about performance than proper OO design, but given the choice, I'd rather have my cake and eat it too.
    I would argue that both methods are proper OO design, if used sparingly. I would rather not maintain wrapper classes, so I would prefer the setter/getter method.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    If you write wrapper classes anyway, you can have more beautiful access than a return-reference function. Namely, you can write the wrappers as property accessor objects.
    Code:
    class WithProps
    {
    public:
      class Width_Prop;
      friend class Width_Prop;
      class Width_Prop : noncopyable
      {
        WithProps &outer;
        friend class WithProps;
        Width_Prop(WithProps &o) : outer(o) {}
    
      public:
        int operator =(int v) { if(v < 0) { throw domain_error(); } outer.m_width = v; }
        operator int() const { return outer.m_width; }
      };
    
      Width_Prop width;
    
      WithProps() : width(*this), m_width(0) {}
      WithProps(const WithProps &o) : width(*this), m_width(o.m_width) {}
      WithProps &operator =(const WithProps &o) { m_width = o.m_width; return *this; }
    
    private:
      int m_width;
    };
    Now you have C#-style properties. It's just a huge code overhead.
    Last edited by CornedBee; 01-15-2008 at 12:48 PM. Reason: Implemented assignment.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  13. #13
    Registered User
    Join Date
    Jan 2008
    Posts
    24
    Quote Originally Posted by CornedBee View Post
    If you write wrapper classes anyway, you can have more beautiful access than a return-reference function. Namely, you can write the wrappers as property accessor objects.
    Code:
    (removed for brevity)
    Now you have C#-style properties. It's just a huge code overhead.
    I don't suppose you could break this down with an explanation as to what you're doing, could you? I don't have a good book handy, and I'm having trouble finding examples of most of what you've done online.

    Edit: I should have clarified that I'm mostly interested in what you're doing concerning the Width_Prop class.

  14. #14
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    In short, the property has two parts:
    1) m_width, the actual data storage
    2) width, which is an object pretending to be the data storage.

    When the user writes code like
    int w = obj.width;
    then he gets the Width_Prop member. Then the type conversion operator is invoked to convert the Width_Prop to an int. This operator reads the real value from m_width.
    When the user writes code like
    obj.width = 10;
    then he assigns to the Width_Prop member, which means that the overloaded assignment operator is invoked. This operator checks the value for validity, then assigns it to m_width.

    The rest is just boilerplate. The outer class and Width_Prop need mutual friendship because they access each other's private stuff. Width_Prop is non-copyable and has a private constructor so that nothing outside the outer class can get the stupid idea to create a Width_Prop object. Width_Prop needs a reference to the outer class so that it can access m_width. The constructors of the outer class need to supply this reference. And because the outer class has a non-copyable and non-assignable member, copy constructor and assignment operator cannot be auto-generated by the compiler, so you have to do that yourself. (I forgot to implement the assignment operator. I'll do that in a moment.)

    As I said, lots of boilerplate, little use.

    There are problems. You can't do either of these, even though the syntax looks like it should work:
    Code:
    int *pi = &obj.width; // Error: can't convert Width_Prop* to int*.
    int &ri = obj.width; // Error: can't convert Width_Prop& to int&, or can't assign temporary to non-const reference.
    Worse, some compilers actually let you do the second thing, but modifying the value then won't have any effect.


    I think this is all stupid. Use a pair of accessor functions. There's not a single C++ programmer who won't understand what they're about.
    Code:
    int get_width() const;
    void set_width(int a_width);
    Names may vary depending on your convention: get_width/set_width, width/width, getWidth/setWidth, GetWidth/SetWidth. But they all mean the same.
    Sometimes the setters return something, too. But since it's not entirely clear what they should return - The new value, like the assignment operator (push through)? The old value, like set_new_handler, set_unexpected_handler, and many functions in the Win32 API (push out)? Or even the outer object itself, to allow for chaining like obj.setWidth(10).setHeight(10)? - I prefer to have them return nothing and avoid the ambiguity.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  15. #15
    Registered User
    Join Date
    Jan 2008
    Posts
    24
    Thanks for the explanation. I think it's all somewhat stupid as well, but to make the implementation as OO as feasibly possible, everything should theoretically be objects, including the class's data members. I know practically every programmer knows the get/set conventional accessor methodology, but a good portion of those who do also know that it's a good example of a convention of simplicity, rather than "proper" design.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. MURK - a small preview
    By Mario F. in forum Game Programming
    Replies: 27
    Last Post: 12-18-2006, 08:22 AM
  2. Replies: 60
    Last Post: 12-20-2005, 11:36 PM
  3. Question about cout an stack object?
    By joenching in forum C++ Programming
    Replies: 8
    Last Post: 05-08-2005, 10:10 PM
  4. chain of objects within pop framework help needed
    By Davey in forum C++ Programming
    Replies: 0
    Last Post: 04-15-2004, 10:01 AM
  5. array of objects?
    By *~*~*~* in forum C++ Programming
    Replies: 4
    Last Post: 05-31-2003, 05:57 PM