This is an automated email from the ASF dual-hosted git repository. bneradt pushed a commit to branch doc-lexicon in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git
commit ce6fe32d7b2011be69fc07553e3f10c086a05417 Author: Alan M. Carroll <a...@apache.org> AuthorDate: Mon May 25 09:47:56 2020 -0500 Lexicon: Documentation and examples. Doxygen: Fixed problem with inline namespace. --- doc/Doxyfile | 2 +- doc/code/Lexicon.en.rst | 54 ++++++++++++++++++++++++++++++++++++ doc/code/TextView.en.rst | 9 ++++-- doc/namespace-filter.sh | 3 ++ tools/update-version.sh | 2 +- unit_tests/ex_Lexicon.cc | 68 ++++++++++++++++++++++++++++++++++++++++------ unit_tests/test_Lexicon.cc | 2 +- 7 files changed, 126 insertions(+), 14 deletions(-) diff --git a/doc/Doxyfile b/doc/Doxyfile index 0ad9afb..6e3048d 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -912,7 +912,7 @@ IMAGE_PATH = # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. -INPUT_FILTER = +INPUT_FILTER = ./namespace-filter.sh # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the diff --git a/doc/code/Lexicon.en.rst b/doc/code/Lexicon.en.rst index b960d04..64468b5 100644 --- a/doc/code/Lexicon.en.rst +++ b/doc/code/Lexicon.en.rst @@ -106,6 +106,12 @@ The file loading and parsing is then: :start-after: doc.load.begin :end-before: doc.load.end +with the simulated file contents + +.. literalinclude:: ../../unit_tests/ex_Lexicon.cc + :start-after: doc.file.begin + :end-before: doc.file.end + This uses the Lexicon to convert the strings in the file to the enumeration values, which are the bitset indices. The defalt is set to ``INVALID`` so that any string that doesn't match a string in the Lexicon is mapped to ``INVALID``. @@ -121,6 +127,54 @@ can be accessed like :: if (flags[NetType::PROD]) { ... } +Constructing +============ + +To make the class more flexible it can be constructed in a variety of ways. For static the entire +class can be initialized in the constructor. For dynamic use any subset can be initialized. In +the previous example, the instance was initialized with all of the defined values and a default +for missing names. Because this fully constructs it, it can be marked ``const`` to prevent +accidental changes. It could also have been constructed with a default name: + +.. literalinclude:: ../../unit_tests/ex_Lexicon.cc + :start-after: doc.ctor.1.begin + :end-before: doc.ctor.1.end + +Note the default name was put before the default value. Because they are distinct types, the +defaults can be added in either order, but must always follow the field defintions. The defaults can +also be omitted entirely, which is common if the Lexicon is used for output and not parsing, where +the enumeration is always valid because all enumeration values are in the Lexicon. + +.. literalinclude:: ../../unit_tests/ex_Lexicon.cc + :start-after: doc.ctor.2.begin + :end-before: doc.ctor.2.end + +For dynamic use, it is common to have just the defaults, and not any of the fields, although of course +if some "built in" names and values are needed those can be added as in the previous examples. + +.. literalinclude:: ../../unit_tests/ex_Lexicon.cc + :start-after: doc.ctor.3.begin + :end-before: doc.ctor.3.end + +As before both, either, or none of the defaults are required. + +Finally, here is a example of using Lexicon to translate a boolean value, allowing for various alternative +forms for the true and false names. + +.. literalinclude:: ../../unit_tests/ex_Lexicon.cc + :start-after: doc.ctor.4.begin + :end-before: doc.ctor.4.end + +The set of value names is easily changed. The ``BoolTag`` type is used to be able to indicate when a +name doesn't match anything in the Lexicon. Each field is a value and then a list of names, instead +of just the pair of a value and name as in the previous examples. If a ``BoolTag`` was passed in to +the Lexicon, it would return "true", "false", or throw an exception for ``BoolTag::INVALID`` because +that value is missing and there is no default name. The strings returned are returned because they +are the first elements in the list of names. This is fine for any debugging or diagnostic messages +because only the ``true`` and ``false`` values would be stored, ``INVALID`` indicates a parsing +error. The enumeration values were chosen so casting from ``bool`` to ``BoolTag`` yields the +appropriate string. + Design Notes ************ diff --git a/doc/code/TextView.en.rst b/doc/code/TextView.en.rst index 63cd632..4b9963e 100644 --- a/doc/code/TextView.en.rst +++ b/doc/code/TextView.en.rst @@ -240,9 +240,12 @@ similar suffixes with the same meaning as the affix methods. This can be done fo character, one of a set of characters, or a predicate. The most common use is with the predicate :code:`isspace` which removes leading and/or trailing whitespace as needed. -Numeric conversions are provided, in signed (:libswoc:`svtoi`) and unsigned (:libswoc:`svtou`) flavors. -These functions are designed to be "complete" in the sense that any other string to integer conversion -can be mapped to one of these functions. +Numeric conversions are provided, in signed (:libswoc:`svtoi`), unsigned (:libswoc:`svtou`), and +floating point (:libswoc:`svtod`) flavors. The integer functions are designed to be "complete" in +the sense that any other string to integer conversion can be mapped to one of these functions. The +floating point conversion is sufficiently accurate - it will return a floating point value that is +within one epsilon of the exact value, but not always the closest. This is fine for general use such +as in configurations, but possibly not quite enough for high precision work. The standard functions :code:`strcmp`, :code:`strcasecmp`, and :code:`memcmp` are overloaded when at least of the parameters is a |TV|. The length is taken from the view, rather than being an explicit diff --git a/doc/namespace-filter.sh b/doc/namespace-filter.sh new file mode 100755 index 0000000..1a74cdb --- /dev/null +++ b/doc/namespace-filter.sh @@ -0,0 +1,3 @@ +#! /bin/sh +echo "Filtering $1" >2 +cat $1 | sed --expr 's/inline namespace SWOC_VERSION_NS {//' | sed --expr 's/}} \/\/ namespace swoc/} \/\/ namespace swoc/' diff --git a/tools/update-version.sh b/tools/update-version.sh index 5fc3d0e..8402946 100644 --- a/tools/update-version.sh +++ b/tools/update-version.sh @@ -6,7 +6,7 @@ if [ -z "$3" ] ; then fi # Header -sed -i -E code/include/swoc/swoc_version.h --expr "s/SWOC_VERSION_NS _[0-9]+_[0-9]+_[0-9]+/SWOC_VERSION_NS _$1_$2_$3/"wqq +sed -i -E code/include/swoc/swoc_version.h --expr "s/SWOC_VERSION_NS _[0-9]+_[0-9]+_[0-9]+/SWOC_VERSION_NS _$1_$2_$3/" sed -i code/include/swoc/swoc_version.h --expr "s/\(MAJOR_VERSION *= *\).*\$/\\1$1;/" sed -i code/include/swoc/swoc_version.h --expr "s/\(MINOR_VERSION *= *\).*\$/\\1$2;/" sed -i code/include/swoc/swoc_version.h --expr "s/\(POINT_VERSION *= *\).*\$/\\1$3;/" diff --git a/unit_tests/ex_Lexicon.cc b/unit_tests/ex_Lexicon.cc index 7a53b12..34281a7 100644 --- a/unit_tests/ex_Lexicon.cc +++ b/unit_tests/ex_Lexicon.cc @@ -31,7 +31,7 @@ static constexpr size_t N_TYPES = size_t(NetType::INVALID); // Set up a Lexicon to convert between the enumeration and strings. // doc.2.begin -swoc::Lexicon<NetType> NetTypeNames { {{NetType::EXTERNAL, "external"}, +swoc::Lexicon<NetType> const NetTypeNames { {{NetType::EXTERNAL, "external"}, {NetType::PROD, "prod"}, {NetType::SECURE, "secure"}, {NetType::EDGE, "edge"}}, @@ -45,13 +45,15 @@ using Flags = std::bitset<N_TYPES>; TEST_CASE("Lexicon Example", "[libts][Lexicon]") { swoc::IPSpace<Flags> space; // Space in which to store the flags. // Load the file contents + // doc.file.begin swoc::TextView text { R"( 10.0.0.2-10.0.0.254,edge 10.12.0.0/25,prod - 10.15.0.10-10.15.0.99,prod,secure - 172.16.0.0/22,external,secure - 192.168.17.0/23,external,prod + 10.15.37.10-10.15.37.99,prod,secure + 172.19.0.0/22,external,secure + 192.168.18.0/23,external,prod )" }; + // doc.file.end // doc.load.begin // Process all the lines in the file. while (text) { @@ -64,7 +66,7 @@ TEST_CASE("Lexicon Example", "[libts][Lexicon]") { auto token = line.take_prefix_at(','); auto idx = NetTypeNames[token]; if (idx != NetType::INVALID) { // one of the valid strings - flags.set(idx); // set the bit + flags.set(static_cast<int>(idx)); // set the bit } } space.mark(r, flags); // store the flags in the spae. @@ -73,14 +75,64 @@ TEST_CASE("Lexicon Example", "[libts][Lexicon]") { // doc.load.end using AddrCase = std::tuple<swoc::IPAddr, Flags>; - std::array<AddrCase, 5> AddrList = { {"10.0.0.6", 0x8} , {"172.19.20.31", 0x5}, {"192.168.18.19", 3} - , {"10.15.0.57", 0x6}, {"10.12.0.126", 0x2}}; + using swoc::IPAddr; + std::array<AddrCase, 5> AddrList = {{ + {IPAddr{"10.0.0.6"}, 0x8} + , {IPAddr{"172.19.3.31"}, 0x5} + , {IPAddr{"192.168.18.19"}, 0x3} + , {IPAddr{"10.15.37.57"}, 0x6} + , {IPAddr{"10.12.0.126"}, 0x2} + }}; for ( auto const& [ addr, bits ] : AddrList ) { // doc.lookup.begin - auto && [ range, flags ] = space.find(addr); + auto && [ range, flags ] = *space.find(addr); // doc.lookup.end REQUIRE(flags == bits); } // doc.lookup.end } +namespace { + +// doc.ctor.1.begin +swoc::Lexicon<NetType> const Example1 { + {{NetType::EXTERNAL, "external"}, + {NetType::PROD, "prod"}, + {NetType::SECURE, "secure"}, + {NetType::EDGE, "edge"}}, +"*invalid*", // default name for undefined values + NetType::INVALID // default value for undefined name +}; +// doc.ctor.1.end + +// doc.ctor.2.begin +swoc::Lexicon<NetType> const Example2 { + {{NetType::EXTERNAL, "external"}, + {NetType::PROD, "prod"}, + {NetType::SECURE, "secure"}, + {NetType::EDGE, "edge"}}, +}; +// doc.ctor.2.end + +// doc.ctor.3.begin +swoc::Lexicon<NetType> Example3 { + "*invalid*", // default name for undefined values + NetType::INVALID // default value for undefined name +}; +// doc.ctor.3.end + +// doc.ctor.4.begin +enum BoolTag { + INVALID = -1, + False = 0, + True = 1, +}; + +swoc::Lexicon<BoolTag> const BoolNames { + {{ BoolTag::True, { "true", "1", "on", "enable", "Y", "yes" }} + , { BoolTag::False, { "false", "0", "off", "disable", "N", "no" }}} + , BoolTag::INVALID }; +// doc.ctor.4.end + + +} // namespace diff --git a/unit_tests/test_Lexicon.cc b/unit_tests/test_Lexicon.cc index e2df5cc..a72bd72 100644 --- a/unit_tests/test_Lexicon.cc +++ b/unit_tests/test_Lexicon.cc @@ -26,7 +26,7 @@ namespace }; } -TEST_CASE("Lexicon Example", "[libts][Lexicon]") +TEST_CASE("Lexicon", "[libts][Lexicon]") { ExampleNames exnames{{Example::Value_0, {"zero", "0"}}, {Example::Value_1, {"one", "1"}},