1

Let's say I have an Image class and I want to provide some operations on the image, like scaling, rotating etc. I want to provide 2 types of functions for each operation. One that modifies the object and other that does not. In Ruby, there are functions that end in ! and indicate that this one is going to modify the argument.

Since this is not allowed in C++/Java, what would be the best naming convention. For e.g. how would you name the Mutating and non-mutating versions of img.scale()?

Thanks

user855
  • 19,048
  • 38
  • 98
  • 162

8 Answers8

10

One option would be to use Scale for the mutating version and ScaleCopy for the non-mutating version, since it returns a copy of the original with the operation performed on the copy.

Another option would be to make the non-mutating version a non-member function. For example,

Image Scale(Image im, double scale_factor) {
    im.Scale(scale_factor);
    return im;
}

I'd lean towards the non-member approach since it reduces the number of member functions in the class. To quote Herb Sutter's Monoliths Unstrung, "where possible, prefer writing functions as nonmember nonfriends."

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • So the deal here is that im is copied first, then scaled and then returned as is by RVO. So, overall you have only 1 copy happening. right? – user855 Sep 02 '10 at 23:17
  • @ajay or `Image& Scale(Image &im, double scale_factor) {` – Anycorn Sep 02 '10 at 23:55
  • @aaa: Returning local variables by reference is a very bad idea. – fredoverflow Sep 03 '10 at 00:03
  • @FredOverflow signature accepts parameter by reference and returns it, a la http://www.cplusplus.com/reference/algorithm/min/ – Anycorn Sep 03 '10 at 00:22
  • 1
    @aaa: You can only return things by reference that outlive the function call. If you want to accept an image and return a modified *copy* of it, you must somehow make a local copy which vanishes when the function returns, so you *cannot* return the copy by reference. `min` does a completely different thing: it streams one of its parameters back to the client. (And it does this by *const* reference, not by reference.) However, `min` does *not* return a local variable by reference. That would be a very bad idea. – fredoverflow Sep 03 '10 at 01:12
  • The OP asks for naming convention. However, it is apparent from the discussion that the *implementation* is going to be language-specific. – rwong Sep 03 '10 at 01:23
  • 1
    @FredOverflow my bad, I forgot copy requirement – Anycorn Sep 03 '10 at 01:40
3

I'd probably do

img.scale()

vs.

img.scaledCopy()
jpalecek
  • 47,058
  • 7
  • 102
  • 144
3

For simple verbs, I think my choice would be "Verb" for the mutator, and "asVerbed" for the non-mutator. For changed properties, I would use "setProperty" for the mutator and "withProperty" for the non-mutator.

In the case of scaling, I would use "Scale" as the mutator and "asScaled" for the non-mutator.

supercat
  • 77,689
  • 9
  • 166
  • 211
2

would you name the non-mutating versions of img.scale()?

Maybe getScaled() or createScaled().

ChrisW
  • 54,973
  • 13
  • 116
  • 224
  • I don't wish to seem pedantic, but there's some possible ambiguity in there: does 'get' return me a new one that is scaled or /this one/, scaled? ..and does 'createScaled' overwrite /this one/ with a scaled image? – JBRWilkinson Sep 03 '10 at 08:24
  • @JBRWilkinson If you have doubts about what the methods do, couldn't you look at the signature: I think that the immutable method needs to return the new object by value. – ChrisW Sep 03 '10 at 08:27
1

Are you sure you need the mutating variant?

If you want to mutate the object, you can always just use the non-mutating one like this:

x = x.Scale();

And I suspect it'll even be just as efficient, due to the C++ compiler's aggressive inlining.

If you do want to implement both, you could name them Scale (nonmutating) and ScaleThis (mutating).

jalf
  • 243,077
  • 51
  • 345
  • 550
  • One reason to have both mutating and non-mutating versions is that an object may define the effect of two threads trying to perform mutations simultaneously (e.g. a thread-safe queue could specify that if two threads try to write simultaneously, the result will be as if one wrote and then the other). Having two threads say myQueue = MyQueue.withAddedObject(objectToAdd) could result in one object get added and the other getting lost. – supercat Sep 03 '10 at 00:56
  • Mutation is bad in two situations: (1) in languages where object mutation is strongly discouraged, for example Java; (2) when multi-threading support is needed. If an object is accessible by two threads, then mutating the object from one thread (which may, for example, change the image's dimensions) is going to cause unexpected errors in the second thread, because the second thread may not expect the dimensions of an image to change. – rwong Sep 03 '10 at 01:35
0

My advice would be to wrap Scale in its own little class, and name the "getter" operator int (or operator double, or whatever you're using for it) and the "setter" operator=.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

how about python convention:

Image Image::scaled(double) const;

Or:

Image im(other, scale);
Image im = Image(other).scale(1);  // should be okay I think
Anycorn
  • 50,217
  • 42
  • 167
  • 261
0

How about using a copy constructor to return an Image object based on an existing Image object but with a new scale:

Image(const Image& src, double scale);

If you want to scale and mutate the same image then you could declare a non-const method:

void setScale(double scale);

If you want to obtain the scale in a non-mutating way:

double getScale() const;
Christopher Hunt
  • 2,071
  • 1
  • 16
  • 20