Home > Software design >  Define object and pass by reference inside function parameter
Define object and pass by reference inside function parameter

Time:09-26

Lets say we have a function called foo that gets a pointer to some object.

void foo(int *i){
    // Some code
}

We can call this function the following way:

int i;
foo(&i);

Is there a way we can make the above snippet in one line in c ? Something like

foo(&int(4)) // This is invalid.

Two limitations:

  1. Changing foo to receive by-reference is not an option.
  2. We also want to avoid using something like foo(new int(4)) which can lead to memory leaks.

CodePudding user response:

"Doing all in one line" is usually not something desirable, because it does not make code more clean or more readable. If you have to declare an int and take its address, your code better expresses that explicitly.

However, the usual way to encapsulate common tasks is functions:

 void foo_wrapper() {
       int i = 0;
       foo(&i);
 }

Then call foo_wrapper() rather than foo(&i).

PS: The elephant in the room is: foo apparently uses the argument as out-parameter, but you want to ignore it. If this is really how foo is supposed to be used, then it does unnecessary work. Writing the wrapper is hiding a flaw. To fix the actual issue foo would have to be modified. I know you said it is third party library code, but rather than writing workarounds for library code, I would reconsider if (a) it is the right library or (b) perhaps I do misunderstand how it is supposed to be used.

CodePudding user response:

I have no idea why such behaviour would be required and assume it's some kind of 'Brain Teaser' question. If it's an exercise in "macro magic" there may be better solutions.

This is valid:

#include <iostream>
#include <memory>

void f(int *x){
      (*x);
    std::cout << *x << '\n';
}
int main() {
     f(std::make_unique<int>(4).get()); 
    return 0;
}

Expected Output: 5

The temporary object (of type std::unique<int>) will be cleaned up after the function call. No memory leaked but there is of course overhead in dynamic allocation.

CodePudding user response:

& and std::addressof are explicitly specified to disallow this, but if you want to ignore that safety measure, you can define your own wrapper around std::addressof that will allow it:

template<typename T>
constexpr auto myaddressof(T&& t) noexcept {
    return std::addressof(t);
}

//...

// temporary created from argument to `addressof` lives
// until end of full-expression
foo(myaddressof(4));

That's quite easy to get wrong though. For example if you replace T&& with T, then using it may result in undefined behavior. This relies on rules for temporary lifetime that are probably not intuitive. I don't really think this is better style than declaring a variable and wrapping it with the call in a block.

Full demo here based on a comment by @JasonLiam.

(Note that you probably shouldn't use addressof as name for this function. There are some corner cases where argument-dependent lookup will cause problems with that name if called unqualified.)

CodePudding user response:

void foo(int *i) expresses intent that the lifetime of the argument is managed by the caller, outside of foo. Which means allocating some time before foo call and deallocating some time after foo returns. So one liner is out of the question because of your reason #2.

The idiomatic way of handling optional pointer values is to use nullptr or NULL (pre-C 11) argument as an indicator to ignore it: foo(nullptr).

CodePudding user response:

You can use wrapper class (which in a way is similar to the other answer with myaddressof function template):

#include <iostream>

template<typename T>
class my_wrapper_class
{
  public:
    my_wrapper_class(T const &v) : value(v)
    {
    }
    T *operator&()
    {
      return(&value);
    }
    T const *operator&() const
    {
      return(&value);
    }
  private:
    T value;
};

void f(int *v)
{
  std::cout << "v: " << *v << std::endl;
  (*v)  ;
}

int main()
{
  f(&my_wrapper_class<int>(4));
  return(0);
}

Please note that EVERY book about C I've read strongly advises against overloading unary operator & which I've just did. The other option (not shown) is to define conversion operator to appropriate pointer and omission of explicit & at call site which in my opinion is equally confusing.

Putting it here so it is available for those in need with a strong disclaimer - DON'T DO THAT. Consider what others wrote on this topic before heading this way.

  •  Tags:  
  • c
  • Related