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

Initial support for std::string_view #633

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
zeux marked this conversation as resolved.
Show resolved Hide resolved
exclude:
- os: macos
compiler: g++
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
7 changes: 5 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions src/pugiconfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand Down
51 changes: 51 additions & 0 deletions src/pugixml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5441,6 +5441,15 @@ 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(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;
Expand All @@ -5455,6 +5464,15 @@ 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(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;
Expand Down Expand Up @@ -5848,6 +5866,18 @@ 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(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;
Expand All @@ -5868,6 +5898,18 @@ 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(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();
Expand Down Expand Up @@ -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 PUGI_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();
Expand Down
34 changes: 34 additions & 0 deletions src/pugixml.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 PUGI_HAS_STRING_VIEW
zeux marked this conversation as resolved.
Show resolved Hide resolved
# elif defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
# define PUGI_HAS_STRING_VIEW
# endif
#endif

// Include string_view if appropriate
#ifdef PUGI_HAS_STRING_VIEW
# include <string_view>
#endif

// Macro for deprecated features
#ifndef PUGIXML_DEPRECATED
# if defined(__GNUC__)
Expand Down Expand Up @@ -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 PUGI_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
Expand Down Expand Up @@ -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 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 +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 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 +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 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
56 changes: 56 additions & 0 deletions tests/test_dom_modify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ TEST_XML(dom_attr_set_name_with_size, "<node attr='value' />")
CHECK_NODE(doc, STR("<node n=\"value\"/>"));
}

#ifdef PUGI_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"));
Expand Down Expand Up @@ -225,6 +242,25 @@ TEST_XML(dom_node_set_name_with_size, "<node>text</node>")
CHECK_NODE(doc, STR("<n>text</n>"));
}

#ifdef PUGI_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")));
Expand Down Expand Up @@ -252,6 +288,26 @@ TEST_XML(dom_node_set_value_with_size, "<node>text</node>")
CHECK_NODE(doc, STR("<node>no text</node>"));
}

#ifdef PUGI_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")));
Expand Down
Loading