Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I combine an in place transformation, and a copy transformation?

I would like to combine these two functionalities into a single function interface:

T& Transform(T & foo){
   //transform t
   return t;
}

T As_Transformed(T foo){
   //transform t
   return t;
}

Sometimes I want to transform a variable passed into the function.
Other times I want a new variable with the applied transformation.

As a result, I end up creating two functions everytime, and following a made up convention of mine where As_ takes and returns a copy, while not having As_ takes and returns a reference.

How can I write a single function implementation that will handle both of these behaviors?

I have no requirements on what it should look like, but I'd like to have a way where I'm not relying on my As_ convention, and ideally where I only make one function instead of two.


Example:
Here is an example of what this would look like.
Let's take Uppercase() and As_Upercased()

std::string str1 = "hello";
Uppercase(str1); //str is now "HELLO"

std::string str2 = "hello";
auto str3 = As_Uppercased(str2); //str2 is still "hello", 
                                 //but str3 is "HELLO"

I don't know what the combined interface would look like, but perhaps:

std::string str1 = "hello";
Uppercase<REF>(str1); //str is now "HELLO"

std::string str2 = "hello";
auto str3 = Uppercase<COPY>(str2); //str2 is still "hello", 
                                   //but str3 is "HELLO"

Or maybe I can do something with reference wrappers.
The possible implementations of this, are what I'm asking about.

like image 942
Trevor Hickey Avatar asked Dec 11 '22 19:12

Trevor Hickey


1 Answers

You can tell the compiler how to differentiate the two by providing an overload on std::reference_wrapper. Then the code would look like this, for example:

#include <iostream>
#include <functional>

using T = std::string;

T Transform(T t)
{
    t += " copy";
    return t;
}

std::reference_wrapper<T> Transform(std::reference_wrapper<T> t)
{
    t.get() += " ref";
    return t;
}

int main()
{
    T t{"original"};

    std::cout << Transform(Transform(t)) << "\n";
    std::cout << t << "\n";
    std::cout << Transform(Transform(std::ref(t))).get() << "\n";
    std::cout << t;
}

Output:

original copy copy
original
original ref ref
original ref ref

Notice, how the original value is left intact after the first call chain and is modified after the second one.

In your actual code, the first overload would just call the second to transform its passed-by-copy parameter wrapped using std::ref to avoid code duplication.

LIVE

like image 147
Rostislav Avatar answered Dec 13 '22 11:12

Rostislav