-
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
Changes from 1 commit
7ab7a4f
900f620
5dd244f
b670fc3
58c457b
d68084d
2e78a81
622afeb
2e8b826
0f69ed8
c93d480
32d3c7b
8888b16
57e3f22
01ff792
7a61a75
1cb0b49
7d3c408
a46be16
ef7ca40
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
Copyright 2024 Adobe | ||
Distributed under the Boost Software License, Version 1.0. | ||
(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
*/ | ||
/**************************************************************************************************/ | ||
|
||
#ifndef ADOBE_ALGORITHM_APPEND_HPP | ||
#define ADOBE_ALGORITHM_APPEND_HPP | ||
|
||
/**************************************************************************************************/ | ||
|
||
namespace adobe { | ||
|
||
/**************************************************************************************************/ | ||
|
||
/// 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) { | ||
return x.insert(std::end(x), std::begin(r), std::end(r)); | ||
} | ||
|
||
/**************************************************************************************************/ | ||
|
||
} // namespace adobe | ||
|
||
/**************************************************************************************************/ | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -14,6 +14,7 @@ | |||||||||||
#include <utility> | ||||||||||||
#include <vector> | ||||||||||||
|
||||||||||||
#include <adobe/algorithm/append.hpp> | ||||||||||||
#include <adobe/algorithm/find.hpp> | ||||||||||||
#include <adobe/algorithm/for_each.hpp> | ||||||||||||
#include <adobe/algorithm/sort.hpp> | ||||||||||||
|
@@ -345,6 +346,27 @@ class sheet_t::implementation_t : boost::noncopyable { | |||||||||||
priority_t name_to_priority(name_t name) const; | ||||||||||||
void flow(cell_bits_t& priority_accessed); | ||||||||||||
|
||||||||||||
/// 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 commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Blank line after summary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Accepted. |
||||||||||||
cell_t& output_cell(const name_t& name) { | ||||||||||||
auto p = output_index_m.find(name); | ||||||||||||
assert(p != output_index_m.end() && "output cell not found"); | ||||||||||||
return *p; | ||||||||||||
} | ||||||||||||
const cell_t& output_cell(const name_t& name) const { | ||||||||||||
auto p = output_index_m.find(name); | ||||||||||||
assert(p != output_index_m.end() && "output cell not found"); | ||||||||||||
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 commentThe reason will be displayed to describe this comment to others. Learn more. Some people think iff is too obscure, but this is an option
Suggested change
Some people like:
Suggested change
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 commentThe 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" - |
||||||||||||
bool resolved(const relation_t& relation) const { | ||||||||||||
return find_if(relation.name_set_m, [&](const auto& name) { | ||||||||||||
return output_cell(name).resolved_m; | ||||||||||||
}) != relation.name_set_m.end(); | ||||||||||||
} | ||||||||||||
|
||||||||||||
|
||||||||||||
/* | ||||||||||||
NOTE (sparent) : cell_t contains boost::signals2::signal<> which is not copyable. The cells | ||||||||||||
support | ||||||||||||
|
@@ -717,10 +739,10 @@ void sheet_t::implementation_t::add_input(name_t name, const line_position_t& po | |||||||||||
void sheet_t::implementation_t::add_output(name_t name, const line_position_t& position, | ||||||||||||
const array_t& expression) { | ||||||||||||
// REVISIT (sparent) : Non-transactional on failure. | ||||||||||||
cell_set_m.push_back(cell_t(access_output, name, | ||||||||||||
std::bind(&implementation_t::calculate_expression, | ||||||||||||
std::ref(*this), position, expression), | ||||||||||||
cell_set_m.size(), nullptr)); | ||||||||||||
cell_set_m.push_back(cell_t( | ||||||||||||
access_output, name, | ||||||||||||
std::bind(&implementation_t::calculate_expression, std::ref(*this), position, expression), | ||||||||||||
cell_set_m.size(), nullptr)); | ||||||||||||
|
||||||||||||
output_index_m.insert(cell_set_m.back()); | ||||||||||||
|
||||||||||||
|
@@ -744,11 +766,10 @@ void sheet_t::implementation_t::add_interface(name_t name, bool linked, | |||||||||||
scope_value_t<bool> scope(initialize_mode_m, true); | ||||||||||||
|
||||||||||||
if (initializer_expression.size()) { | ||||||||||||
cell_set_m.push_back( | ||||||||||||
cell_t(name, linked, | ||||||||||||
std::bind(&implementation_t::calculate_expression, std::ref(*this), | ||||||||||||
position1, initializer_expression), | ||||||||||||
cell_set_m.size())); | ||||||||||||
cell_set_m.push_back(cell_t(name, linked, | ||||||||||||
std::bind(&implementation_t::calculate_expression, | ||||||||||||
std::ref(*this), position1, initializer_expression), | ||||||||||||
cell_set_m.size())); | ||||||||||||
} else { | ||||||||||||
cell_set_m.push_back(cell_t(name, linked, cell_t::calculator_t(), cell_set_m.size())); | ||||||||||||
} | ||||||||||||
|
@@ -763,7 +784,7 @@ void sheet_t::implementation_t::add_interface(name_t name, bool linked, | |||||||||||
// REVISIT (sparent) : Non-transactional on failure. | ||||||||||||
cell_set_m.push_back(cell_t(access_interface_output, name, | ||||||||||||
std::bind(&implementation_t::calculate_expression, | ||||||||||||
std::ref(*this), position2, expression), | ||||||||||||
std::ref(*this), position2, expression), | ||||||||||||
cell_set_m.size(), &cell_set_m.back())); | ||||||||||||
} else { | ||||||||||||
cell_set_m.push_back(cell_t(access_interface_output, name, | ||||||||||||
|
@@ -831,10 +852,10 @@ void sheet_t::implementation_t::add_constant(name_t name, any_regular_t value) { | |||||||||||
|
||||||||||||
void sheet_t::implementation_t::add_logic(name_t logic, const line_position_t& position, | ||||||||||||
const array_t& expression) { | ||||||||||||
cell_set_m.push_back(cell_t(access_logic, logic, | ||||||||||||
std::bind(&implementation_t::calculate_expression, | ||||||||||||
std::ref(*this), position, expression), | ||||||||||||
cell_set_m.size(), nullptr)); | ||||||||||||
cell_set_m.push_back(cell_t( | ||||||||||||
access_logic, logic, | ||||||||||||
std::bind(&implementation_t::calculate_expression, std::ref(*this), position, expression), | ||||||||||||
cell_set_m.size(), nullptr)); | ||||||||||||
|
||||||||||||
if (!name_index_m.insert(cell_set_m.back()).second) { | ||||||||||||
throw stream_error_t(make_string("cell named '", logic.c_str(), "'already exists."), | ||||||||||||
|
@@ -847,10 +868,10 @@ void sheet_t::implementation_t::add_logic(name_t logic, const line_position_t& p | |||||||||||
void sheet_t::implementation_t::add_invariant(name_t name, const line_position_t& position, | ||||||||||||
const array_t& expression) { | ||||||||||||
// REVISIT (sparent) : Non-transactional on failure. | ||||||||||||
cell_set_m.push_back(cell_t(access_invariant, name, | ||||||||||||
std::bind(&implementation_t::calculate_expression, | ||||||||||||
std::ref(*this), position, expression), | ||||||||||||
cell_set_m.size(), nullptr)); | ||||||||||||
cell_set_m.push_back(cell_t( | ||||||||||||
access_invariant, name, | ||||||||||||
std::bind(&implementation_t::calculate_expression, std::ref(*this), position, expression), | ||||||||||||
cell_set_m.size(), nullptr)); | ||||||||||||
|
||||||||||||
output_index_m.insert(cell_set_m.back()); | ||||||||||||
|
||||||||||||
|
@@ -936,7 +957,7 @@ sheet_t::connection_t sheet_t::implementation_t::monitor_enabled(name_t n, const | |||||||||||
(touch_set & priority_accessed_m).any())); | ||||||||||||
|
||||||||||||
return monitor_enabled_m.connect(std::bind(&sheet_t::implementation_t::enabled_filter, this, | ||||||||||||
touch_set, iter->cell_set_pos_m, monitor, _1, _2)); | ||||||||||||
touch_set, iter->cell_set_pos_m, monitor, _1, _2)); | ||||||||||||
} | ||||||||||||
|
||||||||||||
/**************************************************************************************************/ | ||||||||||||
|
@@ -988,8 +1009,8 @@ sheet_t::implementation_t::monitor_contributing(name_t n, const dictionary_t& ma | |||||||||||
monitor(contributing_set(mark, iter->contributing_m)); | ||||||||||||
|
||||||||||||
return iter->monitor_contributing_m.connect( | ||||||||||||
std::bind(monitor, std::bind(&sheet_t::implementation_t::contributing_set, | ||||||||||||
std::ref(*this), mark, _1))); | ||||||||||||
std::bind(monitor, std::bind(&sheet_t::implementation_t::contributing_set, std::ref(*this), | ||||||||||||
mark, _1))); | ||||||||||||
} | ||||||||||||
|
||||||||||||
/**************************************************************************************************/ | ||||||||||||
|
@@ -1018,10 +1039,9 @@ priority_t sheet_t::implementation_t::name_to_priority(name_t name) const { | |||||||||||
void sheet_t::implementation_t::flow(cell_bits_t& priority_accessed) { | ||||||||||||
// Generate the set of cells connected to unresolved relations | ||||||||||||
vector<cell_t*> cells; | ||||||||||||
for (relation_cell_set_t::iterator f(relation_cell_set_m.begin()), l(relation_cell_set_m.end()); | ||||||||||||
f != l; ++f) { | ||||||||||||
if (!f->resolved_m) | ||||||||||||
cells.insert(cells.end(), f->edges_m.begin(), f->edges_m.end()); | ||||||||||||
for (const auto& relation : relation_cell_set_m) { | ||||||||||||
if (!relation.resolved_m) | ||||||||||||
append(cells, relation.edges_m); | ||||||||||||
} | ||||||||||||
sort(cells); | ||||||||||||
cells.erase(unique(cells), cells.end()); | ||||||||||||
|
@@ -1033,8 +1053,8 @@ void sheet_t::implementation_t::flow(cell_bits_t& priority_accessed) { | |||||||||||
// REVISIT <seanparent@google.com> : This is an approximation for enablement that could do | ||||||||||||
// better with connected components | ||||||||||||
|
||||||||||||
for (vector<cell_t*>::iterator f = cells.begin(), l = cells.end(); f != l; ++f) { | ||||||||||||
priority_accessed.set((*f)->interface_input_m->cell_set_pos_m); | ||||||||||||
for (const auto& cell: cells) { | ||||||||||||
priority_accessed.set(cell->interface_input_m->cell_set_pos_m); | ||||||||||||
} | ||||||||||||
|
||||||||||||
/* | ||||||||||||
|
@@ -1057,120 +1077,83 @@ void sheet_t::implementation_t::flow(cell_bits_t& priority_accessed) { | |||||||||||
|
||||||||||||
cell.resolved_m = true; | ||||||||||||
|
||||||||||||
for (relation_index_t::iterator f = cell.relation_index_m.begin(), | ||||||||||||
l = cell.relation_index_m.end(); | ||||||||||||
f != l; ++f) { | ||||||||||||
|
||||||||||||
if ((*f)->resolved_m) | ||||||||||||
for (const auto& relation : cell.relation_index_m) { | ||||||||||||
if (relation->resolved_m) | ||||||||||||
continue; | ||||||||||||
|
||||||||||||
--cell.relation_count_m; | ||||||||||||
|
||||||||||||
const relation_t* term = 0; | ||||||||||||
bool at_least_one = false; | ||||||||||||
|
||||||||||||
for (relation_set_t::iterator tf((*f)->terms_m.begin()), tl((*f)->terms_m.end()); | ||||||||||||
tf != tl; ++tf) { | ||||||||||||
// each term has a set of cells. If any cell is resolved then the term is resolved. | ||||||||||||
|
||||||||||||
bool resolved = false; | ||||||||||||
for (vector<name_t>::iterator fc = tf->name_set_m.begin(), | ||||||||||||
lc = tf->name_set_m.end(); | ||||||||||||
fc != lc; ++fc) { | ||||||||||||
|
||||||||||||
index_t::iterator iter = output_index_m.find(*fc); | ||||||||||||
assert(iter != output_index_m.end()); | ||||||||||||
|
||||||||||||
if (iter->resolved_m) { | ||||||||||||
resolved = true; | ||||||||||||
break; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
if (resolved) | ||||||||||||
continue; | ||||||||||||
|
||||||||||||
if (!term) { | ||||||||||||
term = &(*tf); | ||||||||||||
at_least_one = true; | ||||||||||||
} else { | ||||||||||||
term = NULL; | ||||||||||||
break; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
auto term = | ||||||||||||
find_if(relation->terms_m, [&](const auto& term) { return !resolved(term); }); | ||||||||||||
|
||||||||||||
// REVISIT (sparent) : Better error reporting here. | ||||||||||||
if (!at_least_one) { | ||||||||||||
if (term == relation->terms_m.end()) | ||||||||||||
throw std::logic_error("all terms of relation resolve but relation not applied."); | ||||||||||||
} | ||||||||||||
|
||||||||||||
if (!term) | ||||||||||||
// If there is more than one unresolved term, continue. | ||||||||||||
if (std::find_if(next(term), relation->terms_m.end(), [&](const auto& term) { | ||||||||||||
return !resolved(term); | ||||||||||||
}) != relation->terms_m.end()) | ||||||||||||
continue; | ||||||||||||
|
||||||||||||
// Flow out to lhs cells | ||||||||||||
|
||||||||||||
(*f)->resolved_m = true; | ||||||||||||
relation->resolved_m = true; | ||||||||||||
|
||||||||||||
for (std::size_t n = 0, count = term->name_set_m.size(); n != count; ++n) { | ||||||||||||
index_t::iterator iter = output_index_m.find(term->name_set_m[n]); | ||||||||||||
assert(iter != output_index_m.end()); | ||||||||||||
|
||||||||||||
cell_t& e = *iter; | ||||||||||||
auto& out_cell = output_cell(term->name_set_m[n]); | ||||||||||||
|
||||||||||||
// REVISIT (sparent) : Better error reporting here. | ||||||||||||
if (e.term_m) | ||||||||||||
if (out_cell.term_m) | ||||||||||||
throw logic_error("over constrained."); | ||||||||||||
|
||||||||||||
if (count == 1) { | ||||||||||||
e.term_m = std::bind(&implementation_t::calculate_expression, std::ref(*this), | ||||||||||||
term->position_m, term->expression_m); | ||||||||||||
out_cell.term_m = | ||||||||||||
std::bind(&implementation_t::calculate_expression, std::ref(*this), | ||||||||||||
term->position_m, term->expression_m); | ||||||||||||
} else { | ||||||||||||
e.term_m = std::bind(&implementation_t::calculate_indexed, std::ref(*this), | ||||||||||||
term->position_m, term->expression_m, n); | ||||||||||||
out_cell.term_m = | ||||||||||||
std::bind(&implementation_t::calculate_indexed, std::ref(*this), | ||||||||||||
term->position_m, term->expression_m, n); | ||||||||||||
} | ||||||||||||
|
||||||||||||
--e.relation_count_m; | ||||||||||||
--out_cell.relation_count_m; | ||||||||||||
|
||||||||||||
// This will be a derived cell and will have a priority lower than any cell | ||||||||||||
// contributing to it | ||||||||||||
|
||||||||||||
assert(e.interface_input_m && "Missing input half of interface cell."); | ||||||||||||
if (e.interface_input_m->linked_m) { | ||||||||||||
e.interface_input_m->priority_m = --priority_low_m; | ||||||||||||
assert(out_cell.interface_input_m && "Missing input half of interface cell."); | ||||||||||||
if (out_cell.interface_input_m->linked_m) { | ||||||||||||
out_cell.interface_input_m->priority_m = --priority_low_m; | ||||||||||||
} | ||||||||||||
|
||||||||||||
if (e.relation_count_m) | ||||||||||||
cells.push_back(&e); | ||||||||||||
if (out_cell.relation_count_m) | ||||||||||||
cells.push_back(&out_cell); | ||||||||||||
else | ||||||||||||
e.resolved_m = true; | ||||||||||||
out_cell.resolved_m = true; | ||||||||||||
} | ||||||||||||
|
||||||||||||
// Remove the relation from any cells to which it is still attached. That is, | ||||||||||||
// any cell which is still attached is an "in edge". | ||||||||||||
|
||||||||||||
vector<name_t> remaining_cells; | ||||||||||||
|
||||||||||||
for (relation_set_t::iterator tf((*f)->terms_m.begin()), tl((*f)->terms_m.end()); | ||||||||||||
tf != tl; ++tf) { | ||||||||||||
|
||||||||||||
if (&(*tf) == term) | ||||||||||||
for (const auto& in_term : relation->terms_m) { | ||||||||||||
if (&in_term == &(*term)) | ||||||||||||
continue; | ||||||||||||
|
||||||||||||
remaining_cells.insert(remaining_cells.end(), tf->name_set_m.begin(), | ||||||||||||
tf->name_set_m.end()); | ||||||||||||
append(remaining_cells, in_term.name_set_m); | ||||||||||||
} | ||||||||||||
|
||||||||||||
sort(remaining_cells); | ||||||||||||
remaining_cells.erase(unique(remaining_cells), remaining_cells.end()); | ||||||||||||
|
||||||||||||
for (vector<name_t>::iterator fc = remaining_cells.begin(), lc = remaining_cells.end(); | ||||||||||||
fc != lc; ++fc) { | ||||||||||||
|
||||||||||||
index_t::iterator iter = output_index_m.find(*fc); | ||||||||||||
assert(iter != output_index_m.end()); | ||||||||||||
|
||||||||||||
if (iter->resolved_m) | ||||||||||||
/// REVISIT (sparent) : Rather than for_each we should have a set of in place transform | ||||||||||||
/// algorithms. This is transform_if taking a projection and a predicate. | ||||||||||||
for (const auto& name : remaining_cells) { | ||||||||||||
auto& cell = output_cell(name); | ||||||||||||
if (cell.resolved_m) | ||||||||||||
continue; | ||||||||||||
--iter->relation_count_m; | ||||||||||||
--cell.relation_count_m; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
assert(cell.relation_count_m == 0 && | ||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,9 +2,6 @@ add_executable (adam_smoke_test adam_smoke_test.cpp) | |
target_link_libraries(adam_smoke_test PRIVATE asl) | ||
|
||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") | ||
target_compile_options (adam_smoke_test PRIVATE "/DNOMINMAX") | ||
target_compile_options (adam_smoke_test PRIVATE "/permissive-") | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. @dabrahams @camio - There must be a better way to manage compiler settings. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If by
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/ @camio's statement that:
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 commentThe reason will be displayed to describe this comment to others. Learn more. I updated it to use PRIVATE/PUBLIC (more) appropriately. |
||
|
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.
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.