diff --git a/README.md b/README.md index 077a2f0..56c3f3f 100644 --- a/README.md +++ b/README.md @@ -1455,9 +1455,77 @@ There are some unreleased features in toml-lang/toml:master. Currently, the following features are available after defining `TOML11_USE_UNRELEASED_TOML_FEATURES` macro flag. +To use those features, `#define` `TOML11_USE_UNRELEASED_TOML_FEATURES` before +including `toml.hpp` or pass `-DTOML11_USE_UNRELEASED_TOML_FEATURES` to your +compiler. + - Leading zeroes in exponent parts of floats are permitted. - e.g. `1.0e+01`, `5e+05` + - [toml-lang/toml/PR/656](https://github.com/toml-lang/toml/pull/656) - Allow raw tab characters in basic strings and multi-line basic strings. + - [toml-lang/toml/PR/627](https://github.com/toml-lang/toml/pull/627) +- Allow heterogeneous arrays + - [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676) + +### Note about heterogeneous arrays + +Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value` +does not. + +```cpp +// this won't be compiled +toml::value v{ + "foo", 3.14, 42, {1,2,3,4,5}, {{"key", "value"}} +} +``` + +There is a workaround for this issue. By explicitly converting values into +`toml::value`, you can initialize `toml::value` with a heterogeneous array. + +```cpp +// OK! +toml::value v{ + toml::value("foo"), toml::value(3.14), toml::value(42), + toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} +} +``` + +The reason why the first example is not allowed is the following. +Let's assume that you are initializing a `toml::value` with a table. + +```cpp + // # expecting TOML table. +toml::value v{ // [v] + {"answer", 42}, // answer = 42 + {"pi", 3.14}, // pi = 3.14 + {"foo", "bar"} // foo = "bar" +}; +``` + +This is indistinguishable from a (heterogeneous) TOML array definition. + +```toml +v = [ + ["answer", 42], + ["pi", 3.14], + ["foo", "bar"], +] +``` + +This means that the above C++ code makes constructor's overload resolution +ambiguous. So a constructor that allows both "table as an initializer-list" and +"heterogeneous array as an initializer-list" cannot be implemented. + +Thus, although it is painful, you need to explicitly cast values into +`toml::value` when you initialize heterogeneous array in C++ code. + +```cpp +// You need to do this when you want to initialize hetero array. +toml::value v{ + toml::value("foo"), toml::value(3.14), toml::value(42), + toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} +} +``` ## Breaking Changes from v2 diff --git a/tests/test_error_detection.cpp b/tests/test_error_detection.cpp index c936209..49dff6e 100644 --- a/tests/test_error_detection.cpp +++ b/tests/test_error_detection.cpp @@ -80,10 +80,14 @@ BOOST_AUTO_TEST_CASE(test_detect_conflicting_value) BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array) { +#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES + BOOST_TEST_MESSAGE("heterogeneous array will be allowed in the next release"); +#else std::istringstream stream(std::string( "a = [1, 1.0]\n" )); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); +#endif } BOOST_AUTO_TEST_CASE(test_detect_appending_array_of_table) diff --git a/tests/test_parse_array.cpp b/tests/test_parse_array.cpp index 9cfbc4d..2dafdb9 100644 --- a/tests/test_parse_array.cpp +++ b/tests/test_parse_array.cpp @@ -128,3 +128,47 @@ BOOST_AUTO_TEST_CASE(test_multiline_array_value) TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", toml::value(a)); } } + +BOOST_AUTO_TEST_CASE(test_heterogeneous_array) +{ +#ifndef TOML11_USE_UNRELEASED_TOML_FEATURES + BOOST_TEST_MESSAGE("In strict TOML v0.5.0, heterogeneous arrays are not allowed."); +#else + { + array a(5); + a[0] = toml::value("foo"); + a[1] = toml::value(3.14); + a[2] = toml::value(42); + a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; + a[4] = toml::value{{"key", "value"}}; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\", 3.14, 42, [\"array\", \"of\", \"hetero-array\", 1], {key = \"value\"}]", toml::value(a)); + } + { + array a(5); + a[0] = toml::value("foo"); + a[1] = toml::value(3.14); + a[2] = toml::value(42); + a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; + a[4] = toml::value{{"key", "value"}}; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",\n 3.14,\n 42,\n [\"array\", \"of\", \"hetero-array\", 1],\n {key = \"value\"},\n]", toml::value(a)); + } + { + array a(5); + a[0] = toml::value("foo"); + a[1] = toml::value(3.14); + a[2] = toml::value(42); + a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; + a[4] = toml::value{{"key", "value"}}; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",#comment\n 3.14,#comment\n 42,#comment\n [\"array\", \"of\", \"hetero-array\", 1],#comment\n {key = \"value\"},#comment\n]#comment", toml::value(a)); + } + { + array a(5); + a[0] = toml::value("foo"); + a[1] = toml::value(3.14); + a[2] = toml::value(42); + a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; + a[4] = toml::value{{"key", "value"}}; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",\n 3.14,\n 42,\n [\"array\",\n \"of\",\n \"hetero-array\",\n 1],\n {key = \"value\"},\n]", toml::value(a)); + } +#endif +} diff --git a/toml/parser.hpp b/toml/parser.hpp index 9fd7097..0efccf0 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -938,6 +938,7 @@ parse_array(location& loc) if(auto val = parse_value(loc)) { +#ifndef TOML11_USE_UNRELEASED_TOML_FEATURES if(!retval.empty() && retval.front().type() != val.as_ok().type()) { auto array_start_loc = loc; @@ -956,6 +957,7 @@ parse_array(location& loc) } }), source_location(std::addressof(loc))); } +#endif retval.push_back(std::move(val.unwrap())); } else