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

Add string view support (only impl, no tests) #627

Closed
wants to merge 11 commits into from
Closed
35 changes: 35 additions & 0 deletions src/pugixml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5441,6 +5441,13 @@ namespace pugi
return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, size);
}

#ifdef PUGI_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_attribute::set_name(const string_view_t rhs)
Copy link
Owner

Choose a reason for hiding this comment

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

Here and elsewhere const should not be used for arguments as they are passed by value

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is my usual style (as much const as possible), although I would use east const.
I don't have any problems with changing this, but out of curiosity, could you elaborate on your rationale?
The fact that they are passed by value is orthogonal to the fact that the value is not changed.
And because it is not changed it can be made const and IMHO consequently should be made const.

Copy link
Owner

@zeux zeux Sep 13, 2024

Choose a reason for hiding this comment

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

I personally do not subscribe to the "what could be made const should be" when that doesn't affect the interface; in this case, the caller behavior does not change based on whether the argument is const or not. I am aware of the argument that it can help prevent mistakes but I have not seen these happen much in practice so I view this as syntactic noise. You're welcome to disagree of course, but for consistency this const should be removed: pugixml doesn't have a single function with a const parameter that is passed by value, and the only uses of const locals are that where the value is a compile-time const (when this code was written this seemed to enhance readability, whether this is true or not is subjective).

{
return set_name(rhs.data(), rhs.size());
}
#endif

PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs)
{
if (!_attr) return false;
Expand All @@ -5455,6 +5462,13 @@ namespace pugi
return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, size);
}

#ifdef PUGI_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_attribute::set_value(const string_view_t rhs)
{
return set_value(rhs.data(), rhs.size());
}
#endif

PUGI_IMPL_FN bool xml_attribute::set_value(int rhs)
{
if (!_attr) return false;
Expand Down Expand Up @@ -5848,6 +5862,13 @@ namespace pugi
return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, size);
}

#ifdef PUGI_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_node::set_name(const string_view_t rhs)
{
return set_name(rhs.data(), rhd.size());
}
#endif

PUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs)
{
xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;
Expand All @@ -5868,6 +5889,13 @@ namespace pugi
return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, size);
}

#ifdef PUGI_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_node::set_value(const string_view_t rhs)
{
return set_value(rhs.data(), rhs.value());
}
#endif

PUGI_IMPL_FN xml_attribute xml_node::append_attribute(const char_t* name_)
{
if (!impl::allow_insert_attribute(type())) return xml_attribute();
Expand Down Expand Up @@ -6757,6 +6785,13 @@ namespace pugi
return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, size) : false;
}

#ifdef PUGI_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_text::set(const string_view_t rhs)
{
return set(rhs.data(), rhs.size());
}
#endif

PUGI_IMPL_FN bool xml_text::set(int rhs)
{
xml_node_struct* dn = _data_new();
Expand Down
40 changes: 40 additions & 0 deletions src/pugixml.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,26 @@
# endif
#endif

// Detect if C++ standard supports 'string_view' (2017 or higher)
Copy link
Owner

Choose a reason for hiding this comment

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

I would propose simplifying this to enabling internal PUGI_HAS_STRING_VIEW define if PUGIXML_STRING_VIEW define (note the PUGIXML prefix; PUGI_ prefix is for internal macros, PUGIXML_ prefix is for user facing configuration) is enabled, and dropping the _SUPPORTED macro as it does not seem to be useful.

Separately, PUGI_HAS_STRING_VIEW should be undef'd at the end of the header.

PUGIXML_STRING_VIEW should be added, commented out, in pugiconfig.hpp. We can note there that the macro will become unnecessary with future versions.

Copy link
Contributor Author

@brandl-muc brandl-muc Sep 13, 2024

Choose a reason for hiding this comment

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

A question regarding consistency: PUGI_HAS_STRING_VIEW seems to be the only macro using PUGI prefix in the header. All others use PUGIXML, e.g., PUGIXML_HAS_MOVE or PUGIXML_HAS_LONG_LONG which are auto-detected as well. PUGI-macros can only be found in the cpp.
So shouldn't it be PUGIXML_HAS_STRING_VIEW then?

Probably for the same reason it would be the only macro that is actually undefined in the header. (there is no #undef to be found) So I cannot orient on anything. Should I always #undef it even if I didn't defined it? (not that there is a #ifndef PUGI_HAS_STRING_VIEW)

Copy link
Owner

Choose a reason for hiding this comment

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

Apologies, I've misled you about the prefix; I forgot the conventions :) Indeed, it should be prefixed with PUGIXML_ and no undef is necessary. I mixed this up with the .cpp file where the prefix is PUGI_IMPL_ and the macros are undef'd to not pollute the namespace in header-only mode.

#ifndef PUGI_STRING_VIEW_SUPPORTED
# if defined(__cpp_lib_string_view) || __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
brandl-muc marked this conversation as resolved.
Show resolved Hide resolved
# define PUGI_STRING_VIEW_SUPPORTED 1
# endif
#endif
// Enable 'string_view' support if requested and supported
#ifndef PUGI_HAS_STRING_VIEW
# if defined(PUGI_ENABLE_STRING_VIEW) && !defined(PUGIXML_NO_STL)
# ifdef PUGI_STRING_VIEW_SUPPORTED
# define PUGI_HAS_STRING_VIEW
# else
# warning "Support for std::string_view was requested but is not supported by your C++ standard"
brandl-muc marked this conversation as resolved.
Show resolved Hide resolved
# endif
# endif
#endif
#ifdef PUGI_HAS_STRING_VIEW
# include <string_view>
#endif

// Character interface macros
#ifdef PUGIXML_WCHAR_MODE
# define PUGIXML_TEXT(t) L ## t
Expand All @@ -140,6 +160,11 @@ namespace pugi
// String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE
typedef std::basic_string<PUGIXML_CHAR> string_t;
#endif

#ifdef PUGI_HAS_STRING_VIEW
// String view type used for overloads of functions that accept strings; depends on PUGIXML_WCHAR_MODE
using string_view_t = std::basic_string_view<char_t>;
brandl-muc marked this conversation as resolved.
Show resolved Hide resolved
#endif
}

// The PugiXML namespace
Expand Down Expand Up @@ -423,8 +448,14 @@ namespace pugi
// Set attribute name/value (returns false if attribute is empty or there is not enough memory)
bool set_name(const char_t* rhs);
bool set_name(const char_t* rhs, size_t size);
#ifdef PUGI_HAS_STRING_VIEW
bool set_name(string_view_t rhs);
#endif
bool set_value(const char_t* rhs);
bool set_value(const char_t* rhs, size_t size);
#ifdef PUGI_HAS_STRING_VIEW
bool set_value(string_view_t rhs);
#endif

// Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
bool set_value(int rhs);
Expand Down Expand Up @@ -559,8 +590,14 @@ namespace pugi
// Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value)
bool set_name(const char_t* rhs);
bool set_name(const char_t* rhs, size_t size);
#ifdef PUGI_HAS_STRING_VIEW
bool set_name(string_view_t rhs);
#endif
bool set_value(const char_t* rhs);
bool set_value(const char_t* rhs, size_t size);
#ifdef PUGI_HAS_STRING_VIEW
bool set_value(string_view_t rhs);
#endif

// Add attribute with specified name. Returns added attribute, or empty attribute on errors.
xml_attribute append_attribute(const char_t* name);
Expand Down Expand Up @@ -790,6 +827,9 @@ namespace pugi
// Set text (returns false if object is empty or there is not enough memory)
bool set(const char_t* rhs);
bool set(const char_t* rhs, size_t size);
#ifdef PUGI_HAS_STRING_VIEW
bool set(string_view_t rhs);
#endif

// Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
bool set(int rhs);
Expand Down