views:

305

answers:

10

Let's say there is a function like

void SendToTheWorld(const Foo& f);

and I need to preprocess Foo object before sending

X PreprocessFoo(const Foo& f)
{
   if (something)
   {
      // create new object (very expensive).
      return Foo();
   }

   // return original object (cannot be copied)
   return f;
}

Usage

Foo foo;
SendToTheWorld(PreprocessFoo(foo));

So X PreprocessFoo() function should be able to return original object or copy/modify and than return new one. I cannot return const Foo& as it may refer a temporary object. Also I don't like to create Foo on the heap.

Perfectly, X should be some kind of union of const Foo& and Foo that may be treated as const Foo&. Any idea how to do that in more elegant way?

My current solution:

Foo PreprocessFoo(const Foo& f)
{
   // create new object (very expensive).
   return Foo();
}

Usage:

Foo foo;

if (something)
{
   SendToTheWorld(PreprocessFoo(foo));
}
else
{
   SendToTheWorld(foo);
}
+2  A: 

I’m not 100% clear on what you mean but if you just want to omit an unnecessary copy of Foo in the return value, make your function small (right now, it is) and rely on the optimizing compiler to take care of your problem.

Once the function has been inlined, the compiler will elide unnecessary copies of Foo.

(Note for the interested: NRVO (named return value optimization) cannot be applied here since no unique name can be assigned to all instances of possible return values.)

Konrad Rudolph
I cannot copy original object. It is pretty expensive and there is no copy constructor. It is only copied in extremely specific situation.
bocco
A: 

The only way I can think of is to return a Foo object instead of a reference.

Maurice Perry
A: 
Foo PreprocessFoo(const Foo& f)
{
   if (something)
   {
      // create new object
      return Foo();
   }

   // return original object
   return f;
}
anon
Yep. But the goal is not to copy original Foo object. It is pretty expensive operation (there is no copy ctr).
bocco
A: 

What's wrong with your current solution? Copy-constructor for Foo could be omitted due to RVO. Reference to temporary object will valid all time while function SendToTheWorld is executed.

Kirill V. Lyadvinsky
+1  A: 

If you do not want to create the object in heap then you should create it before calling the function and pass a reference down there. Think of the nature of the temporary: the function creates the new object in stack and then returns, dropping its stack frame. So the object should either be there or it should be copied over there.

Michael Krelin - hacker
A: 

If the lifetime of your Foo object is not to be determined by just one piece of code, why not use a shared_ptr instead?

void SendToTheWorld( shared_ptr<Foo>& pFoo );

shared_ptr<Foo> preProc( shared_ptr<Foo>& pFoo ) {
    if( something ) return new Foo(); // wrapped in a shared pointer
    return pFoo;
}

shared_ptr<Foo> pFoo = new Foo();
SendToTheWorld( preProc( pFoo ) );
xtofl
or step away from the const argument of `preProcess`.
xtofl
Yes, but I need to create Foo on the stack
bocco
+1  A: 

If foo (in the calling function) is not used again after the call, you may be able to do it with a swap function (specializing std::swap for class Foo if necessary):

const Foo& PreprocessFoo(Foo& f)
{
   if (something)
   {
      std::swap(f, Foo());
   }

   // return original or new object
   return f;
}
finnw
Good solution, but `new Foo()` should be simply `Foo()`.
Konrad Rudolph
You're right. Maybe I shouldn't try to post C++ code whilst working on a Java project
finnw
A: 

Your requirements are conflicting: you want the object to be an automatic variable, yet you want to have your say on it's lifetime. It is impossible to return an automatic variable by reference. You could return it by value, but that's too expensive. My idea: if the cost of copying is so high, probably the cost of allocating on the free store is small compared to it.

You could use an output argument, and most of the time not copy the original:

Foo& preProcess( Foo& f ) {
    if( something ) f=Foo(); // hopefully something is special!
    return f;
}

Foo localFoo;
SendToTheWorld( preProcess( localFoo ) );
xtofl
This seems clever. However, it overwrites the original `Foo` object.
sbi
You looked at the code. Look at the text and find the contradictions.
xtofl
A: 

This is not an easy one.

I see three ways to solve this, all of which come down to making copying cheaper. All require control over Foo, one requires control over PreprocessFoo, too:

  1. Use a compiler implementing rvalue references (part of C++1x) and equip Foo with the necessary stuff to have them kick in and make copying cheap.
  2. Read Alexandrescu's mojo article and change Foo to implement that.
  3. Use the Programmer's Universal Cure: add another layer of indirection. Wrap Foo within some smart pointer-like object that makes copying cheap. This, however, will require dynamic allocation.

If you can't change Foo, then I don't see a way.

sbi
A: 

Here's one more idea: If the condition (something) is fix at compile-time, you can make it a template parameter to PreprocessFoo(). Then you can use TMP to branch to two different functions with different return types.

Here's a sketch of that:

template< bool Cond >
struct to_the_world_sender;

template<>
struct to_the_world_sender<true> {
  typedef Foo return_type;
  static return_type preprocess(const Foo& foo) {return Foo();}
};

template<>
struct to_the_world_sender<false> {
  typedef const Foo& return_type;
  static return_type preprocess((const Foo& foo) {return foo;}
};

template< typename Cond >
inline 
typename to_the_world_sender<Cond>::return_type PreprocessFoo(const Foo& foo)
{
  return to_the_world_sender<Cond>::preprocess((foo);
}
sbi