Thread: returning values (tuples)

  1. #1
    Registered User
    Join Date
    May 2006
    Posts
    630

    returning values (tuples)

    Hello

    I have 2 classes (parser and caller class).
    Caller class calls a function (.parse()) which is a member of parser class and should return 3 strings (objects).

    I could create 3 basic strings, and pass their references to parse() function which would then set/modify each of them. However, I dont like this kind of design so I decided to return a tuple (multi-pair) instead.

    I decided to wrap tuple<std::string, std::string, std::string> in a boost smart/shared pointer.

    Now when I want to access one of tuple members I have to call get function.
    The code becomes something like this:
    Code:
    typedef boost::tuple<std::string, std::string, std::string> data_type;
    boost::shared_ptr<data_type> ptr = parser.parse();
    std::string first_string = boost::tuples::get<0>(*ptr.get());
    std::string second_string = boost::tuples::get<1>(*ptr.get());
    //etc..
    The problem is I dont want to call ptr.get() each time to be able to access tuple members because I think its not a good way to do it because then I loose the real benefit of shared/smart pointers.

    Do you guys have any better idea how I could solve this problem?
    Maybe some different design would be more suitable?

    Thanks a lot for help

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    I would just wrap the access inside convenience functions, i.e. get_first(), get_second(), get_third().

  3. #3
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    1) *ptr suffices, *ptr.get() is unnecessary. And even if you call get() every time, you don't lose the real benefit, which is ownership and RAII semantics.

    2) You could write it like this:
    Code:
    std::string first_string, second_string, third_string;
    boost::tie(first_string, second_string, third_string) = *ptr;
    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
    Registered User
    Join Date
    May 2006
    Posts
    630
    Thanks for information!

    Since I dont want to have too much copies if not neccessary, I wonder if this is okay:

    Code:
    std::string &first_string, &second_string, &third_string;
    boost::tie(first_string, second_string, third_string) = *ptr;
    Thanks again!

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    That does not look right: references have to refer to something. In your code snippet, there is a point where first_string, second_string, and third_string are aliases of nothing, which is not allowed.
    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

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Nor is it allowed to reassign references, sorry. It'll all be better with r-value references.
    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

  7. #7
    Registered User
    Join Date
    May 2006
    Posts
    630
    Sorry, what do you mean by 'r-value references' ?

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    It's an upcoming feature of the next C++ standard, perhaps the most important feature, even though it seems so small.
    GCC 4.3 contains experimental support for it.
    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

  9. #9
    Registered User
    Join Date
    May 2006
    Posts
    630
    Could you please give me a simple example how it would look to reassign a reference (so I can get a better picture)?

    Thank you very much!

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    You can't re-assign references, not even the r-value type. What r-value references change is something else: they can bind to temporary values, even when they're non-const. But this little change allows so-called move construction: similar to copy construction, except that modifying the source object is allowed. In the case of std::string, for example, move construction would simply transfer the pointers (and thus the ownership) to the string content from one object to the other, similar to swapping two strings. Thus, only a few pointers (at most) are copied; no reallocations, no block transfer. Result: speed!

    The net result is that returning complex objects that are move-constructible becomes cheap. Why do you return a shared_ptr to the tuple? My guess is that you want to avoid the copying of the three strings that would result if you returned the tuple directly. But this copying would no longer happen. Consider:
    Code:
    std::string foo()
    {
      std::string t = "hello";
      return t;
    }
    
    void bar()
    {
      std::string s = foo();
    }
    Currently, this naively:
    1) Calls the const char * constructor (allocation, block transfer!) to construct t.
    2) Calls the copy constructor (allocation, block transfer!) to construct the return value from t.
    3) Calls the copy constructor (allocation, block transfer!) to construct s from the return value.

    With named return value optimization (implemented by an increasing number of compilers), it:
    1) Calls the const char * constructor (allocation, block transfer!) to construct t, which is the same as the return value.
    2) Calls the copy constructor (allocation, block transfer!) to construct s from the return value.

    That's good.

    With r-value references and NRVO, it:
    1) Calls the const char * constructor (allocation, block transfer!) to construct t, which is the same as the return value.
    2) Calls the move constructor (a few pointer copies) to construct s from the return value.

    Ooh, NICE!


    Now, since std::tuple (yeah, boost::tuple will be part of the next standard) will support move construction if all of its members do, the same optimization applies to it.
    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

  11. #11
    Registered User
    Join Date
    May 2006
    Posts
    630
    Thanks for this information!
    Really good explanation indeed!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. disposing error
    By dropper166 in forum C# Programming
    Replies: 2
    Last Post: 03-30-2009, 11:53 PM
  2. returning multiple values from functions?
    By jamesn56 in forum C++ Programming
    Replies: 5
    Last Post: 08-23-2005, 01:10 PM
  3. Struct Values
    By Muphin in forum C++ Programming
    Replies: 5
    Last Post: 08-13-2005, 09:24 PM
  4. Please Help - Problem with Compilers
    By toonlover in forum C++ Programming
    Replies: 5
    Last Post: 07-23-2005, 10:03 AM
  5. printf returning values problem
    By hayai32 in forum C Programming
    Replies: 20
    Last Post: 06-25-2005, 12:16 PM