0
0
mirror of https://github.com/zeux/pugixml.git synced 2024-12-25 20:14:10 +08:00

Initial support for std::string_view (#633)

When PUGIXML_STRING_VIEW define is set and C++17 is available, add std::string_view support to a few functions. In the future, string view support will be enabled without the need for an extra define, but for now the support is opt-in to reduce compatibility risks.

PR is based on initial contribution by @brandl-muc.
This commit is contained in:
dantargz 2024-10-22 13:31:54 -07:00 committed by GitHub
parent 3b17184379
commit a0db6e2185
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 251 additions and 4 deletions

View File

@ -12,7 +12,7 @@ jobs:
matrix:
os: [ubuntu, macos]
compiler: [g++, clang++]
defines: [standard, PUGIXML_WCHAR_MODE, PUGIXML_COMPACT, PUGIXML_NO_EXCEPTIONS]
defines: [standard, PUGIXML_WCHAR_MODE, PUGIXML_COMPACT, PUGIXML_NO_EXCEPTIONS, PUGIXML_STRING_VIEW]
exclude:
- os: macos
compiler: g++
@ -25,6 +25,7 @@ jobs:
export CXX=${{matrix.compiler}}
make test cxxstd=c++11 defines=${{matrix.defines}} config=release -j2
make test cxxstd=c++98 defines=${{matrix.defines}} config=debug -j2
make test cxxstd=c++17 defines=${{matrix.defines}} config=debug -j2
make test defines=${{matrix.defines}} config=sanitize -j2
- name: make coverage
if: ${{!(matrix.os == 'ubuntu' && matrix.compiler == 'clang++')}} # linux/clang produces coverage info gcov can't parse
@ -38,7 +39,7 @@ jobs:
strategy:
matrix:
arch: [Win32, x64]
defines: [standard, PUGIXML_WCHAR_MODE, PUGIXML_COMPACT, PUGIXML_NO_EXCEPTIONS]
defines: [standard, PUGIXML_WCHAR_MODE, PUGIXML_COMPACT, PUGIXML_NO_EXCEPTIONS, PUGIXML_STRING_VIEW]
steps:
- uses: actions/checkout@v1
- name: cmake configure
@ -50,3 +51,10 @@ jobs:
Debug/pugixml-check.exe
cmake --build . -- -property:Configuration=Release -verbosity:minimal
Release/pugixml-check.exe
- name: cmake configure (c++17)
run: cmake . -DPUGIXML_BUILD_TESTS=ON -DCMAKE_CXX_STANDARD=17 -D${{matrix.defines}}=ON -A ${{matrix.arch}}
- name: cmake test (c++17)
shell: bash # necessary for fail-fast
run: |
cmake --build . -- -property:Configuration=Debug -verbosity:minimal
Debug/pugixml-check.exe

View File

@ -48,14 +48,17 @@ option(PUGIXML_INSTALL "Enable installation rules" ON)
option(PUGIXML_NO_XPATH "Disable XPath" OFF)
option(PUGIXML_NO_STL "Disable STL" OFF)
option(PUGIXML_NO_EXCEPTIONS "Disable Exceptions" OFF)
mark_as_advanced(PUGIXML_NO_XPATH PUGIXML_NO_STL PUGIXML_NO_EXCEPTIONS)
option(PUGIXML_STRING_VIEW "Enable std::string_view overloads" OFF) # requires C++17 and for PUGI_NO_STL to be OFF
mark_as_advanced(PUGIXML_NO_XPATH PUGIXML_NO_STL PUGIXML_NO_EXCEPTIONS PUGIXML_STRING_VIEW)
set(PUGIXML_PUBLIC_DEFINITIONS
$<$<BOOL:${PUGIXML_WCHAR_MODE}>:PUGIXML_WCHAR_MODE>
$<$<BOOL:${PUGIXML_COMPACT}>:PUGIXML_COMPACT>
$<$<BOOL:${PUGIXML_NO_XPATH}>:PUGIXML_NO_XPATH>
$<$<BOOL:${PUGIXML_NO_STL}>:PUGIXML_NO_STL>
$<$<BOOL:${PUGIXML_NO_EXCEPTIONS}>:PUGIXML_NO_EXCEPTIONS>)
$<$<BOOL:${PUGIXML_NO_EXCEPTIONS}>:PUGIXML_NO_EXCEPTIONS>
$<$<BOOL:${PUGIXML_STRING_VIEW}>:PUGIXML_STRING_VIEW>
)
# This is used to backport a CMake 3.15 feature, but is also forwards compatible
if (NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)

View File

@ -49,6 +49,11 @@
// Uncomment this to enable long long support
// #define PUGIXML_HAS_LONG_LONG
// Uncomment this to enable support for std::string_view (requires c++17 and for PUGIXML_NO_STL to not be set)
// Note: In a future version of pugixml this macro will become obsolete.
// Support will then be enabled automatically if the used C++ standard supports it.
// #define PUGIXML_STRING_VIEW
#endif
/**

View File

@ -5441,6 +5441,15 @@ namespace pugi
return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, size);
}
#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_attribute::set_name(string_view_t rhs)
{
if (!_attr) return false;
return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs.data(), rhs.size());
}
#endif
PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs)
{
if (!_attr) return false;
@ -5455,6 +5464,15 @@ namespace pugi
return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, size);
}
#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_attribute::set_value(string_view_t rhs)
{
if (!_attr) return false;
return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs.data(), rhs.size());
}
#endif
PUGI_IMPL_FN bool xml_attribute::set_value(int rhs)
{
if (!_attr) return false;
@ -5848,6 +5866,18 @@ namespace pugi
return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, size);
}
#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_node::set_name(string_view_t rhs)
{
xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;
if (type_ != node_element && type_ != node_pi && type_ != node_declaration)
return false;
return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs.data(), rhs.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;
@ -5868,6 +5898,18 @@ namespace pugi
return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, size);
}
#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_node::set_value(string_view_t rhs)
{
xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;
if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype)
return false;
return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs.data(), rhs.size());
}
#endif
PUGI_IMPL_FN xml_attribute xml_node::append_attribute(const char_t* name_)
{
if (!impl::allow_insert_attribute(type())) return xml_attribute();
@ -6757,6 +6799,15 @@ namespace pugi
return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, size) : false;
}
#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_text::set(string_view_t rhs)
{
xml_node_struct* dn = _data_new();
return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs.data(), rhs.size()) : false;
}
#endif
PUGI_IMPL_FN bool xml_text::set(int rhs)
{
xml_node_struct* dn = _data_new();

View File

@ -38,6 +38,20 @@
# include <string>
#endif
// Check if std::string_view is both requested and available
#if defined(PUGIXML_STRING_VIEW) && !defined(PUGIXML_NO_STL)
# if __cplusplus >= 201703L
# define PUGIXML_HAS_STRING_VIEW
# elif defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
# define PUGIXML_HAS_STRING_VIEW
# endif
#endif
// Include string_view if appropriate
#ifdef PUGIXML_HAS_STRING_VIEW
# include <string_view>
#endif
// Macro for deprecated features
#ifndef PUGIXML_DEPRECATED
# if defined(__GNUC__)
@ -140,6 +154,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 PUGIXML_HAS_STRING_VIEW
// String view type used for operations that can work with a length delimited string; depends on PUGIXML_WCHAR_MODE
typedef std::basic_string_view<PUGIXML_CHAR> string_view_t;
#endif
}
// The PugiXML namespace
@ -423,8 +442,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 PUGIXML_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 PUGIXML_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);
@ -559,8 +584,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 PUGIXML_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 PUGIXML_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);
@ -790,6 +821,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 PUGIXML_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);

View File

@ -56,6 +56,23 @@ TEST_XML(dom_attr_set_name_with_size, "<node attr='value' />")
CHECK_NODE(doc, STR("<node n=\"value\"/>"));
}
#ifdef PUGIXML_HAS_STRING_VIEW
TEST_XML(dom_attr_set_name_with_string_view, "<node attr='value' />")
{
xml_attribute attr = doc.child(STR("node")).attribute(STR("attr"));
CHECK(attr.set_name(string_view_t()));
CHECK(!xml_attribute().set_name(string_view_t()));
CHECK_NODE(doc, STR("<node :anonymous=\"value\"/>"));
CHECK(attr.set_name(string_view_t(STR("n1234"), 1)));
CHECK(!xml_attribute().set_name(string_view_t(STR("nfail"), 1)));
CHECK_NODE(doc, STR("<node n=\"value\"/>"));
}
#endif
TEST_XML(dom_attr_set_value, "<node/>")
{
xml_node node = doc.child(STR("node"));
@ -225,6 +242,25 @@ TEST_XML(dom_node_set_name_with_size, "<node>text</node>")
CHECK_NODE(doc, STR("<n>text</n>"));
}
#ifdef PUGIXML_HAS_STRING_VIEW
TEST_XML(dom_node_set_name_with_string_view, "<node>text</node>")
{
xml_node node = doc.child(STR("node"));
CHECK(node.set_name(string_view_t()));
CHECK(!node.first_child().set_name(string_view_t(STR("n42"), 1)));
CHECK(!xml_node().set_name(string_view_t(STR("nanothername"), 1)));
CHECK_NODE(doc, STR("<:anonymous>text</:anonymous>"));
CHECK(node.set_name(string_view_t(STR("nlongname"), 1)));
CHECK(!doc.child(STR("node")).first_child().set_name(string_view_t(STR("n42"), 1)));
CHECK(!xml_node().set_name(string_view_t(STR("nanothername"), 1)));
CHECK_NODE(doc, STR("<n>text</n>"));
}
#endif
TEST_XML(dom_node_set_value, "<node>text</node>")
{
CHECK(doc.child(STR("node")).first_child().set_value(STR("no text")));
@ -252,6 +288,26 @@ TEST_XML(dom_node_set_value_with_size, "<node>text</node>")
CHECK_NODE(doc, STR("<node>no text</node>"));
}
#ifdef PUGIXML_HAS_STRING_VIEW
TEST_XML(dom_node_set_value_partially_with_string_view, "<node>text</node>")
{
CHECK(doc.child(STR("node")).first_child().set_value(string_view_t(STR("no text"), 2)));
CHECK(!doc.child(STR("node")).set_value(string_view_t(STR("no text"), 2)));
CHECK(!xml_node().set_value(string_view_t(STR("no text"), 2)));
CHECK_NODE(doc, STR("<node>no</node>"));
}
TEST_XML(dom_node_set_value_with_string_view, "<node>text</node>")
{
CHECK(doc.child(STR("node")).first_child().set_value(string_view_t(STR("no text"), 7)));
CHECK(!doc.child(STR("node")).set_value(string_view_t(STR("no text"), 7)));
CHECK(!xml_node().set_value(string_view_t(STR("no text"), 7)));
CHECK_NODE(doc, STR("<node>no text</node>"));
}
#endif
TEST_XML(dom_node_set_value_allocated, "<node>text</node>")
{
CHECK(doc.child(STR("node")).first_child().set_value(STR("no text")));

View File

@ -301,6 +301,69 @@ TEST_XML(dom_text_set_partially_with_size, "<node/>")
CHECK_NODE(node, STR("<node></node>"));
}
#ifdef PUGIXML_HAS_STRING_VIEW
TEST_XML(dom_text_set_with_string_view, "<node/>")
{
xml_node node = doc.child(STR("node"));
xml_text t = node.text();
t.set(string_view_t(STR("")));
CHECK(node.first_child().type() == node_pcdata);
CHECK_NODE(node, STR("<node></node>"));
t.set(string_view_t(STR("boo")));
CHECK(node.first_child().type() == node_pcdata);
CHECK(node.first_child() == node.last_child());
CHECK_NODE(node, STR("<node>boo</node>"));
t.set(string_view_t(STR("foobarfoobar")));
CHECK(node.first_child().type() == node_pcdata);
CHECK(node.first_child() == node.last_child());
CHECK_NODE(node, STR("<node>foobarfoobar</node>"));
t.set(string_view_t(STR("")));
CHECK(node.first_child().type() == node_pcdata);
CHECK_NODE(node, STR("<node></node>"));
t.set(string_view_t(STR("something")));
CHECK(node.first_child().type() == node_pcdata);
CHECK_NODE(node, STR("<node>something</node>"));
// empty string view (null data pointer)
t.set(string_view_t());
CHECK(node.first_child().type() == node_pcdata);
CHECK_NODE(node, STR("<node></node>"));
t.set(string_view_t(STR("afternulldata")));
CHECK(node.first_child().type() == node_pcdata);
CHECK_NODE(node, STR("<node>afternulldata</node>"));
}
TEST_XML(dom_text_set_partially_with_string_view, "<node/>")
{
xml_node node = doc.child(STR("node"));
xml_text t = node.text();
t.set(string_view_t(STR("foo"), 0));
CHECK(node.first_child().type() == node_pcdata);
CHECK_NODE(node, STR("<node></node>"));
t.set(string_view_t(STR("boofoo"), 3));
CHECK(node.first_child().type() == node_pcdata);
CHECK(node.first_child() == node.last_child());
CHECK_NODE(node, STR("<node>boo</node>"));
t.set(string_view_t(STR("foobarfoobar"), 3));
CHECK(node.first_child().type() == node_pcdata);
CHECK(node.first_child() == node.last_child());
CHECK_NODE(node, STR("<node>foo</node>"));
t.set(string_view_t(STR("foo"), 0));
CHECK(node.first_child().type() == node_pcdata);
CHECK_NODE(node, STR("<node></node>"));
}
#endif
TEST_XML(dom_text_assign, "<node/>")
{
xml_node node = doc.child(STR("node"));
@ -334,6 +397,10 @@ TEST_XML(dom_text_set_value, "<node/>")
CHECK(node.append_child(STR("text1")).text().set(STR("v1")));
CHECK(!xml_text().set(STR("v1")));
CHECK(!xml_text().set(STR("v1"), 2));
#ifdef PUGIXML_HAS_STRING_VIEW
CHECK(!xml_text().set(string_view_t(STR("v1"))));
#endif
CHECK(node.append_child(STR("text2")).text().set(-2147483647));
CHECK(node.append_child(STR("text3")).text().set(-2147483647 - 1));
@ -470,6 +537,29 @@ TEST_XML(dom_text_middle, "<node><c1>notthisone</c1>text<c2/></node>")
CHECK(t.data() == node.last_child());
}
#ifdef PUGIXML_HAS_STRING_VIEW
TEST_XML(dom_text_middle_string_view, "<node><c1>notthisone</c1>text<c2/></node>")
{
xml_node node = doc.child(STR("node"));
xml_text t = node.text();
CHECK_STRING(t.get(), STR("text"));
t.set(string_view_t(STR("notext")));
CHECK_NODE(node, STR("<node><c1>notthisone</c1>notext<c2/></node>"));
CHECK(node.remove_child(t.data()));
CHECK(!t);
CHECK_NODE(node, STR("<node><c1>notthisone</c1><c2/></node>"));
t.set(string_view_t(STR("yestext")));
CHECK(t);
CHECK_NODE(node, STR("<node><c1>notthisone</c1><c2/>yestext</node>"));
CHECK(t.data() == node.last_child());
}
#endif
TEST_XML_FLAGS(dom_text_data, "<node><a>foo</a><b><![CDATA[bar]]></b><c><?pi value?></c><d/></node>", parse_default | parse_pi)
{
xml_node node = doc.child(STR("node"));