Skip to content

Commit a0db6e2

Browse files
authored
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.
1 parent 3b17184 commit a0db6e2

File tree

7 files changed

+251
-4
lines changed

7 files changed

+251
-4
lines changed

.github/workflows/build.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
matrix:
1313
os: [ubuntu, macos]
1414
compiler: [g++, clang++]
15-
defines: [standard, PUGIXML_WCHAR_MODE, PUGIXML_COMPACT, PUGIXML_NO_EXCEPTIONS]
15+
defines: [standard, PUGIXML_WCHAR_MODE, PUGIXML_COMPACT, PUGIXML_NO_EXCEPTIONS, PUGIXML_STRING_VIEW]
1616
exclude:
1717
- os: macos
1818
compiler: g++
@@ -25,6 +25,7 @@ jobs:
2525
export CXX=${{matrix.compiler}}
2626
make test cxxstd=c++11 defines=${{matrix.defines}} config=release -j2
2727
make test cxxstd=c++98 defines=${{matrix.defines}} config=debug -j2
28+
make test cxxstd=c++17 defines=${{matrix.defines}} config=debug -j2
2829
make test defines=${{matrix.defines}} config=sanitize -j2
2930
- name: make coverage
3031
if: ${{!(matrix.os == 'ubuntu' && matrix.compiler == 'clang++')}} # linux/clang produces coverage info gcov can't parse
@@ -38,7 +39,7 @@ jobs:
3839
strategy:
3940
matrix:
4041
arch: [Win32, x64]
41-
defines: [standard, PUGIXML_WCHAR_MODE, PUGIXML_COMPACT, PUGIXML_NO_EXCEPTIONS]
42+
defines: [standard, PUGIXML_WCHAR_MODE, PUGIXML_COMPACT, PUGIXML_NO_EXCEPTIONS, PUGIXML_STRING_VIEW]
4243
steps:
4344
- uses: actions/checkout@v1
4445
- name: cmake configure
@@ -50,3 +51,10 @@ jobs:
5051
Debug/pugixml-check.exe
5152
cmake --build . -- -property:Configuration=Release -verbosity:minimal
5253
Release/pugixml-check.exe
54+
- name: cmake configure (c++17)
55+
run: cmake . -DPUGIXML_BUILD_TESTS=ON -DCMAKE_CXX_STANDARD=17 -D${{matrix.defines}}=ON -A ${{matrix.arch}}
56+
- name: cmake test (c++17)
57+
shell: bash # necessary for fail-fast
58+
run: |
59+
cmake --build . -- -property:Configuration=Debug -verbosity:minimal
60+
Debug/pugixml-check.exe

CMakeLists.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,17 @@ option(PUGIXML_INSTALL "Enable installation rules" ON)
4848
option(PUGIXML_NO_XPATH "Disable XPath" OFF)
4949
option(PUGIXML_NO_STL "Disable STL" OFF)
5050
option(PUGIXML_NO_EXCEPTIONS "Disable Exceptions" OFF)
51-
mark_as_advanced(PUGIXML_NO_XPATH PUGIXML_NO_STL PUGIXML_NO_EXCEPTIONS)
51+
option(PUGIXML_STRING_VIEW "Enable std::string_view overloads" OFF) # requires C++17 and for PUGI_NO_STL to be OFF
52+
mark_as_advanced(PUGIXML_NO_XPATH PUGIXML_NO_STL PUGIXML_NO_EXCEPTIONS PUGIXML_STRING_VIEW)
5253

5354
set(PUGIXML_PUBLIC_DEFINITIONS
5455
$<$<BOOL:${PUGIXML_WCHAR_MODE}>:PUGIXML_WCHAR_MODE>
5556
$<$<BOOL:${PUGIXML_COMPACT}>:PUGIXML_COMPACT>
5657
$<$<BOOL:${PUGIXML_NO_XPATH}>:PUGIXML_NO_XPATH>
5758
$<$<BOOL:${PUGIXML_NO_STL}>:PUGIXML_NO_STL>
58-
$<$<BOOL:${PUGIXML_NO_EXCEPTIONS}>:PUGIXML_NO_EXCEPTIONS>)
59+
$<$<BOOL:${PUGIXML_NO_EXCEPTIONS}>:PUGIXML_NO_EXCEPTIONS>
60+
$<$<BOOL:${PUGIXML_STRING_VIEW}>:PUGIXML_STRING_VIEW>
61+
)
5962

6063
# This is used to backport a CMake 3.15 feature, but is also forwards compatible
6164
if (NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)

src/pugiconfig.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
// Uncomment this to enable long long support
5050
// #define PUGIXML_HAS_LONG_LONG
5151

52+
// Uncomment this to enable support for std::string_view (requires c++17 and for PUGIXML_NO_STL to not be set)
53+
// Note: In a future version of pugixml this macro will become obsolete.
54+
// Support will then be enabled automatically if the used C++ standard supports it.
55+
// #define PUGIXML_STRING_VIEW
56+
5257
#endif
5358

5459
/**

src/pugixml.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5441,6 +5441,15 @@ namespace pugi
54415441
return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, size);
54425442
}
54435443

5444+
#ifdef PUGIXML_HAS_STRING_VIEW
5445+
PUGI_IMPL_FN bool xml_attribute::set_name(string_view_t rhs)
5446+
{
5447+
if (!_attr) return false;
5448+
5449+
return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs.data(), rhs.size());
5450+
}
5451+
#endif
5452+
54445453
PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs)
54455454
{
54465455
if (!_attr) return false;
@@ -5455,6 +5464,15 @@ namespace pugi
54555464
return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, size);
54565465
}
54575466

5467+
#ifdef PUGIXML_HAS_STRING_VIEW
5468+
PUGI_IMPL_FN bool xml_attribute::set_value(string_view_t rhs)
5469+
{
5470+
if (!_attr) return false;
5471+
5472+
return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs.data(), rhs.size());
5473+
}
5474+
#endif
5475+
54585476
PUGI_IMPL_FN bool xml_attribute::set_value(int rhs)
54595477
{
54605478
if (!_attr) return false;
@@ -5848,6 +5866,18 @@ namespace pugi
58485866
return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, size);
58495867
}
58505868

5869+
#ifdef PUGIXML_HAS_STRING_VIEW
5870+
PUGI_IMPL_FN bool xml_node::set_name(string_view_t rhs)
5871+
{
5872+
xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;
5873+
5874+
if (type_ != node_element && type_ != node_pi && type_ != node_declaration)
5875+
return false;
5876+
5877+
return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs.data(), rhs.size());
5878+
}
5879+
#endif
5880+
58515881
PUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs)
58525882
{
58535883
xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;
@@ -5868,6 +5898,18 @@ namespace pugi
58685898
return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, size);
58695899
}
58705900

5901+
#ifdef PUGIXML_HAS_STRING_VIEW
5902+
PUGI_IMPL_FN bool xml_node::set_value(string_view_t rhs)
5903+
{
5904+
xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;
5905+
5906+
if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype)
5907+
return false;
5908+
5909+
return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs.data(), rhs.size());
5910+
}
5911+
#endif
5912+
58715913
PUGI_IMPL_FN xml_attribute xml_node::append_attribute(const char_t* name_)
58725914
{
58735915
if (!impl::allow_insert_attribute(type())) return xml_attribute();
@@ -6757,6 +6799,15 @@ namespace pugi
67576799
return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, size) : false;
67586800
}
67596801

6802+
#ifdef PUGIXML_HAS_STRING_VIEW
6803+
PUGI_IMPL_FN bool xml_text::set(string_view_t rhs)
6804+
{
6805+
xml_node_struct* dn = _data_new();
6806+
6807+
return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs.data(), rhs.size()) : false;
6808+
}
6809+
#endif
6810+
67606811
PUGI_IMPL_FN bool xml_text::set(int rhs)
67616812
{
67626813
xml_node_struct* dn = _data_new();

src/pugixml.hpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@
3838
# include <string>
3939
#endif
4040

41+
// Check if std::string_view is both requested and available
42+
#if defined(PUGIXML_STRING_VIEW) && !defined(PUGIXML_NO_STL)
43+
# if __cplusplus >= 201703L
44+
# define PUGIXML_HAS_STRING_VIEW
45+
# elif defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
46+
# define PUGIXML_HAS_STRING_VIEW
47+
# endif
48+
#endif
49+
50+
// Include string_view if appropriate
51+
#ifdef PUGIXML_HAS_STRING_VIEW
52+
# include <string_view>
53+
#endif
54+
4155
// Macro for deprecated features
4256
#ifndef PUGIXML_DEPRECATED
4357
# if defined(__GNUC__)
@@ -140,6 +154,11 @@ namespace pugi
140154
// String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE
141155
typedef std::basic_string<PUGIXML_CHAR> string_t;
142156
#endif
157+
158+
#ifdef PUGIXML_HAS_STRING_VIEW
159+
// String view type used for operations that can work with a length delimited string; depends on PUGIXML_WCHAR_MODE
160+
typedef std::basic_string_view<PUGIXML_CHAR> string_view_t;
161+
#endif
143162
}
144163

145164
// The PugiXML namespace
@@ -423,8 +442,14 @@ namespace pugi
423442
// Set attribute name/value (returns false if attribute is empty or there is not enough memory)
424443
bool set_name(const char_t* rhs);
425444
bool set_name(const char_t* rhs, size_t size);
445+
#ifdef PUGIXML_HAS_STRING_VIEW
446+
bool set_name(string_view_t rhs);
447+
#endif
426448
bool set_value(const char_t* rhs);
427449
bool set_value(const char_t* rhs, size_t size);
450+
#ifdef PUGIXML_HAS_STRING_VIEW
451+
bool set_value(string_view_t rhs);
452+
#endif
428453

429454
// Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
430455
bool set_value(int rhs);
@@ -559,8 +584,14 @@ namespace pugi
559584
// Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value)
560585
bool set_name(const char_t* rhs);
561586
bool set_name(const char_t* rhs, size_t size);
587+
#ifdef PUGIXML_HAS_STRING_VIEW
588+
bool set_name(string_view_t rhs);
589+
#endif
562590
bool set_value(const char_t* rhs);
563591
bool set_value(const char_t* rhs, size_t size);
592+
#ifdef PUGIXML_HAS_STRING_VIEW
593+
bool set_value(string_view_t rhs);
594+
#endif
564595

565596
// Add attribute with specified name. Returns added attribute, or empty attribute on errors.
566597
xml_attribute append_attribute(const char_t* name);
@@ -790,6 +821,9 @@ namespace pugi
790821
// Set text (returns false if object is empty or there is not enough memory)
791822
bool set(const char_t* rhs);
792823
bool set(const char_t* rhs, size_t size);
824+
#ifdef PUGIXML_HAS_STRING_VIEW
825+
bool set(string_view_t rhs);
826+
#endif
793827

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

tests/test_dom_modify.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,23 @@ TEST_XML(dom_attr_set_name_with_size, "<node attr='value' />")
5656
CHECK_NODE(doc, STR("<node n=\"value\"/>"));
5757
}
5858

59+
#ifdef PUGIXML_HAS_STRING_VIEW
60+
TEST_XML(dom_attr_set_name_with_string_view, "<node attr='value' />")
61+
{
62+
xml_attribute attr = doc.child(STR("node")).attribute(STR("attr"));
63+
64+
CHECK(attr.set_name(string_view_t()));
65+
CHECK(!xml_attribute().set_name(string_view_t()));
66+
67+
CHECK_NODE(doc, STR("<node :anonymous=\"value\"/>"));
68+
69+
CHECK(attr.set_name(string_view_t(STR("n1234"), 1)));
70+
CHECK(!xml_attribute().set_name(string_view_t(STR("nfail"), 1)));
71+
72+
CHECK_NODE(doc, STR("<node n=\"value\"/>"));
73+
}
74+
#endif
75+
5976
TEST_XML(dom_attr_set_value, "<node/>")
6077
{
6178
xml_node node = doc.child(STR("node"));
@@ -225,6 +242,25 @@ TEST_XML(dom_node_set_name_with_size, "<node>text</node>")
225242
CHECK_NODE(doc, STR("<n>text</n>"));
226243
}
227244

245+
#ifdef PUGIXML_HAS_STRING_VIEW
246+
TEST_XML(dom_node_set_name_with_string_view, "<node>text</node>")
247+
{
248+
xml_node node = doc.child(STR("node"));
249+
250+
CHECK(node.set_name(string_view_t()));
251+
CHECK(!node.first_child().set_name(string_view_t(STR("n42"), 1)));
252+
CHECK(!xml_node().set_name(string_view_t(STR("nanothername"), 1)));
253+
254+
CHECK_NODE(doc, STR("<:anonymous>text</:anonymous>"));
255+
256+
CHECK(node.set_name(string_view_t(STR("nlongname"), 1)));
257+
CHECK(!doc.child(STR("node")).first_child().set_name(string_view_t(STR("n42"), 1)));
258+
CHECK(!xml_node().set_name(string_view_t(STR("nanothername"), 1)));
259+
260+
CHECK_NODE(doc, STR("<n>text</n>"));
261+
}
262+
#endif
263+
228264
TEST_XML(dom_node_set_value, "<node>text</node>")
229265
{
230266
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>")
252288
CHECK_NODE(doc, STR("<node>no text</node>"));
253289
}
254290

291+
#ifdef PUGIXML_HAS_STRING_VIEW
292+
TEST_XML(dom_node_set_value_partially_with_string_view, "<node>text</node>")
293+
{
294+
CHECK(doc.child(STR("node")).first_child().set_value(string_view_t(STR("no text"), 2)));
295+
CHECK(!doc.child(STR("node")).set_value(string_view_t(STR("no text"), 2)));
296+
CHECK(!xml_node().set_value(string_view_t(STR("no text"), 2)));
297+
298+
CHECK_NODE(doc, STR("<node>no</node>"));
299+
}
300+
301+
TEST_XML(dom_node_set_value_with_string_view, "<node>text</node>")
302+
{
303+
CHECK(doc.child(STR("node")).first_child().set_value(string_view_t(STR("no text"), 7)));
304+
CHECK(!doc.child(STR("node")).set_value(string_view_t(STR("no text"), 7)));
305+
CHECK(!xml_node().set_value(string_view_t(STR("no text"), 7)));
306+
307+
CHECK_NODE(doc, STR("<node>no text</node>"));
308+
}
309+
#endif
310+
255311
TEST_XML(dom_node_set_value_allocated, "<node>text</node>")
256312
{
257313
CHECK(doc.child(STR("node")).first_child().set_value(STR("no text")));

0 commit comments

Comments
 (0)