Customization point for to-string conversion#121
Open
ecatmur wants to merge 2 commits intoboostorg:developfrom
Open
Customization point for to-string conversion#121ecatmur wants to merge 2 commits intoboostorg:developfrom
ecatmur wants to merge 2 commits intoboostorg:developfrom
Conversation
The use of boost::lexical_cast for default_value and implicit_value has several deficiencies; firstly, with the adoption of std::format in C++20 it is becoming more and more usual for libraries to regard iostreams formatting as deprecated and not provide ostream operator<< overloads for classes. Secondly, the standard library has added ostreams formatting for classes (esp. std::chrono) at different times, making it confusing when code works on one compiler and not another; it is possible to always use the two-argument overloads of default_value and implicit_value, but this is extra repeated work for the user and encourages providing string literals which may get out of sync with the actually provided default/implicit value. Finally, as a general point, when validate() is customized it may use a different format than iostreams (often terser and avoiding characters that are difficult to supply on the command line).
In conclusion, it would be useful to lexical_cast to std::string indirectly, via a customization point that can be customized by the user specifically for Boost.ProgramOptions.
This PR supplies a customization point in boost::program_options namespace with primary overload:
template<class T, class charT>
std::string make_textual(const T& v, typed_value<T, charT>*, long)
{
return boost::lexical_cast<std::string>(v);
}
The first parameter is the value being textualized (by default_value or implicit_value single-argument overloads).
The second parameter is mainly for ADL and disambiguation, but also if non-null it provides access to the 'typed_value' being modified.
The third parameter is for overload resolution, as with 'validate'.
There is one overload provided, for std::vector<T>:
template<class T, class charT>
std::string make_textual(const std::vector<T>& v, typed_value<std::vector<T>, charT>*, long)
{
std::string textual;
for (unsigned i = 0; i < v.size(); ++i)
{
if (i != 0)
textual += ' ';
textual += make_textual(v[i], (typed_value<T, charT>*)0, 0);
}
return textual;
}
This mirrors the overload of 'validate' for std::vector<T>, since it would be surprising for 'make_textual' to work where 'validate' does. It also illustrates how to recursively invoke 'make_textual', and that the 'typed_value' parameter may be null. This is just conceivably a breaking change if some user has provided operator<< for std::vector<T> but not for T for some type T, but this seems very unlikely.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The use of boost::lexical_cast for default_value and implicit_value has several deficiencies; firstly, with the adoption of std::format in C++20 it is becoming more and more usual for libraries to regard iostreams formatting as deprecated and not provide ostream operator<< overloads for classes. Secondly, the standard library has added ostreams formatting for classes (esp. std::chrono) at different times, making it confusing when code works on one compiler and not another; it is possible to always use the two-argument overloads of default_value and implicit_value, but this is extra repeated work for the user and encourages providing string literals which may get out of sync with the actually provided default/implicit value. Finally, as a general point, when validate() is customized it may use a different format than iostreams (often terser and avoiding characters that are difficult to supply on the command line).
In conclusion, it would be useful to lexical_cast to std::string indirectly, via a customization point that can be customized by the user specifically for Boost.ProgramOptions.
This PR supplies a customization point in boost::program_options namespace with primary overload:
The first parameter is the value being textualized (by default_value or implicit_value single-argument overloads). The second parameter is mainly for ADL and disambiguation, but also if non-null it provides access to the 'value_semantic' being modified. The third parameter is for overload resolution, as with 'validate'.
There is one overload provided, for std::vector:
This mirrors the overload of 'validate' for std::vector, since it would be surprising for 'make_textual' to work where 'validate' does. It also illustrates how to recursively invoke 'make_textual', and that the 'value_semantic' parameter may be null. This is just conceivably a breaking change if some user has provided operator<< for std::vector but not for T for some type T, but this seems very unlikely.