Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle basic_string in datastream serializing/deserializing #118

Merged
merged 4 commits into from
Mar 24, 2023

Conversation

mikelik
Copy link
Contributor

@mikelik mikelik commented Mar 23, 2023

Change Description

Resolves #103
Fixes crash in cdt-cpp when using a std::basic_string<> in action wrapper

API Changes

  • API Changes

Documentation Additions

  • Documentation Additions

template<typename Stream, typename T>
datastream<Stream>& operator << ( datastream<Stream>& ds, const std::basic_string<T>& s ) {
ds << unsigned_int( s.size() );
for( const auto& i : s ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't datastream.write and datastream.read below be better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked in terms of performance and (if I'm reading assembler code correctly) there should be no difference for operator<< with above for loop vs ds.write - both end with a single call to memcpy (when compiled with -O2 flag)

From readers perspective ds.write/read is a bit more concise so I have changed the code.

basic_string operator<< with for loop:
basic_string_for
basic_string operator<< with ds.write:
basic_string_write

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change it back then. Otherwise the write() needs sizeof I believe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

operator>> and ds.read needs sizeof(T), but sizeof is a compile time thing (there is no runtime cost) so I wouldn't worry about it.
I think it is only matter of what is easier to read by developer.

@mikelik mikelik marked this pull request as ready for review March 23, 2023 13:45
@mikelik mikelik requested review from larryk85 and dimas1185 March 23, 2023 13:47
// std::basic_string
ds.seekp(0);
fill(begin(datastream_buffer), end(datastream_buffer), 0);
static const std::basic_string<uint8_t> inputBasicString {0, 1, 2, 3, 4, 5};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add test with 2+ bytes type. something like wchar_t

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was really, really good idea.
It occurred that the second solution with datastream.read/write was wrong. Probably because casting to char* was narrowing 2 bytes to 1 byte?
I reverted solution to the original one and it looks good.
Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your issue was that you had * sizeof(T) in read but not in write. I can't think of other reason but if wouldn't work, try to use static_cast instead of c-cast. We have std::string that is using read/write. I propose to have consistent code. I see that -o2 supposed to make no difference here but anyway that is performance critical code so I think we should use read/write to make even non-optimized code faster.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My code is copied from vector serializing so I believe it is consistent (because basically basic_string is a container).
What is the purpose of making non-optimized code faster? The difference would be only if someone deliberately builds code with debug flags. If it really is important then we need to create issues for improving both vectors and arrays serialization.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your issue was that you had * sizeof(T) in read but not in write.

Thanks, that was the issue.
I additionally changed char* to void* in read method, so I could avoid unnecessary reinterpret_cast<char*> from wchar_t* in my code (which shouldn't be needed anyway, because memcpy needs void* type).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Short answer - I don't know for sure. My comment was based on the fact that cdt is not most recently installed compiler. this is our own patched compiler and its main purpose is to compile to web assembly. So I think it is easier to make it just optimal than check this behavior in wasm.
Regarding having optimal debug build - that may be helpful to avoid timeouts while we debug. You may recall errors in DUNE due to performance. Imagine you have slow machine and debug builds, it will be even slower so you need additional configuration to allow big timeouts

@mikelik mikelik force-pushed the mikelik/basic_string branch from 060000a to da5547d Compare March 24, 2023 14:46
@larryk85 larryk85 merged commit 1de734c into main Mar 24, 2023
@larryk85 larryk85 deleted the mikelik/basic_string branch March 24, 2023 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

crash in cdt-cpp when using a std::basic_string<> in action wrapper
4 participants