-
Notifications
You must be signed in to change notification settings - Fork 47
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
MSVC C++20 Support #126
MSVC C++20 Support #126
Conversation
target_compile_options (adam_smoke_test PRIVATE "/Zc:__cplusplus") | ||
target_compile_options (adam_smoke_test PRIVATE "/W4") | ||
target_compile_options (adam_smoke_test PRIVATE "/WX") | ||
endif() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dabrahams @camio - There must be a better way to manage compiler settings.
I want the library to require the first 3 options, and the cilents (test cases) to add the second 2 options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how I think about it: For portable libraries, the consumer of the library determines the compiler+flags combination for building the library. When the library writer dictates flags, the library gets tied to a snapshot of today's compilers and options making it unportable. The library writer can, however, require certain compiler capabilities.
The requirement underlying the desire for -DNOMINMAX
is a WinDef.h
that doesn't define min/max macros. I'd enforce this in CMake like this
if(WIN32)
check_cxx_symbol_exists(min WinDef.h ASL_WINDEF_DEFINES_MIN_MAX)
if(ASL_WINDEF_DEFINES_MIN_MAX)
message(FATAL_ERROR "WinDef.h defining `min` and `max` is unsupported by this library. If using MSVC, adding -DNOMINMAX to your compiler flags will address this issue.")
endif()
endif()
The same goes for the other two options you want to require. The idea is to check for the functionality you require using something like check_cxx_source_compiles
and providing helpful messages if they fail.
The last two options that you want for a single file are only useful for developers of this library, but problematic for users and integrators (especially when those flags prevent this library from working with a new compiler version).
If applying these flags to the entire repository isn't feasible, I'd suggest adding a CMake option (defaulting to FALSE) for enabling these flags. Then your code would look something like this:
if (ASL_FORCE_WARNINGS AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
target_compile_options (adam_smoke_test PRIVATE "/W4")
target_compile_options (adam_smoke_test PRIVATE "/WX")
endif()
The next question to ask is how are users going to know which flags they need to pass to the various compilers they use. This is what presets are for. With the right preset a user should be able to use cmake --workflow --preset msvc-release
to get everything built and tested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If by
I want the library to require the first 3 options and the cilents (test cases) to add the second 2 options.
You actually mean you want the library to be built with the first 3 options and its clients to have the 2nd 2 options imposed on them automatically due to the fact that they link the library, then you want
target_compile_options(the_library PRIVATE "/Zc:__cplusplus" PUBLIC "/W4" "/WX")
The last two become usage requirements as well as requirements for building the library. To make them pure usage requirements, you'd s/PUBLIC
/INTERFACE
/.
@camio's statement that:
When the library writer dictates flags, the library gets tied to a snapshot of today's compilers and options making it unportable
Could be interpreted two ways: 1, you should try to use higher-level constructs than the flags for specific compilers whenever possible, or 2, you should never employ usage requirements. I agree with the first but would disagree with the second. I'm not sure what these flags do or whether higher-level constructs exist for them, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated it to use PRIVATE/PUBLIC (more) appropriately.
adobe/manip.hpp
Outdated
However, if it is not included here, the build fails for C++17 under MSVC only. I have not been | ||
able to determine why this is the case. | ||
*/ | ||
#include <adobe/serializable.hpp> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dabrahams - I was able to significantly reduce the problem to the pointer where I determined that adobe/serializable.hpp
had to be included before manip.hpp
, but there does not appear to be any dependency on serializable.hpp
- circular or not. In fact, in my reduced case, not include manip.hpp
at all also worked. If it was more than one configuration of one compiler, I'd reduce it more.
Wihtout this line the following fails to compiler:
#include <adobe/array.hpp>
#include <adobe/manip.hpp>
#include <adobe/serializable.hpp>
/**************************************************************************************************/
namespace adobe {
/**************************************************************************************************/
void test() {
serializable_t value{array_t{}};
}
/**************************************************************************************************/
} // namespace adobe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without what line?
I don't think giving up on the reduction is appropriate. This configuration of this compiler applies all Adobe products and many others. IMO you should get to the point where you can submit a bug report. But that's just me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found it - a namespace lookup issue. Fixed property in array.hpp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some suggestions inline.
@@ -7,8 +7,7 @@ project(adobe_source_libraries CXX) | |||
|
|||
set(CMAKE_CXX_EXTENSIONS OFF) | |||
if(NOT DEFINED CMAKE_CXX_STANDARD) | |||
set(CMAKE_CXX_STANDARD 17) | |||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++17") | |||
set(CMAKE_CXX_STANDARD 20) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to assume that dropping the Xcode setting is intentional and correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It isn't needed.
"value": "x64", | ||
"strategy": "external" | ||
}, | ||
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this toolchain requires vcpkg? Is that dependency documented in the README? If not you better do that.
Is how to use the presets documented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The system is currently using vcpkg to pull in boost. It's undocumented, and I'd like to get rid of it in favor of fetch content, but not in this PR.
adobe/exception.hpp
Outdated
|
||
/**************************************************************************************************/ | ||
|
||
/// invoke `std::terminate` with the given message. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
? You can't invoke std::terminate
with a message, so you need to fix this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the comment to be more clear.
@@ -227,7 +227,7 @@ class byte_toroid_t : boost::noncopyable { | |||
|
|||
void purify(toroid_node_t* p) { | |||
toroid_header_t* c(column_of(p)); | |||
int x(p->color_m); | |||
auto x(p->color_m); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Surely not needed for C++20 support? Why is it even an improvement?
Aside: we really don't use copy-initialization anymore? This syntax makes it look like a function declaration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This silences a warning because x
is char type and later uses will narrow back to a char type.
stuff_bit_offset += num_bits; | ||
n_stuffed_bits += num_bits; | ||
ADOBE_ASSERT(num_bits < 8); | ||
stuff_bit_offset += static_cast<std::uint16_t>(num_bits); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a comment explaining how you know it won't overflow is warranted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not an expert in this code - it took me a bit to figure out that num_bits
< 8 here. I'm not certain, but it looks like stuff_bit_offset
is always used in modulo value_bitsize_k and can warp (unsigned). n_stuffed_bits
is 64 bits, so it would take a lot to overflow incrementing by 8
. And I think this is going to be bounded by sizeof message_block_type * 8 - so you'll run out of VM space first (if not addressable space).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, if you can't prove it, there's always boost::numeric_cast
to maintain safety.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That might be very bad if it is expected to wrap.
source/adam.cpp
Outdated
@@ -1114,36 +1114,34 @@ void sheet_t::implementation_t::flow(cell_bits_t& priority_accessed) { | |||
index_t::iterator iter = output_index_m.find(term->name_set_m[n]); | |||
assert(iter != output_index_m.end()); | |||
|
|||
cell_t& cell = *iter; | |||
cell_t& e = *iter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OMG this is a hecka long function. Nice getting rid of the type information in the name, but can we do better than e
? What role is this variable playing on the inside of the (raw!) loop? That is, if you were to break the body of the loop into a function call taking cell_t&
, what would you call the parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a fair amount of refactoring and cleanup of that function and used better names. Still long - opened Issue#127 to revisit.
source/eve.cpp
Outdated
for (guide_set_t::iterator first(boost::next(forward_guide_set.begin())), | ||
last(forward_guide_set.end()); | ||
first != last; ++first) { | ||
for (guide_set_t::iterator f(boost::next(forward_guide_set.begin())), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this an improvement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was a quick way to squelch a warning - but I resolved the revisit comment and moved the code to use std::generate()
source/eve_evaluate.cpp
Outdated
for (array_t::const_iterator iter(spacing_array.begin()); | ||
iter != spacing_array.end(); ++iter) { | ||
*dest_iter = iter->cast<int>(); | ||
for (array_t::const_iterator spacing_iter(spacing_array.begin()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a single character name would actually be clearer in this case. Also, though, no raw loops would alleviate the need to name these trivial things everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced this one with transform()
@@ -151,7 +151,7 @@ void flat_format::handle_atom(stream_type& os, bool is_push) { | |||
} else if (value.type_info() == typeid(dictionary_t)) { | |||
os << value.cast<dictionary_t>(); | |||
} else if (value.type_info() == typeid(array_t)) { | |||
os << value.cast<array_t>(); | |||
os << top.value().cast<::adobe::array_t>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably you want to revert this change, but if not, comment to explain why this line needs to be so different from the rest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Reducing loops in sheet flow Added append algorithm
adobe/algorithm/append.hpp
Outdated
/// Insert the range `r` at the end of the sequence container `x`. | ||
/// Returns an iterator pointing to the first element inserted, or `x.end()` if `r` is empty. | ||
template <class T, // T models SequenceContainer | ||
class R> // R models Range | ||
inline auto append(T& x, const R& r) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Insert the range `r` at the end of the sequence container `x`. | |
/// Returns an iterator pointing to the first element inserted, or `x.end()` if `r` is empty. | |
template <class T, // T models SequenceContainer | |
class R> // R models Range | |
inline auto append(T& x, const R& r) { | |
```suggestion | |
/// Inserts the elements of *range* `r` (a `Range`) at the end of *forward container* `x`, | |
/// returning the position corresponding to the incoming `end` of `x`. | |
template <class T, class R> | |
inline auto append(T& x, const R& r) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accepted.
source/adam.cpp
Outdated
/// Returns the output cell with the given name. | ||
/// \pre the output cell must exist. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Returns the output cell with the given name. | |
/// \pre the output cell must exist. | |
/// Returns the output cell with the given name. | |
/// | |
/// \pre A cell with that name exists. |
Blank line after summary.
A precondition is a predicate statement. Write in English what you'd put in the assert()
parens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accepted.
source/adam.cpp
Outdated
return *p; | ||
} | ||
|
||
/// Returns `true` if any output cells in the relation are resolved, false otherwise. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some people think iff is too obscure, but this is an option
/// Returns `true` if any output cells in the relation are resolved, false otherwise. | |
/// Returns `true` iff any output cells in the relation are resolved. |
Some people like:
/// Returns `true` if any output cells in the relation are resolved, false otherwise. | |
/// Returns whether any output cells in the relation are resolved. |
I find that really awkward personally but I can see the argument.
If you are in the context of a type representing a relation, you can leave out “in the relation.”
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My preference was iff (I had that initially, but I know people who would object, so I changed it). I'll go with "whether" -
Co-authored-by: Dave Abrahams <dabrahams@adobe.com>
Added Support for MSVC C++20.
Fixed warnings at \W4
Improved cmake presets support for VSCode.
New libraries: