added insert, emplace, push_back etc. compatibility with node_views

In service of satisfying #49.
This commit is contained in:
Mark Gillard 2020-07-28 01:04:52 +03:00
parent 17d1876529
commit 2efb15bf9e
47 changed files with 417 additions and 118 deletions

View File

@ -350,7 +350,6 @@ PREDEFINED = \
"TOML_TRIVIAL_ABI=" \
"TOML_EMPTY_BASES=" \
"TOML_INTERFACE" \
"TOML_SIMPLE_STATIC_ASSERT_MESSAGES=1" \
"TOML_INTERNAL_LINKAGE=static"
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = NO

View File

@ -47,7 +47,7 @@ article div > section > section
pre, code, .tpp-enable-if > a
{
font-family: 'Consolas', 'Source Code Pro', monospace, monospace, monospace;
font-family: 'Consolas', 'Source Code Pro', monospace;
}
a.tpp-external
@ -142,6 +142,7 @@ pre.m-code + pre.m-console span
.m-doc-details div table.m-table.m-fullwidth.m-flat tbody tr td strong em
{
color: #a5c9ea;
font-style: normal;
}
/* comments */
@ -316,9 +317,10 @@ pre > p.godbolt
{
float: right;
}
@media only screen and ((max-width: 575px) or ((hover: none) and (pointer: coarse)))
@media only screen and ((max-width: 575px) or (hover: none) or (pointer: coarse) or (pointer: none))
{
.godbolt {
.godbolt
{
display: none;
}
}

View File

@ -129,7 +129,10 @@ int main(int argc, char** argv)
toml::node* new_node{};
if (auto arr = tree.back()->as_array())
new_node = &arr->push_back(std::forward<decltype(obj)>(obj));
{
arr->push_back(std::forward<decltype(obj)>(obj));
new_node = &arr->back();
}
else
new_node = &(*tree.back()->ref<toml::table>().insert_or_assign(
rand_string(rand<size_t>(1u, 4u), '-'),

View File

@ -15,7 +15,7 @@ TOML_IMPL_NAMESPACE_START
class TOML_TRIVIAL_ABI array_iterator final
{
private:
friend class ::toml::array;
friend class TOML_NAMESPACE::array;
using raw_mutable_iterator = std::vector<std::unique_ptr<node>>::iterator;
using raw_const_iterator = std::vector<std::unique_ptr<node>>::const_iterator;
@ -180,6 +180,7 @@ TOML_IMPL_NAMESPACE_START
{
using type = unwrap_node<remove_cvref_t<T>>;
static_assert(!std::is_same_v<type, node>);
static_assert(!is_node_view<type>);
if constexpr (is_one_of<type, array, table>)
{
@ -211,12 +212,17 @@ TOML_IMPL_NAMESPACE_START
template <typename T>
[[nodiscard]]
TOML_ATTR(returns_nonnull)
auto* make_node(T&& val) noexcept
{
using type = unwrap_node<remove_cvref_t<T>>;
if constexpr (std::is_same_v<type, node>)
if constexpr (std::is_same_v<type, node> || is_node_view<type>)
{
if constexpr (is_node_view<type>)
{
if (!val)
return static_cast<toml::node*>(nullptr);
}
return std::forward<T>(val).visit([](auto&& concrete) noexcept
{
return static_cast<toml::node*>(make_node_specialized(std::forward<decltype(concrete)>(concrete)));
@ -228,7 +234,6 @@ TOML_IMPL_NAMESPACE_START
template <typename T>
[[nodiscard]]
TOML_ATTR(returns_nonnull)
auto* make_node(inserter<T>&& val) noexcept
{
return make_node(std::move(val.value));
@ -304,6 +309,17 @@ TOML_NAMESPACE_START
void preinsertion_resize(size_t idx, size_t count) noexcept;
template <typename T>
void emplace_back_if_not_empty_view(T&& val) noexcept
{
if constexpr (impl::is_node_view<T>)
{
if (!val)
return;
}
elements.emplace_back(impl::make_node(std::forward<T>(val)));
}
public:
using value_type = node;
@ -376,11 +392,11 @@ TOML_NAMESPACE_START
explicit array(ElemType&& val, ElemTypes&&... vals)
{
elements.reserve(sizeof...(ElemTypes) + 1_sz);
elements.emplace_back(impl::make_node(std::forward<ElemType>(val)));
emplace_back_if_not_empty_view(std::forward<ElemType>(val));
if constexpr (sizeof...(ElemTypes) > 0)
{
(
elements.emplace_back(impl::make_node(std::forward<ElemTypes>(vals))),
emplace_back_if_not_empty_view(std::forward<ElemTypes>(vals)),
...
);
}
@ -529,14 +545,27 @@ TOML_NAMESPACE_START
/// [ 1, 'two', 3, [ 4, 5 ] ]
/// \eout
///
/// \tparam ElemType One of the TOML node or value types (or a type promotable to one).
/// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type
/// (or a type promotable to one).
/// \param pos The insertion position.
/// \param val The node or value being inserted.
///
/// \returns An iterator to the newly-inserted element.
/// \returns <strong><em>Valid input:</em></strong><br>
/// An iterator to the newly-inserted element.
/// <br><br>
/// <strong><em>`val` is an empty toml::node_view:</em></strong><br>
/// end()
///
/// \attention The return value will always be `end()` if the input value was an empty toml::node_view,
/// because no insertion can take place. This is the only circumstance in which this can occur.
template <typename ElemType>
iterator insert(const_iterator pos, ElemType&& val) noexcept
{
if constexpr (impl::is_node_view<ElemType>)
{
if (!val)
return end();
}
return { elements.emplace(pos.raw_, impl::make_node(std::forward<ElemType>(val))) };
}
@ -562,15 +591,31 @@ TOML_NAMESPACE_START
/// ]
/// \eout
///
/// \tparam ElemType One of the TOML node or value types (or a type promotable to one).
/// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type
/// (or a type promotable to one).
/// \param pos The insertion position.
/// \param count The number of times the node or value should be inserted.
/// \param val The node or value being inserted.
///
/// \returns An iterator to the first newly-inserted element (or a copy of `pos` if count was 0).
/// \returns <strong><em>Valid input:</em></strong><br>
/// An iterator to the newly-inserted element.
/// <br><br>
/// <strong><em>`count == 0`:</em></strong><br>
/// A copy of pos
/// <br><br>
/// <strong><em>`val` is an empty toml::node_view:</em></strong><br>
/// end()
///
/// \attention The return value will always be `end()` if the input value was an empty toml::node_view,
/// because no insertion can take place. This is the only circumstance in which this can occur.
template <typename ElemType>
iterator insert(const_iterator pos, size_t count, ElemType&& val) noexcept
{
if constexpr (impl::is_node_view<ElemType>)
{
if (!val)
return end();
}
switch (count)
{
case 0: return { elements.begin() + (pos.raw_ - elements.cbegin()) };
@ -597,50 +642,70 @@ TOML_NAMESPACE_START
/// \param first Iterator to the first node or value being inserted.
/// \param last Iterator to the one-past-the-last node or value being inserted.
///
/// \returns An iterator to the first newly-inserted element (or a copy of `pos` if `first` >= `last`).
/// \returns <strong><em>Valid input:</em></strong><br>
/// An iterator to the first newly-inserted element.
/// <br><br>
/// <strong><em>`first >= last`:</em></strong><br>
/// A copy of pos
/// <br><br>
/// <strong><em>All objects in the range were empty toml::node_views:</em></strong><br>
/// A copy of pos
template <typename Iter>
iterator insert(const_iterator pos, Iter first, Iter last) noexcept
{
const auto count = std::distance(first, last);
if (count <= 0)
const auto distance = std::distance(first, last);
if (distance <= 0)
return { elements.begin() + (pos.raw_ - elements.cbegin()) };
else if (count == 1)
return insert(pos, *first);
else
{
auto count = distance;
using deref_type = decltype(*first);
if constexpr (impl::is_node_view<deref_type>)
{
for (auto it = first; it != last; it++)
if (!(*it))
count--;
if (!count)
return { elements.begin() + (pos.raw_ - elements.cbegin()) };
}
const auto start_idx = static_cast<size_t>(pos.raw_ - elements.cbegin());
preinsertion_resize(start_idx, static_cast<size_t>(count));
size_t i = start_idx;
for (auto it = first; it != last; it++)
elements[i++].reset(impl::make_node(*it));
{
if constexpr (impl::is_node_view<deref_type>)
{
if (!(*it))
continue;
}
if constexpr (std::is_rvalue_reference_v<deref_type>)
elements[i++].reset(impl::make_node(std::move(*it)));
else
elements[i++].reset(impl::make_node(*it));
}
return { elements.begin() + static_cast<ptrdiff_t>(start_idx) };
}
}
/// \brief Inserts a range of elements into the array at a specific position.
///
/// \tparam ElemType One of the TOML node or value types (or a type promotable to one).
/// \tparam ElemType toml::node_view, toml::table, toml::array, or a native TOML value type
/// (or a type promotable to one).
/// \param pos The insertion position.
/// \param ilist An initializer list containing the values to be inserted.
///
/// \returns An iterator to the first newly-inserted element (or a copy of `pos` if `ilist` was empty).
/// \returns <strong><em>Valid input:</em></strong><br>
/// An iterator to the first newly-inserted element.
/// <br><br>
/// <strong><em>`ilist.size() == 0`:</em></strong><br>
/// A copy of pos
/// <br><br>
/// <strong><em>All objects in the list were empty toml::node_views:</em></strong><br>
/// A copy of pos
template <typename ElemType>
iterator insert(const_iterator pos, std::initializer_list<ElemType> ilist) noexcept
{
switch (ilist.size())
{
case 0: return { elements.begin() + (pos.raw_ - elements.cbegin()) };
case 1: return insert(pos, *ilist.begin());
default:
{
const auto start_idx = static_cast<size_t>(pos.raw_ - elements.cbegin());
preinsertion_resize(start_idx, ilist.size());
size_t i = start_idx;
for (auto& val : ilist)
elements[i++].reset(impl::make_node(val));
return { elements.begin() + static_cast<ptrdiff_t>(start_idx) };
}
}
return insert(pos, ilist.begin(), ilist.end());
}
/// \brief Emplaces a new element at a specific position in the array.
@ -658,7 +723,7 @@ TOML_NAMESPACE_START
/// [ 1, 'drill', 2 ]
/// \eout
///
/// \tparam ElemType One of the TOML node or value types.
/// \tparam ElemType toml::table, toml::array, or any native TOML value type.
/// \tparam Args Value constructor argument types.
/// \param pos The insertion position.
/// \param args Arguments to forward to the value's constructor.
@ -745,13 +810,19 @@ TOML_NAMESPACE_START
/// [ 1, 2 ]
/// \eout
///
/// \tparam ElemType One of the TOML node or value types (or a type promotable to one).
/// \tparam ElemType toml::node, toml::table, toml::array, or a native TOML value type
/// (or a type promotable to one).
///
/// \param new_size The number of elements the array will have after resizing.
/// \param default_init_val The node or value used to initialize new elements if the array needs to grow.
template <typename ElemType>
void resize(size_t new_size, ElemType&& default_init_val) noexcept
{
static_assert(
!impl::is_node_view<ElemType>,
"The default element type argument to toml::array::resize may not be toml::node_view."
);
if (!new_size)
elements.clear();
else if (new_size < elements.size())
@ -800,16 +871,15 @@ TOML_NAMESPACE_START
/// [ 1, 2, 3, 4.0, [ 5, 'six' ] ]
/// \eout
///
/// \tparam ElemType One of the TOML node or value types (or a type promotable to one).
/// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type
/// \param val The node or value being added.
///
/// \returns A reference to the newly-constructed element.
/// \attention No insertion takes place if the input value is an empty toml::node_view.
/// This is the only circumstance in which this can occur.
template <typename ElemType>
decltype(auto) push_back(ElemType&& val) noexcept
void push_back(ElemType&& val) noexcept
{
auto nde = impl::make_node(std::forward<ElemType>(val));
elements.emplace_back(nde);
return *nde;
emplace_back_if_not_empty_view(std::forward<ElemType>(val));
}
/// \brief Emplaces a new element at the end of the array.
@ -825,7 +895,7 @@ TOML_NAMESPACE_START
/// [ 1, 2, [ 3, 'four' ] ]
/// \eout
///
/// \tparam ElemType One of the TOML node or value types.
/// \tparam ElemType toml::table, toml::array, or a native TOML value type
/// \tparam Args Value constructor argument types.
/// \param args Arguments to forward to the value's constructor.
///
@ -895,7 +965,7 @@ TOML_NAMESPACE_START
/// element [0] is an integer with value 42
/// \eout
///
/// \tparam ElemType The element's type.
/// \tparam ElemType toml::table, toml::array, or a native TOML value type
/// \param index The element's index.
///
/// \returns A pointer to the selected element if it existed and was of the specified type, or nullptr.
@ -910,7 +980,7 @@ TOML_NAMESPACE_START
/// \brief Gets the element at a specific index if it is a particular type (const overload).
///
/// \tparam ElemType The element's type.
/// \tparam ElemType toml::table, toml::array, or a native TOML value type
/// \param index The element's index.
///
/// \returns A pointer to the selected element if it existed and was of the specified type, or nullptr.

View File

@ -549,6 +549,9 @@ TOML_IMPL_NAMESPACE_START
template <> struct node_type_getter<array> { static constexpr auto value = node_type::array; };
template <typename T>
inline constexpr node_type node_type_of = node_type_getter<unwrap_node<remove_cvref_t<T>>>::value;
template <typename T>
inline constexpr bool is_node_view = is_one_of<impl::remove_cvref_t<T>, node_view<node>, node_view<const node>>;
}
TOML_IMPL_NAMESPACE_END

View File

@ -10,7 +10,7 @@ TOML_PUSH_WARNINGS
TOML_DISABLE_PADDING_WARNINGS
TOML_DISABLE_MISC_WARNINGS
#if TOML_SIMPLE_STATIC_ASSERT_MESSAGES
#if defined(doxygen) || TOML_SIMPLE_STATIC_ASSERT_MESSAGES
#define TOML_SA_NEWLINE " "
#define TOML_SA_LIST_SEP ", "

View File

@ -63,8 +63,8 @@ TOML_NAMESPACE_START
using viewed_type = ViewedType;
private:
friend class toml::table;
template <typename T> friend class toml::node_view;
friend class TOML_NAMESPACE::table;
template <typename T> friend class TOML_NAMESPACE::node_view;
mutable viewed_type* node_ = nullptr;
@ -83,6 +83,19 @@ TOML_NAMESPACE_START
TOML_NODISCARD_CTOR
node_view() noexcept = default;
///// \brief Copy constructor.
TOML_NODISCARD_CTOR
node_view(const node_view&) noexcept = default;
///// \brief Copy-assignment operator.
TOML_NODISCARD_CTOR
node_view& operator= (const node_view&) noexcept = default;
///// \brief Move constructor.
node_view(node_view&&) noexcept = default;
node_view& operator= (node_view&&) noexcept = delete;
/// \brief Returns true if the view references a node.
[[nodiscard]] explicit operator bool() const noexcept { return node_ != nullptr; }
/// \brief Returns the node that's being referenced by the view.

View File

@ -25,7 +25,7 @@ TOML_IMPL_NAMESPACE_START
class table_iterator final
{
private:
friend class ::toml::table;
friend class TOML_NAMESPACE::table;
using proxy_type = table_proxy_pair<IsConst>;
using raw_mutable_iterator = string_map<std::unique_ptr<node>>::iterator;
@ -278,7 +278,6 @@ TOML_NAMESPACE_START
: table{ arr, N }
{}
/// \brief Always returns `node_type::table` for table nodes.
[[nodiscard]] node_type type() const noexcept override;
/// \brief Always returns `true` for table nodes.
@ -453,13 +452,22 @@ TOML_NAMESPACE_START
/// \eout
///
/// \tparam KeyType std::string (or a type convertible to it).
/// \tparam ValueType One of the TOML ndoe or value types (or a type promotable to one).
/// \tparam ValueType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type
/// (or a type promotable to one).
/// \param key The key at which to insert the new value.
/// \param val The new value to insert.
///
/// \returns A std::pair containing:
/// - An iterator to the insertion position (or the position of the value that prevented insertion)
/// - A boolean indicating if the insertion was successful.
/// \returns <strong><em>Valid input:</em></strong><br>
/// <ul>
/// <li>An iterator to the insertion position (or the position of the value that prevented insertion)
/// <li>A boolean indicating if the insertion was successful.
/// </ul>
/// <strong><em>`val` is an empty toml::node_view:</em></strong><br>
/// `{ end(), false }`
///
/// \attention The return value will always be `{ end(), false }` if the input value was an
/// empty toml::node_view, because no insertion can take place. This is the only circumstance
/// in which this can occur.
template <typename KeyType, typename ValueType, typename = std::enable_if_t<
std::is_convertible_v<KeyType&&, std::string_view>
|| impl::is_wide_string<KeyType>
@ -471,6 +479,12 @@ TOML_NAMESPACE_START
"Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
);
if constexpr (impl::is_node_view<ValueType>)
{
if (!val)
return { end(), false };
}
if constexpr (impl::is_wide_string<KeyType>)
{
#if TOML_WINDOWS_COMPAT
@ -485,9 +499,9 @@ TOML_NAMESPACE_START
if (ipos == map.end() || ipos->first != key)
{
ipos = map.emplace_hint(ipos, std::forward<KeyType>(key), impl::make_node(std::forward<ValueType>(val)));
return { ipos, true };
return { iterator{ ipos }, true };
}
return { ipos, false };
return { iterator{ ipos }, false };
}
}
@ -583,13 +597,22 @@ TOML_NAMESPACE_START
/// \eout
///
/// \tparam KeyType std::string (or a type convertible to it).
/// \tparam ValueType One of the TOML node or value types (or a type promotable to one).
/// \tparam ValueType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type
/// (or a type promotable to one).
/// \param key The key at which to insert or assign the value.
/// \param val The value to insert/assign.
///
/// \returns A std::pair containing:
/// - An iterator to the value's position
/// - A boolean containing `true` if the value was inserted, `false` if it was assigned.
/// \returns <strong><em>Valid input:</em></strong><br>
/// <ul>
/// <li>An iterator to the value's position
/// <li>`true` if the value was inserted, `false` if it was assigned.
/// </ul>
/// <strong><em>`val` is an empty toml::node_view:</em></strong><br>
/// `{ end(), false }`
///
/// \attention The return value will always be `{ end(), false }` if the input value was
/// an empty toml::node_view, because no insertion or assignment can take place.
/// This is the only circumstance in which this can occur.
template <typename KeyType, typename ValueType>
std::pair<iterator, bool> insert_or_assign(KeyType&& key, ValueType&& val) noexcept
{
@ -598,6 +621,12 @@ TOML_NAMESPACE_START
"Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
);
if constexpr (impl::is_node_view<ValueType>)
{
if (!val)
return { end(), false };
}
if constexpr (impl::is_wide_string<KeyType>)
{
#if TOML_WINDOWS_COMPAT
@ -612,12 +641,12 @@ TOML_NAMESPACE_START
if (ipos == map.end() || ipos->first != key)
{
ipos = map.emplace_hint(ipos, std::forward<KeyType>(key), impl::make_node(std::forward<ValueType>(val)));
return { ipos, true };
return { iterator{ ipos }, true };
}
else
{
(*ipos).second.reset(impl::make_node(std::forward<ValueType>(val)));
return { ipos, false };
return { iterator{ ipos }, false };
}
}
}
@ -649,13 +678,13 @@ TOML_NAMESPACE_START
/// { a = 1, b = 2, c = 3, d = "drill" }
/// \eout
///
/// \tparam ValueType One of the TOML node or value types.
/// \tparam ValueType toml::table, toml::array, or any native TOML value type.
/// \tparam KeyType std::string (or a type convertible to it).
/// \tparam ValueArgs Value constructor argument types.
/// \param key The key at which to emplace the new value.
/// \param args Arguments to forward to the value's constructor.
///
/// \returns A std::pair containing:
/// \returns A std::pair containing: <br>
/// - An iterator to the emplacement position (or the position of the value that prevented emplacement)
/// - A boolean indicating if the emplacement was successful.
///
@ -694,9 +723,9 @@ TOML_NAMESPACE_START
std::forward<KeyType>(key),
new impl::wrap_node<type>{ std::forward<ValueArgs>(args)... }
);
return { ipos, true };
return { iterator{ ipos }, true };
}
return { ipos, false };
return { iterator{ ipos }, false };
}
}

View File

@ -70,6 +70,8 @@ TOML_NAMESPACE_START
{
for (size_t i = 0; i < count; i++)
{
if (!pairs[i].value) // empty node_views
continue;
map.insert_or_assign(
std::move(pairs[i].key),
std::move(pairs[i].value)

View File

@ -120,6 +120,7 @@ def main():
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -256,7 +256,8 @@ TEST_CASE("arrays - insertion and erasure")
// push_back(ElemType&& val) noexcept
{
decltype(auto) val = arr.push_back("test"sv);
arr.push_back("test"sv);
auto& val = *arr.back().as_string();
CHECK(arr.size() == 6_sz);
REQUIRE(arr.get_as<std::string>(5_sz));
CHECK(*arr.get_as<std::string>(5_sz) == "test"sv);

View File

@ -21,6 +21,7 @@ test_sources = [
'manipulating_values.cpp',
'unicode.cpp',
'unicode_generated.cpp',
'user_feedback.cpp',
'windows_compat.cpp'
]
@ -300,12 +301,6 @@ foreach cpp20 : cpp20_modes
name = name + '_tlopt'
endif
if compiler_supports_float16
if compiler.get_id() == 'gcc'
args += '-mfp16-format=ieee'
endif
endif
executables += [[
name,
executable(

View File

@ -84,12 +84,12 @@
#ifndef SHOULD_HAVE_FLOAT16
#define SHOULD_HAVE_FLOAT16 0
#endif
#ifndef SHOULD_HAVE_INT128
#define SHOULD_HAVE_INT128 0
#endif
#ifndef SHOULD_HAVE_FLOAT128
#define SHOULD_HAVE_FLOAT128 0
#endif
#ifndef SHOULD_HAVE_INT128
#define SHOULD_HAVE_INT128 0
#endif
#ifndef SHOULD_HAVE_EXCEPTIONS
#define SHOULD_HAVE_EXCEPTIONS 1
#endif

88
tests/user_feedback.cpp Normal file
View File

@ -0,0 +1,88 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
// this file is about testing user misc. repros submitted via github issues, et cetera.
TEST_CASE("feedback - github/issues/49")
{
// see: https://github.com/marzer/tomlplusplus/issues/49#issuecomment-664428571
{
toml::table t1;
t1.insert_or_assign("bar1", toml::array{ 1, 2, 3 });
CHECK(t1 == toml::table{{
{ "bar1"sv, toml::array{ 1, 2, 3 } }
}});
t1.insert_or_assign("foo1", *t1.get("bar1"));
CHECK(t1 == toml::table{{
{ "bar1"sv, toml::array{ 1, 2, 3 } },
{ "foo1"sv, toml::array{ 1, 2, 3 } }
}});
//t1["foo1"] = t1["bar1"]; // does nothing, should this fail to compile?
// - yes -
//*t1["foo1"].node() = *t1["bar1"].node(); // compile failure; copying node_view would be a bad thing
// - correct; copying toml::node directly like that is impossible b.c. it's an abstract base class-
toml::array* array1 = t1["foo1"].node()->as_array();
array1->push_back(4);
CHECK(t1 == toml::table{{
{ "bar1"sv, toml::array{ 1, 2, 3 } },
{ "foo1"sv, toml::array{ 1, 2, 3, 4 } }
}});
t1.insert_or_assign("foo3", t1["foo1"]);
CHECK(t1 == toml::table{{
{ "bar1"sv, toml::array{ 1, 2, 3 } },
{ "foo1"sv, toml::array{ 1, 2, 3, 4 } },
{ "foo3"sv, toml::array{ 1, 2, 3, 4 } }
}});
t1.insert_or_assign("foo2", *t1["foo1"].node());
CHECK(t1 == toml::table{{
{ "bar1"sv, toml::array{ 1, 2, 3 } },
{ "foo1"sv, toml::array{ 1, 2, 3, 4 } },
{ "foo2"sv, toml::array{ 1, 2, 3, 4 } },
{ "foo3"sv, toml::array{ 1, 2, 3, 4 } }
}});
toml::array* array2 = t1["foo2"].node()->as_array();
array2->push_back("wrench");
CHECK(t1 == toml::table{{
{ "bar1"sv, toml::array{ 1, 2, 3 } },
{ "foo1"sv, toml::array{ 1, 2, 3, 4 } },
{ "foo2"sv, toml::array{ 1, 2, 3, 4, "wrench" } },
{ "foo3"sv, toml::array{ 1, 2, 3, 4 } }
}});
toml::table t2 = t1;
CHECK(t2 == t1);
CHECK(&t2 != &t1);
//t2.emplace("bar", toml::array{6, 7}); // fails to compile? not sure what I did wrong
// - it should be this: -
t2.emplace<toml::array>("bar", 6, 7);
CHECK(t2 == toml::table{{
{ "bar"sv, toml::array{ 6, 7 } },
{ "bar1"sv, toml::array{ 1, 2, 3 } },
{ "foo1"sv, toml::array{ 1, 2, 3, 4 } },
{ "foo2"sv, toml::array{ 1, 2, 3, 4, "wrench" } },
{ "foo3"sv, toml::array{ 1, 2, 3, 4 } }
}});
t2.insert_or_assign("bar2", toml::array{ 6, 7 });
CHECK(t2 == toml::table{{
{ "bar"sv, toml::array{ 6, 7 } },
{ "bar1"sv, toml::array{ 1, 2, 3 } },
{ "bar2"sv, toml::array{ 6, 7 } },
{ "foo1"sv, toml::array{ 1, 2, 3, 4 } },
{ "foo2"sv, toml::array{ 1, 2, 3, 4, "wrench" } },
{ "foo3"sv, toml::array{ 1, 2, 3, 4 } }
}});
}
}

139
toml.hpp
View File

@ -1067,6 +1067,9 @@ TOML_IMPL_NAMESPACE_START
template <> struct node_type_getter<array> { static constexpr auto value = node_type::array; };
template <typename T>
inline constexpr node_type node_type_of = node_type_getter<unwrap_node<remove_cvref_t<T>>>::value;
template <typename T>
inline constexpr bool is_node_view = is_one_of<impl::remove_cvref_t<T>, node_view<node>, node_view<const node>>;
}
TOML_IMPL_NAMESPACE_END
@ -2026,7 +2029,7 @@ TOML_PUSH_WARNINGS
TOML_DISABLE_PADDING_WARNINGS
TOML_DISABLE_MISC_WARNINGS
#if TOML_SIMPLE_STATIC_ASSERT_MESSAGES
#if defined(doxygen) || TOML_SIMPLE_STATIC_ASSERT_MESSAGES
#define TOML_SA_NEWLINE " "
#define TOML_SA_LIST_SEP ", "
@ -3235,7 +3238,7 @@ TOML_IMPL_NAMESPACE_START
class TOML_TRIVIAL_ABI array_iterator final
{
private:
friend class ::toml::array;
friend class TOML_NAMESPACE::array;
using raw_mutable_iterator = std::vector<std::unique_ptr<node>>::iterator;
using raw_const_iterator = std::vector<std::unique_ptr<node>>::const_iterator;
@ -3400,6 +3403,7 @@ TOML_IMPL_NAMESPACE_START
{
using type = unwrap_node<remove_cvref_t<T>>;
static_assert(!std::is_same_v<type, node>);
static_assert(!is_node_view<type>);
if constexpr (is_one_of<type, array, table>)
{
@ -3431,12 +3435,17 @@ TOML_IMPL_NAMESPACE_START
template <typename T>
[[nodiscard]]
TOML_ATTR(returns_nonnull)
auto* make_node(T&& val) noexcept
{
using type = unwrap_node<remove_cvref_t<T>>;
if constexpr (std::is_same_v<type, node>)
if constexpr (std::is_same_v<type, node> || is_node_view<type>)
{
if constexpr (is_node_view<type>)
{
if (!val)
return static_cast<toml::node*>(nullptr);
}
return std::forward<T>(val).visit([](auto&& concrete) noexcept
{
return static_cast<toml::node*>(make_node_specialized(std::forward<decltype(concrete)>(concrete)));
@ -3448,7 +3457,6 @@ TOML_IMPL_NAMESPACE_START
template <typename T>
[[nodiscard]]
TOML_ATTR(returns_nonnull)
auto* make_node(inserter<T>&& val) noexcept
{
return make_node(std::move(val.value));
@ -3470,6 +3478,17 @@ TOML_NAMESPACE_START
void preinsertion_resize(size_t idx, size_t count) noexcept;
template <typename T>
void emplace_back_if_not_empty_view(T&& val) noexcept
{
if constexpr (impl::is_node_view<T>)
{
if (!val)
return;
}
elements.emplace_back(impl::make_node(std::forward<T>(val)));
}
public:
using value_type = node;
@ -3499,11 +3518,11 @@ TOML_NAMESPACE_START
explicit array(ElemType&& val, ElemTypes&&... vals)
{
elements.reserve(sizeof...(ElemTypes) + 1_sz);
elements.emplace_back(impl::make_node(std::forward<ElemType>(val)));
emplace_back_if_not_empty_view(std::forward<ElemType>(val));
if constexpr (sizeof...(ElemTypes) > 0)
{
(
elements.emplace_back(impl::make_node(std::forward<ElemTypes>(vals))),
emplace_back_if_not_empty_view(std::forward<ElemTypes>(vals)),
...
);
}
@ -3560,12 +3579,22 @@ TOML_NAMESPACE_START
template <typename ElemType>
iterator insert(const_iterator pos, ElemType&& val) noexcept
{
if constexpr (impl::is_node_view<ElemType>)
{
if (!val)
return end();
}
return { elements.emplace(pos.raw_, impl::make_node(std::forward<ElemType>(val))) };
}
template <typename ElemType>
iterator insert(const_iterator pos, size_t count, ElemType&& val) noexcept
{
if constexpr (impl::is_node_view<ElemType>)
{
if (!val)
return end();
}
switch (count)
{
case 0: return { elements.begin() + (pos.raw_ - elements.cbegin()) };
@ -3587,18 +3616,36 @@ TOML_NAMESPACE_START
template <typename Iter>
iterator insert(const_iterator pos, Iter first, Iter last) noexcept
{
const auto count = std::distance(first, last);
if (count <= 0)
const auto distance = std::distance(first, last);
if (distance <= 0)
return { elements.begin() + (pos.raw_ - elements.cbegin()) };
else if (count == 1)
return insert(pos, *first);
else
{
auto count = distance;
using deref_type = decltype(*first);
if constexpr (impl::is_node_view<deref_type>)
{
for (auto it = first; it != last; it++)
if (!(*it))
count--;
if (!count)
return { elements.begin() + (pos.raw_ - elements.cbegin()) };
}
const auto start_idx = static_cast<size_t>(pos.raw_ - elements.cbegin());
preinsertion_resize(start_idx, static_cast<size_t>(count));
size_t i = start_idx;
for (auto it = first; it != last; it++)
elements[i++].reset(impl::make_node(*it));
{
if constexpr (impl::is_node_view<deref_type>)
{
if (!(*it))
continue;
}
if constexpr (std::is_rvalue_reference_v<deref_type>)
elements[i++].reset(impl::make_node(std::move(*it)));
else
elements[i++].reset(impl::make_node(*it));
}
return { elements.begin() + static_cast<ptrdiff_t>(start_idx) };
}
}
@ -3606,20 +3653,7 @@ TOML_NAMESPACE_START
template <typename ElemType>
iterator insert(const_iterator pos, std::initializer_list<ElemType> ilist) noexcept
{
switch (ilist.size())
{
case 0: return { elements.begin() + (pos.raw_ - elements.cbegin()) };
case 1: return insert(pos, *ilist.begin());
default:
{
const auto start_idx = static_cast<size_t>(pos.raw_ - elements.cbegin());
preinsertion_resize(start_idx, ilist.size());
size_t i = start_idx;
for (auto& val : ilist)
elements[i++].reset(impl::make_node(val));
return { elements.begin() + static_cast<ptrdiff_t>(start_idx) };
}
}
return insert(pos, ilist.begin(), ilist.end());
}
template <typename ElemType, typename... Args>
@ -3641,6 +3675,11 @@ TOML_NAMESPACE_START
template <typename ElemType>
void resize(size_t new_size, ElemType&& default_init_val) noexcept
{
static_assert(
!impl::is_node_view<ElemType>,
"The default element type argument to toml::array::resize may not be toml::node_view."
);
if (!new_size)
elements.clear();
else if (new_size < elements.size())
@ -3652,11 +3691,9 @@ TOML_NAMESPACE_START
void truncate(size_t new_size);
template <typename ElemType>
decltype(auto) push_back(ElemType&& val) noexcept
void push_back(ElemType&& val) noexcept
{
auto nde = impl::make_node(std::forward<ElemType>(val));
elements.emplace_back(nde);
return *nde;
emplace_back_if_not_empty_view(std::forward<ElemType>(val));
}
template <typename ElemType, typename... Args>
@ -3786,7 +3823,7 @@ TOML_IMPL_NAMESPACE_START
class table_iterator final
{
private:
friend class ::toml::table;
friend class TOML_NAMESPACE::table;
using proxy_type = table_proxy_pair<IsConst>;
using raw_mutable_iterator = string_map<std::unique_ptr<node>>::iterator;
@ -4031,6 +4068,12 @@ TOML_NAMESPACE_START
"Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
);
if constexpr (impl::is_node_view<ValueType>)
{
if (!val)
return { end(), false };
}
if constexpr (impl::is_wide_string<KeyType>)
{
#if TOML_WINDOWS_COMPAT
@ -4045,9 +4088,9 @@ TOML_NAMESPACE_START
if (ipos == map.end() || ipos->first != key)
{
ipos = map.emplace_hint(ipos, std::forward<KeyType>(key), impl::make_node(std::forward<ValueType>(val)));
return { ipos, true };
return { iterator{ ipos }, true };
}
return { ipos, false };
return { iterator{ ipos }, false };
}
}
@ -4076,6 +4119,12 @@ TOML_NAMESPACE_START
"Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled."
);
if constexpr (impl::is_node_view<ValueType>)
{
if (!val)
return { end(), false };
}
if constexpr (impl::is_wide_string<KeyType>)
{
#if TOML_WINDOWS_COMPAT
@ -4090,12 +4139,12 @@ TOML_NAMESPACE_START
if (ipos == map.end() || ipos->first != key)
{
ipos = map.emplace_hint(ipos, std::forward<KeyType>(key), impl::make_node(std::forward<ValueType>(val)));
return { ipos, true };
return { iterator{ ipos }, true };
}
else
{
(*ipos).second.reset(impl::make_node(std::forward<ValueType>(val)));
return { ipos, false };
return { iterator{ ipos }, false };
}
}
}
@ -4134,9 +4183,9 @@ TOML_NAMESPACE_START
std::forward<KeyType>(key),
new impl::wrap_node<type>{ std::forward<ValueArgs>(args)... }
);
return { ipos, true };
return { iterator{ ipos }, true };
}
return { ipos, false };
return { iterator{ ipos }, false };
}
}
@ -4274,8 +4323,8 @@ TOML_NAMESPACE_START
using viewed_type = ViewedType;
private:
friend class toml::table;
template <typename T> friend class toml::node_view;
friend class TOML_NAMESPACE::table;
template <typename T> friend class TOML_NAMESPACE::node_view;
mutable viewed_type* node_ = nullptr;
@ -4292,6 +4341,16 @@ TOML_NAMESPACE_START
TOML_NODISCARD_CTOR
node_view() noexcept = default;
TOML_NODISCARD_CTOR
node_view(const node_view&) noexcept = default;
TOML_NODISCARD_CTOR
node_view& operator= (const node_view&) noexcept = default;
node_view(node_view&&) noexcept = default;
node_view& operator= (node_view&&) noexcept = delete;
[[nodiscard]] explicit operator bool() const noexcept { return node_ != nullptr; }
[[nodiscard]] viewed_type* node() const noexcept { return node_; }
@ -7654,6 +7713,8 @@ TOML_NAMESPACE_START
{
for (size_t i = 0; i < count; i++)
{
if (!pairs[i].value) // empty node_views
continue;
map.insert_or_assign(
std::move(pairs[i].key),
std::move(pairs[i].value)

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -77,6 +77,7 @@
</ClCompile>
<ClCompile Include="..\..\tests\unicode.cpp" />
<ClCompile Include="..\..\tests\unicode_generated.cpp" />
<ClCompile Include="..\..\tests\user_feedback.cpp" />
<ClCompile Include="..\..\tests\windows_compat.cpp" />
</ItemGroup>
<ItemGroup>