[serialization] Passing literals to oarchive
Currently we cannot pass literals directly to an oarchive like this: oarchive << 42; since compilation fails in interface_oarchive.hpp, as the operator<< only takes parameters by reference. We can remedy this by changing operator<< to take the parameter by const-ref instead, as show below. Is there any reason not to do this? --- a/include/boost/archive/detail/interface_oarchive.hpp +++ b/include/boost/archive/detail/interface_oarchive.hpp @@ -59,7 +59,7 @@ public: } template<class T> - Archive & operator<<(T & t){ + Archive & operator<<(const T & t){ this->This()->save_override(t, 0); return * this->This(); }
Bjorn Reese wrote
Currently we cannot pass literals directly to an oarchive like this:
oarchive << 42;
since compilation fails in interface_oarchive.hpp, as the operator<< only takes parameters by reference.
Actually, all parameters are already const reference. I realize that it doesn't look that way. That's because interface_oarchive::operator<<(T & t) is never called from the application but rather from lower level. By the time interface_oarchive::operator<<(T & t) gets called, the T already has the "const" as part of the type. (Actually it has a & also so the interface_oarchive::operator<<(T) would actually be the same. I realize this is confusing, but that's the way C++ works.
We can remedy this by changing operator<< to take the parameter by const-ref instead, as show below. Is there any reason not to do this?
so there's nothing to remedy FWIW - there is a case to be made for replacing at the top level text_oarchive::operator<<(const T & t) with text_oarchive::operator<<(T && t) but that's another discussion. Robert Ramey -- View this message in context: http://boost.2283326.n4.nabble.com/serialization-Passing-literals-to-oarchiv... Sent from the Boost - Dev mailing list archive at Nabble.com.
On 03/27/2015 05:40 AM, Robert Ramey wrote:
is never called from the application but rather from lower level. By the time interface_oarchive::operator<<(T & t) gets called, the T already has the "const" as part of the type. (Actually it has a & also so the
That is not what my compiler says: hasher.cpp:126:13: error: invalid operands to binary expression ('hasher::oarchivehasher::fnv1a' and 'int') archive << 42 << x; ~~~~~~~ ^ ~~ /home/breese/src/boost/boost/boost/archive/detail/interface_oarchive.hpp:62:15: note: candidate function [with T = int] not viable: expects an l-value for 1st argument Archive & operator<<(T & t){ ^ 1 error generated.
interface_oarchive::operator<<(T) would actually be the same. I realize this is confusing, but that's the way C++ works.
We can remedy this by changing operator<< to take the parameter by const-ref instead, as show below. Is there any reason not to do this?
so there's nothing to remedy
FWIW - there is a case to be made for replacing at the top level text_oarchive::operator<<(const T & t) with text_oarchive::operator<<(T && t) but that's another discussion.
I am not using text_oarchive, but rather my own archive which inherits from detail::common_oarchive. I have attach a full working example (notice the comments in main().)
Bjorn Reese wrote
On 03/27/2015 05:40 AM, Robert Ramey wrote:
is never called from the application but rather from lower level. By the time interface_oarchive::operator<<(T & t) gets called, the T already has the "const" as part of the type. (Actually it has a & also so the
That is not what my compiler says:
hasher.cpp:126:13: error: invalid operands to binary expression ('hasher::oarchive hasher::fnv1a ' and 'int') archive << 42 << x; ~~~~~~~ ^ ~~ /home/breese/src/boost/boost/boost/archive/detail/interface_oarchive.hpp:62:15: note: candidate function [with T = int] not viable: expects an l-value for 1st argument Archive & operator<<(T & t){ ^ 1 error generated.
I realized this immediately just after I hit the send button. I sent another post but it seems it didn't come through. Sorry about that. interface::operator<<(T & t) eventually calls a lower level which checks for "constness" of T. In this case the problem is different. A parameter value placed on the stack cannot be passed by "const T & t". This syntax assumes and enforces that t is an lvalue refeference. To make this work, one could pass a "universal reference "T && t" which will accept either an lvalue or an rvalue (your literal placed on the stack). But this would create a couple of other issues: a) code wouldn't be C++03 compatible any more. b) The serialization library tracks the address of most serialized objects in order to detect duplicates and avoid creation of multiple instances when the archive is loaded. So serializing something currently on the the stack would generally not be be a great idea. In your particular case, it wouldn't matter since primitive types are not tracked. Now one could add yet one more layer of TMP code to distinguish between tracked and untracked types and handle them differently. But that would introduce another layer of complexity and hide even more what is going on. I would be reluctant to do this. So your current alternative in these cases would be to use: int x = 42; oa << x; which will work without problem. It will be no different nor less efficient (when compiled for release) than oa << 42; I'm presuming that this is not a common case. Robert Ramey -- View this message in context: http://boost.2283326.n4.nabble.com/serialization-Passing-literals-to-oarchiv... Sent from the Boost - Dev mailing list archive at Nabble.com.
On 03/27/2015 05:49 PM, Robert Ramey wrote:
In this case the problem is different. A parameter value placed on the stack cannot be passed by "const T & t". This syntax assumes and enforces
Why not?
Now one could add yet one more layer of TMP code to distinguish between tracked and untracked types and handle them differently. But that would introduce another layer of complexity and hide even more what is going on. I would be reluctant to do this.
Making a special case for primitive types is not too bad. See the attached patch.
I'm presuming that this is not a common case.
I encounter it all the time in unit tests, and occasionally in other code.
Bjorn Reese wrote
On 03/27/2015 05:49 PM, Robert Ramey wrote:
In this case the problem is different. A parameter value placed on the stack cannot be passed by "const T & t". This syntax assumes and enforces
Why not?
It generates a compile time error when you pass an rvalue.
Now one could add yet one more layer of TMP code to distinguish between tracked and untracked types and handle them differently. But that would introduce another layer of complexity and hide even more what is going on. I would be reluctant to do this.
Making a special case for primitive types is not too bad. See the attached patch.
Since I wrote this, I've been considering it. I looked at your patch. I would likely condition it on the type being a tracked or untracked type rather than being on a primitive type.
I'm presuming that this is not a common case.
I encounter it all the time in unit tests, and occasionally in other code.
the reason I would think that it's not a common case is the following: ar << 42; // put the value of 42 into the archive ... ar >> ?; // what do we do with the result we're retrieving? So I'm not seeing where this comes up. -- View this message in context: http://boost.2283326.n4.nabble.com/serialization-Passing-literals-to-oarchiv... Sent from the Boost - Dev mailing list archive at Nabble.com.
On 30/03/2015 04:35, Robert Ramey wrote:
Bjorn Reese wrote
On 03/27/2015 05:49 PM, Robert Ramey wrote:
In this case the problem is different. A parameter value placed on the stack cannot be passed by "const T & t". This syntax assumes and enforces
Why not?
It generates a compile time error when you pass an rvalue.
It shouldn't. Literals are definitely compatible with "const T &". I'm not 100% certain but I believe rvalue references "T&&" should also be compatible, although they will probably collapse to basic references.
the reason I would think that it's not a common case is the following:
ar << 42; // put the value of 42 into the archive ...
ar >> ?; // what do we do with the result we're retrieving?
So I'm not seeing where this comes up.
Obviously >> must have an lvalue. That doesn't constrain << though. The OP was referring to unit tests, where presumably they were wanting to poke raw values into an archive in a particular order and verify that they deserialised the correct way into an actual object. As for why they wanted to do that, I don't know, but I can guess that one possibility is for testing backwards compatibility: given a known sequence of values written by some prior version of the object, verify that it deserialises correctly in the current version of the object. But there's lots of other possibilities too.
On 03/29/2015 05:35 PM, Robert Ramey wrote:
Bjorn Reese wrote
On 03/27/2015 05:49 PM, Robert Ramey wrote:
In this case the problem is different. A parameter value placed on the stack cannot be passed by "const T & t". This syntax assumes and enforces
Why not?
It generates a compile time error when you pass an rvalue.
I am not sure I follow you. Are you saying that the following does not compile? template <typename T> void foo(const T& t) {} int main() { foo(42); int x = 42; foo(x); return 0; }
Since I wrote this, I've been considering it. I looked at your patch. I would likely condition it on the type being a tracked or untracked type rather than being on a primitive type.
That is perfectly fine with me.
the reason I would think that it's not a common case is the following:
The unit-tests for my own output archives checks that the encoding is correct by feeding literal values into the archive. If I could pass literal values to an oarchive, then it would look like this: archive << 1 << 0x0100 << 0x010000 << 0x0100000000LL; followed by a check to verify that the archive has generated to correct output. However, currently I have to specify temporary variables (with odd names) for the literal values, which leads to code like this: int alpha = 1; int bravo = 0x0100; int charlie = 0x010000; long long delta = 0x0100000000LL; archive << alpha << bravo << charlie << delta; I have plenty of the above workarounds in my unit-tests.
ar >> ?; // what do we do with the result we're retrieving?
That is not what I am asking for, but you compare and skip the literal values like iostream does.
Bjorn Reese wrote
I am not sure I follow you. Are you saying that the following does not compile?
template <typename T> void foo(const T& t) {}
int main() { foo(42); int x = 42; foo(x); return 0; }
I meant that following won't compile template <typename T> void foo(const T& t) {} int main() { foo(42); return 0; } I just tried to compile this and now I see I was wrong about this. I knew that template <typename T> void foo( T & t) {} int main() { foo(42); return 0; } doesn't compile. To me this always made sense since it doesn't make much sense to take the address of something which is about to disappear regardless of whether it's "const" or now. My fix was to use void foo(T && t) which made sense to me - but of course not available until now. So make a trac ticket and I'll get around to addressing this. Robert Ramey -- View this message in context: http://boost.2283326.n4.nabble.com/serialization-Passing-literals-to-oarchiv... Sent from the Boost - Dev mailing list archive at Nabble.com.
On 03/30/2015 03:50 PM, Robert Ramey wrote:
So make a trac ticket and I'll get around to addressing this.
https://svn.boost.org/trac/boost/ticket/11152 PS: The trac system was unresponsive, so I ended up submitting this issue twice. #11153 is a duplicate of #11152, but I am unable to delete it. Sorry about the inconvenience.
participants (3)
-
Bjorn Reese
-
Gavin Lambert
-
Robert Ramey