diff --git a/include/boost/program_options/config.hpp b/include/boost/program_options/config.hpp index 8b7052174..e5b6d03d4 100644 --- a/include/boost/program_options/config.hpp +++ b/include/boost/program_options/config.hpp @@ -14,7 +14,7 @@ #if BOOST_VERSION >= 103100 // works beginning from Boost V1.31.0 /////////////////////////////////////////////////////////////////////////////// -// enable automatic library variant selection +// enable automatic library variant selection #if !defined(BOOST_PROGRAM_OPTIONS_SOURCE) && !defined(BOOST_ALL_NO_LIB) && \ !defined(BOOST_PROGRAM_OPTIONS_NO_LIB) @@ -47,6 +47,4 @@ #define BOOST_PROGRAM_OPTIONS_DECL #endif - #endif // PROGRAM_OPTIONS_CONFIG_HK_2004_01_11 - diff --git a/include/boost/program_options/variables_map.hpp b/include/boost/program_options/variables_map.hpp index 362dedf28..9560db1d8 100644 --- a/include/boost/program_options/variables_map.hpp +++ b/include/boost/program_options/variables_map.hpp @@ -138,13 +138,24 @@ namespace boost { namespace program_options { const abstract_variables_map* m_next; }; + namespace details + { + typedef + #if !defined(BOOST_PROGRAM_OPTIONS_NO_TRANSPARENT_COMPARATOR) && BOOST_CXX_VERSION >= 201402L + std::less<> + #else + std::less<std::string> + #endif + comparator_t; + } + /** Concrete variables map which store variables in real map. This class is derived from std::map<std::string, variable_value>, so you can use all map operators to examine its content. */ class BOOST_PROGRAM_OPTIONS_DECL variables_map : public abstract_variables_map, - public std::map<std::string, variable_value> + public std::map<std::string, variable_value, details::comparator_t> { public: variables_map(); diff --git a/src/variables_map.cpp b/src/variables_map.cpp index 94ac6148c..97d322672 100644 --- a/src/variables_map.cpp +++ b/src/variables_map.cpp @@ -32,7 +32,7 @@ namespace boost { namespace program_options { // We need to access map's operator[], not the overriden version // variables_map. Ehmm.. messy. - std::map<std::string, variable_value>& m = xm; + std::map<std::string, variable_value, details::comparator_t>& m = xm; std::set<std::string> new_final; @@ -194,7 +194,7 @@ namespace boost { namespace program_options { void variables_map::clear() { - std::map<std::string, variable_value>::clear(); + std::map<std::string, variable_value, details::comparator_t>::clear(); m_final.clear(); m_required.clear(); } @@ -220,7 +220,7 @@ namespace boost { namespace program_options { { const string& opt = r->first; const string& display_opt = r->second; - map<string, variable_value>::const_iterator iter = find(opt); + map<string, variable_value, details::comparator_t>::const_iterator iter = find(opt); if (iter == end() || iter->second.empty()) { boost::throw_exception(required_option(display_opt)); @@ -229,7 +229,7 @@ namespace boost { namespace program_options { } // Lastly, run notify actions. - for (map<string, variable_value>::iterator k = begin(); + for (map<string, variable_value, details::comparator_t>::iterator k = begin(); k != end(); ++k) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ea8832ec1..70f9b0242 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,5 +23,6 @@ boost_test(TYPE run SOURCES unrecognized_test.cpp) boost_test(TYPE run SOURCES required_test.cpp ARGUMENTS ${CMAKE_CURRENT_SOURCE_DIR}/required_test.cfg) boost_test(TYPE run SOURCES exception_txt_test.cpp) boost_test(TYPE run SOURCES optional_test.cpp) +boost_test(TYPE run SOURCES transparent_comparator_test.cpp) boost_test(TYPE run SOURCES quick.cpp ARGUMENTS --path=initial LINK_LIBRARIES Boost::core) diff --git a/test/transparent_comparator_test.cpp b/test/transparent_comparator_test.cpp new file mode 100644 index 000000000..b51bd97c2 --- /dev/null +++ b/test/transparent_comparator_test.cpp @@ -0,0 +1,56 @@ +#include <boost/config.hpp> +#include <boost/core/noncopyable.hpp> +#include <boost/program_options/variables_map.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/utility/string_view.hpp> + +#ifndef BOOST_NO_CXX17_HDR_STRING_VIEW +#include <string_view> +#endif + +namespace { +// Contrived class that proves no extra copies are being made and only operator< is being used +struct Dummy : private boost::noncopyable +{}; + +bool operator<(const Dummy&, const std::string& rhs) { return "test" < rhs; } +bool operator<(const std::string& lhs, const Dummy&) { return lhs < "test"; } +} + +void test_transparent_comparator() +{ + boost::program_options::variables_map vm; + vm.insert({"test", boost::program_options::variable_value(42, false)}); + + boost::program_options::variables_map::iterator iter; + + // c-string literal. + // If pre c++14, a std::string object will be implicitly created on the call to find() + // after c++14 no extra object will be created + iter = vm.find("test"); + BOOST_REQUIRE(iter != vm.end()); + BOOST_CHECK(boost::any_cast<int>(iter->second.value()) == 42); + + // Rest of the checks require c++14 for a transparent comparator +#ifdef BOOST_CXX_VERSION > 201402L + // boost::string_view + const boost::string_view bsv("test"); + iter = vm.find(bsv); + BOOST_REQUIRE(iter != vm.end()); + BOOST_CHECK(boost::any_cast<int>(iter->second.value()) == 42); + + #ifndef BOOST_NO_CXX17_HDR_STRING_VIEW + // std::string_view + const std::string_view sv("test"); + iter = vm.find(sv); + BOOST_REQUIRE(iter != vm.end()); + BOOST_CHECK(boost::any_cast<int>(iter->second.value()) == 42); + #endif + + // custom class + Dummy d; + iter = vm.find(d); + BOOST_REQUIRE(iter != vm.end()); + BOOST_CHECK(boost::any_cast<int>(iter->second.value()) == 42); +#endif +}