20

Reading the Eigen library documentation, I noticed that some objects cannot be passed by value. Are there any developments in C++11 or planned developments that will make it safe to pass such objects by value?

Also, why is there no problem with returning such objects by value?

Neil G
  • 32,138
  • 39
  • 156
  • 257
  • 6
    Have you looked at [`std::aligned_storage<>`](http://en.cppreference.com/w/cpp/types/aligned_storage)? – ildjarn Apr 04 '12 at 21:06
  • 2
    I've never heard of an object that couldn't be passed by value, and would consider such an object poorly designed. I wonder why Eigen did that? – Mooing Duck Apr 04 '12 at 21:17
  • @ildjarn: Now I have :) Could you explain how it affects passing a type by value? – Neil G Apr 04 '12 at 21:17
  • 12
    I take issue with Eigen's comment: "Passing objects by value is almost always a very bad idea in C++." Passing objects by value is perfectly reasonable for when you want to, you know, pass it by value. – Robᵩ Apr 04 '12 at 21:17
  • @Mooing: There are many objects that can't be passed by value, because copying doesn't make sense. How do you write a copy constructor for an exclusive lock? – Ben Voigt Apr 04 '12 at 21:18
  • @Robᵩ: Considering that Eigen is a matrix library, I suspect that the objects in question are rather heavy, and should be passed by reference whenever possible. There's nothing specifically wrong with disabling copy to eliminate unintentional implicit expensive copies. – Ben Voigt Apr 04 '12 at 21:20
  • @NeilG : You can place any trivially copyable type inside of the `aligned_storage<>` object (via aliasing) and pass the `aligned_storage<>` object by value. The other side of the call would simply need to know how to alias the data. A sensible API would implement those trivially copyable types _in terms of_ `aligned_storage<>` so the consumer would never need to know about it or do any aliasing manually. – ildjarn Apr 04 '12 at 21:20
  • @BenVoigt: Oh right, I hadn't considered noncopyable objects. I also looked at Eigen's documentation, and it makes sense. I can't think of a way to meet their requirements nicely in C++03, not sure it's possible. – Mooing Duck Apr 04 '12 at 21:20
  • 1
    @ildjarn: I suspect that "cannot be passed by value" means "non-copyable". So a solution that requires trivial copy is not likely to be helpful. – Ben Voigt Apr 04 '12 at 21:21
  • @BenVoigt - Fine. Then let them say, "Passing Eigen objects by value is a bad idea." But their blanket statement is just silly. – Robᵩ Apr 04 '12 at 21:21
  • 2
    @Ben : I think they're non-copyable in this case to avoid alignment issues. I.e., it's an artificial limitation. – ildjarn Apr 04 '12 at 21:22
  • @Robᵩ: I agree that version of the statement is better. But I haven't seen it in context. Now I have, and you didn't even quote the entire sentence. Shame on you. – Ben Voigt Apr 04 '12 at 21:22
  • @MooingDuck: What do types have to do with anything? `alignas` takes a number. `alignas(16)` means align on 16-byte boundaries. If the compiler cannot do so, the program is ill-formed (diagnostic required). `alignas(T)` is defined to be `alignas(alignof(T))`. – GManNickG Apr 04 '12 at 21:32
  • @ildjarn: I checked the spec, I hadn't realized `alignas` took a number, and didn't realize there was both a `alignas` and `alignof`. My bad. Comments deleted. – Mooing Duck Apr 04 '12 at 21:38
  • @Mooing Duck: Eigen uses SSE2 heavily for packed vector and matrix operations which use 16-bit aligned memory for performance. For example, the compiler intrinsic `__m128` is 16-bit aligned and cannot be passed by value. – Inverse Apr 10 '12 at 01:42
  • ... this has nothing to do with non-copyable objects, just alignment for 128 byte types. – Inverse Apr 10 '12 at 01:52
  • @Inverse: does `alignas` allow you to pass the compiler intrinsic `__m128` by value? – Neil G Apr 10 '12 at 02:27

2 Answers2

19

It is entirely possible that Eigen is just a terribly written library (or just poorly-thought out); just because something is online doesn't make it true. For example:

Passing objects by value is almost always a very bad idea in C++, as this means useless copies, and one should pass them by reference instead.

This is not good advice in general, depending on the object. It is sometimes necessary pre-C++11 (because you might want an object to be uncopyable), but in C++11, it is never necessary. You might still do it, but it is never necessary to always pass a value by reference. You can just move it by value if it contains allocated memory or something. Obviously, if it's a "look-but-don't-touch" sort of thing, const& is fine.

Simple struct objects, presumably like Eigen's Vector2d are probably cheap enough to copy (especially in x86-64, where pointers are 64-bits) that the copy won't mean much in terms of performance. At the same time, it is overhead (theoretically), so if you're in performance critical code, it may help.

Then again, it may not.

The particular crash issue that Eigen seems to be talking about has to do with alignment of objects. However, most C++03 compiler-specific alignment support guarantees that alignment in all cases. So there's no reason that should "make your program crash!". I've never seen an SSE/AltaVec/etc-based library that used compiler-specific alignment declarations that caused crashes with value parameters. And I've used quite a few.

So if they're having some kind of crash problem with this, then I would consider Eigen to be of... dubious merit. Not without further investigation.

Also, if an object is unsafe to pass by value, as the Eigen docs suggest, then the proper way to handle this would be to make the object non-copy-constructable. Copy assignment would be fine, since it requires an already existing object. However, Eigen doesn't do this, which again suggests that the developers missed some of the finer points of API design.

However, for the record, C++11 has the alignas keyword, which is a standard way to declare that an object shall be of a certain alignment.

Also, why is there no problem with returning such objects by value?

Who says that there isn't (noting the copying problem, not the alignment problem)? The difference is that you can't return a temporary value by reference. So they're not doing it because it's not possible.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Thanks for your answer (+1) — In answer to your question "who says there isn't?" I was merely quoting the bottom of the linked documentation. They say there isn't. – Neil G Apr 04 '12 at 21:25
  • 2
    @NeilG: I'd guess that there's no problem with returning objects by value, since the temporary (which may be unaligned or not even exist) can't be used anyway. The values are properly aligned both before and after the return, no matter where (or if) the temporary is. – Mooing Duck Apr 04 '12 at 21:27
  • @MooingDuck: Thanks, that answers the return-value part of my question. – Neil G Apr 04 '12 at 21:27
  • 3
    @MooingDuck: VS doesn't support `alignas` yet, but if GCC supports 16-byte alignment at all (with compiler-specific alignment directives), then it should support 16-byte alignment through `alignas`. And if the compiler *can't* do 16-byte alignment, then... it's not possible to even create these things by *value* safely. – Nicol Bolas Apr 04 '12 at 21:32
  • 4
    Worth noting that while VC++ doesn't support `alignas` yet, it does have [`__declspec(align(#))`](http://msdn.microsoft.com/en-us/library/83ythb65.aspx), so the semantics are possible, just not yet in a portable syntax. – ildjarn Apr 04 '12 at 21:47
  • 1
    Several Eigen objects use sse intrinsics member variables like `__m128` which can't be passed by value :\ – Inverse Apr 10 '12 at 01:47
  • @Inverse: Then those objects should be non-copyable to make it much more difficult to pass them by value. Also, what is it about these intrinsics that makes them impossible to pass by value? – Nicol Bolas Apr 10 '12 at 02:31
  • Considering the problem doesn't exist for non-passed objects, making it non-copyable would make the library un-usable. I think even the copy-constructors are necessary for usability sake with all the templated types being passed around that need automatic conversion. I don't think they were in-cautious when designing their API, I think they looked at the trade offs and then made the decision anyway. The do offer a define flag to disable alignment but AFAIK it breaks ABI compatibility between code compile with and without. – Catskul Apr 09 '13 at 19:13
11

They could do this in C++11:

class alignas(16) Matrix4f
{
    // ...
};

Now the class will always be aligned on a 16-byte boundary.

Also, maybe I'm being silly but this shouldn't be an issue anyway. Given a class like this:

class Matrix4f
{
public:
    // ...
private:
    // their data type (aligned however they decided in that library):
    aligned_data_type data;

    // or in C++11
    alignas(16) float data[16];
};

Compilers are now obligated to allocate a Matrix4f on a 16-byte boundary anyway, because that would break it; the class-level alignas should be redundant. But I've been known to be wrong in the past, somehow.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Of course, then you also need 16-byte aligned stacks... I think some popular compilers don't respect alignment requirements for local variables and arguments unless you also configure stack alignment. – Ben Voigt Apr 04 '12 at 21:27
  • 4
    @BenVoigt: Ah. :/ Well, at least with `alignas` they compiler is required to spit out an error saying, "sorry, I can't do that." (Ergo, enforcing passing by reference when truly required.) – GManNickG Apr 04 '12 at 21:33
  • @BenVoigt: If that's the case, then you could never put compiler intrinsics (without special compiler configurations) for SSE or other such things on the stack, since they need 16-byte alignment. – Nicol Bolas Apr 04 '12 at 21:33
  • So, could Eigen be using alignas to solve the problem of objects that can't be passed by value? – Neil G Apr 10 '12 at 02:28
  • @NeilG: As pointed out elsewhere, if there really is a problem passing Eigen objects by value then that's a nasty bug in Eigen and not really a C++ problem per se. But yes, they could use that. (They probably aren't, as compilers in general don't yet suppose `alignas`.) – GManNickG Apr 10 '12 at 03:19
  • 4
    @GManNickG: Sometimes, when performance is paramount (as it is in the kind of code Eigen is intended for), and the language is limited (as it is in C++ prior to C++11), the only acceptable option is to do something dangerous and put a big warning on it. I would not consider this to be a bug in Eigen: it's a design decision, and in my opinion as a user a valid one. The addition of alignment facilities in C++11 is great, but it's an indication of the importance of their prior lack: if libraries could get around these problems then C++11 wouldn't need alignas. – John Bartholomew Aug 08 '12 at 19:47